557 lines
16 KiB
C++
557 lines
16 KiB
C++
/* Copyright (c) <2003-2011> <Julio Jerez, Newton Game Dynamics>
|
|
*
|
|
* This software is provided 'as-is', without any express or implied
|
|
* warranty. In no event will the authors be held liable for any damages
|
|
* arising from the use of this software.
|
|
*
|
|
* Permission is granted to anyone to use this software for any purpose,
|
|
* including commercial applications, and to alter it and redistribute it
|
|
* freely, subject to the following restrictions:
|
|
*
|
|
* 1. The origin of this software must not be misrepresented; you must not
|
|
* claim that you wrote the original software. If you use this software
|
|
* in a product, an acknowledgment in the product documentation would be
|
|
* appreciated but is not required.
|
|
*
|
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
|
* misrepresented as being the original software.
|
|
*
|
|
* 3. This notice may not be removed or altered from any source distribution.
|
|
*/
|
|
|
|
#include "dgMeshEffectSolidTree.h"
|
|
#include "dgMeshEffect.h"
|
|
#include "hpl1/engine/libraries/newton/core/dg.h"
|
|
|
|
|
|
#define DG_CLIPPER_TOL dgFloat64(1.0e-12)
|
|
|
|
dgMeshTreeCSGFace::dgMeshTreeCSGFace(const dgMeshEffect &mesh,
|
|
dgEdge *const face) : dgList<dgHugeVector>(mesh.GetAllocator()), m_side(dgMeshEffectSolidTree::m_divider) {
|
|
const dgEdge *ptr = face;
|
|
const dgMeshEffect::dgVertexAtribute *const attib = mesh.m_attib;
|
|
do {
|
|
Append(attib[ptr->m_userData].m_vertex);
|
|
ptr = ptr->m_next;
|
|
} while (ptr != face);
|
|
}
|
|
|
|
dgMeshTreeCSGFace::dgMeshTreeCSGFace(dgMemoryAllocator *const allocator,
|
|
dgInt32 count, const dgHugeVector *const points) : dgList<dgHugeVector>(allocator), m_side(dgMeshEffectSolidTree::m_divider) {
|
|
for (dgInt32 i = 0; i < count; i++) {
|
|
Append(points[i]);
|
|
}
|
|
}
|
|
|
|
bool dgMeshTreeCSGFace::IsPointOnEdge(const dgHugeVector &p0,
|
|
const dgHugeVector &p1, const dgHugeVector &q0) const {
|
|
dgHugeVector p1p0(p1 - p0);
|
|
dgGoogol den(p1p0 % p1p0);
|
|
|
|
dgHugeVector q0p0(q0 - p0);
|
|
dgGoogol num(q0p0 % p1p0);
|
|
|
|
dgFloat64 numf = num.GetAproximateValue();
|
|
if (numf > (DG_CLIPPER_TOL * dgFloat64(1.0e3))) {
|
|
if (numf < den.GetAproximateValue() * dgFloat64(1.0 - (DG_CLIPPER_TOL * dgFloat64(1.0e3)))) {
|
|
dgGoogol t = num / den;
|
|
NEWTON_ASSERT(t.GetAproximateValue() > dgFloat64(0.0f));
|
|
NEWTON_ASSERT(t.GetAproximateValue() < dgFloat64(1.0f));
|
|
dgHugeVector q1(p0 + p1p0.Scale(t));
|
|
dgHugeVector dist(q1 - q0);
|
|
dgGoogol dist2 = dist % dist;
|
|
if (dist2.GetAproximateValue() < DG_CLIPPER_TOL) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool dgMeshTreeCSGFace::CheckConvex(const dgHugeVector &normal) const {
|
|
dgHugeVector p1(GetLast()->GetInfo());
|
|
dgHugeVector p0(GetLast()->GetPrev()->GetInfo());
|
|
dgHugeVector e0(p0 - p1);
|
|
for (dgListNode *node = GetFirst(); node; node = node->GetNext()) {
|
|
dgHugeVector p2(node->GetInfo());
|
|
dgHugeVector e1(p2 - p1);
|
|
|
|
dgHugeVector n(e1 * e0);
|
|
dgGoogol convex = n % normal;
|
|
if (convex.GetAproximateValue() < dgFloat64(-1.0e10f)) {
|
|
return false;
|
|
}
|
|
p1 = p2;
|
|
e0 = e1.Scale(dgGoogol(-1.0f));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void dgMeshTreeCSGFace::MergeMissingVertex(const dgMeshTreeCSGFace *const face) {
|
|
for (dgMeshTreeCSGFace::dgListNode *outNode = GetFirst();
|
|
outNode != GetLast(); outNode = outNode->GetNext()) {
|
|
dgHugeVector p0(outNode->GetInfo());
|
|
for (dgMeshTreeCSGFace::dgListNode *node = face->GetFirst(); node; node =
|
|
node->GetNext()) {
|
|
if (IsPointOnEdge(p0, outNode->GetNext()->GetInfo(), node->GetInfo())) {
|
|
dgMeshTreeCSGFace::dgListNode *const insertNode = Append(
|
|
node->GetInfo());
|
|
InsertAfter(outNode, insertNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
RotateToBegin(GetLast());
|
|
dgListNode *const last = GetFirst()->GetNext();
|
|
for (dgMeshTreeCSGFace::dgListNode *outNode = GetFirst(); outNode != last;
|
|
outNode = outNode->GetNext()) {
|
|
dgHugeVector p0(outNode->GetInfo());
|
|
dgHugeVector p1(outNode->GetNext()->GetInfo());
|
|
|
|
for (dgMeshTreeCSGFace::dgListNode *node = face->GetFirst(); node; node =
|
|
node->GetNext()) {
|
|
if (IsPointOnEdge(p0, outNode->GetNext()->GetInfo(), node->GetInfo())) {
|
|
dgMeshTreeCSGFace::dgListNode *const insertNode = Append(
|
|
node->GetInfo());
|
|
InsertAfter(outNode, insertNode);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
dgInt32 dgMeshTreeCSGFace::RemoveDuplicates(dgInt32 count,
|
|
dgHugeVector *const points) const {
|
|
dgInt32 index[256];
|
|
for (dgInt32 i = 0; i < count; i++) {
|
|
index[i] = i + 1;
|
|
}
|
|
index[count - 1] = 0;
|
|
|
|
dgInt32 originalCount = count;
|
|
dgInt32 start = index[0];
|
|
for (dgInt32 count1 = 0; (count1 != count) && (count >= 3);) {
|
|
count1 = 0;
|
|
for (dgInt32 i = 0; i < count; i++) {
|
|
dgInt32 next = index[start];
|
|
dgHugeVector err(points[start] - points[next]);
|
|
dgGoogol dist2 = err % err;
|
|
dgFloat64 val = dist2.GetAproximateValue();
|
|
if (val < dgFloat64(1.0e-12f)) {
|
|
index[start] = index[index[start]];
|
|
count1 = 0;
|
|
count--;
|
|
break;
|
|
}
|
|
|
|
count1++;
|
|
start = index[start];
|
|
}
|
|
}
|
|
|
|
if ((count != originalCount) && (count >= 3)) {
|
|
dgHugeVector tmp[256];
|
|
for (dgInt32 i = 0; i < count; i++) {
|
|
tmp[i] = points[start];
|
|
start = index[start];
|
|
}
|
|
|
|
for (dgInt32 i = 0; i < count; i++) {
|
|
points[i] = tmp[i];
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
dgMatrix dgMeshTreeCSGFace::DebugMatrix() const {
|
|
dgMatrix matrix(dgGetIdentityMatrix());
|
|
dgHugeVector e0(GetFirst()->GetNext()->GetInfo() - GetFirst()->GetInfo());
|
|
dgHugeVector e1(
|
|
GetFirst()->GetNext()->GetNext()->GetInfo() - GetFirst()->GetInfo());
|
|
dgHugeVector aaa(e0 * e1);
|
|
dgVector aaaa(dgFloat32(aaa.m_x.GetAproximateValue()),
|
|
dgFloat32(aaa.m_y.GetAproximateValue()),
|
|
dgFloat32(aaa.m_z.GetAproximateValue()), 0.0f);
|
|
aaaa = aaaa.Scale(dgFloat32(1.0) / dgSqrt(aaaa % aaaa));
|
|
|
|
dgVector bbbb(dgFloat32(e0.m_x.GetAproximateValue()),
|
|
dgFloat32(e0.m_y.GetAproximateValue()),
|
|
dgFloat32(e0.m_z.GetAproximateValue()), 0.0f);
|
|
bbbb = bbbb.Scale(dgFloat32(1.0) / dgSqrt(bbbb % bbbb));
|
|
|
|
matrix.m_up = bbbb;
|
|
matrix.m_right = aaaa;
|
|
matrix.m_front = bbbb * aaaa;
|
|
|
|
return matrix;
|
|
}
|
|
|
|
void dgMeshTreeCSGFace::Trace(const dgMatrix &matrix) const {
|
|
for (dgMeshTreeCSGFace::dgListNode *node = GetFirst(); node;
|
|
node = node->GetNext()) {
|
|
const dgHugeVector &bbb = node->GetInfo();
|
|
dgVector bbbb(dgFloat32(bbb.m_x.GetAproximateValue()),
|
|
dgFloat32(bbb.m_y.GetAproximateValue()),
|
|
dgFloat32(bbb.m_z.GetAproximateValue()), 0.0f);
|
|
bbbb = matrix.UnrotateVector(bbbb);
|
|
bbbb.Trace();
|
|
}
|
|
dgTrace(("\n"));
|
|
}
|
|
|
|
#endif
|
|
|
|
dgHugeVector dgMeshTreeCSGFace::FaceNormal() const {
|
|
dgHugeVector area(0.0f, 0.0f, 0.0f, 0.0f);
|
|
dgHugeVector e0(GetFirst()->GetNext()->GetInfo() - GetFirst()->GetInfo());
|
|
for (dgListNode *node = GetFirst()->GetNext()->GetNext(); node;
|
|
node = node->GetNext()) {
|
|
dgHugeVector e1(node->GetInfo() - GetFirst()->GetInfo());
|
|
area += e0 * e1;
|
|
e0 = e1;
|
|
}
|
|
return area;
|
|
}
|
|
|
|
bool dgMeshTreeCSGFace::CheckFaceArea(dgInt32 count,
|
|
const dgHugeVector *const points) const {
|
|
dgHugeVector area(dgFloat64(0.0), dgFloat64(0.0), dgFloat64(0.0),
|
|
dgFloat64(0.0));
|
|
dgHugeVector e0(points[1] - points[0]);
|
|
for (dgInt32 i = 2; i < count; i++) {
|
|
dgHugeVector e1(points[i] - points[0]);
|
|
area += e0 * e1;
|
|
e0 = e1;
|
|
}
|
|
|
|
dgFloat64 val = (area % area).GetAproximateValue();
|
|
return (val > dgFloat64(1.0e-12f)) ? true : false;
|
|
}
|
|
|
|
/*
|
|
//dgGoogol xxx1(dgGoogol v)
|
|
dgGoogol xxx1(dgInt32 x)
|
|
{
|
|
dgGoogol old (0);
|
|
dgGoogol ans (1);
|
|
while (x > 1) {
|
|
dgGoogol n (ans + old);
|
|
old = ans;
|
|
ans = n;
|
|
x = x - 1;
|
|
}
|
|
return ans;
|
|
}
|
|
|
|
dgGoogol xxx2(dgInt32 x)
|
|
{
|
|
dgGoogol one (1);
|
|
dgGoogol n (x);
|
|
dgGoogol a (1);
|
|
for (int i = x; i >= 1; i --) {
|
|
a = a * n;
|
|
n -= one;
|
|
}
|
|
return a;
|
|
}
|
|
|
|
void xxxx()
|
|
{
|
|
char text[256];
|
|
// dgGoogol a (xxx1(100));
|
|
// dgGoogol a (1.05);
|
|
dgGoogol a (xxx2 (60));
|
|
a.ToString(text);
|
|
// dgFloat64 xxxxx = a.GetAproximateValue();
|
|
}
|
|
*/
|
|
|
|
void dgMeshTreeCSGFace::Clip(const dgHugeVector &plane,
|
|
dgMeshTreeCSGFace **leftOut, dgMeshTreeCSGFace **rightOut) {
|
|
// xxxx ();
|
|
|
|
dgInt8 pointSide[256];
|
|
|
|
dgInt32 count = 0;
|
|
dgInt32 rightPCount = 0;
|
|
dgInt32 leftPCount = 0;
|
|
for (dgMeshTreeCSGFace::dgListNode *ptr = GetFirst(); ptr; ptr =
|
|
ptr->GetNext()) {
|
|
const dgHugeVector &p = ptr->GetInfo();
|
|
|
|
dgGoogol test = plane.EvaluePlane(p);
|
|
dgFloat64 val = test.GetAproximateValue();
|
|
if (fabs(val) < DG_CLIPPER_TOL) {
|
|
val = dgFloat64(0.0f);
|
|
}
|
|
|
|
if (val > dgFloat64(0.0f)) {
|
|
pointSide[count] = 1;
|
|
rightPCount++;
|
|
} else if (val < dgFloat64(0.0f)) {
|
|
pointSide[count] = -1;
|
|
leftPCount++;
|
|
} else {
|
|
pointSide[count] = 0;
|
|
}
|
|
count++;
|
|
}
|
|
|
|
*leftOut = NULL;
|
|
*rightOut = NULL;
|
|
if ((leftPCount && !rightPCount) || (!leftPCount && rightPCount)) {
|
|
if (leftPCount) {
|
|
NEWTON_ASSERT(!rightPCount);
|
|
AddRef();
|
|
*leftOut = this;
|
|
} else {
|
|
NEWTON_ASSERT(!leftPCount);
|
|
*rightOut = this;
|
|
AddRef();
|
|
}
|
|
} else if (!(leftPCount || rightPCount)) {
|
|
NEWTON_ASSERT(!leftPCount);
|
|
NEWTON_ASSERT(!rightPCount);
|
|
AddRef();
|
|
// AddRef();
|
|
} else {
|
|
dgInt32 leftCount = 0;
|
|
dgInt32 rightCount = 0;
|
|
dgHugeVector leftFace[256];
|
|
dgHugeVector rightFace[256];
|
|
|
|
dgInt32 i1 = 0;
|
|
dgInt32 i0 = count - 1;
|
|
dgHugeVector p0(GetLast()->GetInfo());
|
|
for (dgMeshTreeCSGFace::dgListNode *ptr = GetFirst(); ptr;
|
|
ptr = ptr->GetNext()) {
|
|
const dgHugeVector &p1(ptr->GetInfo());
|
|
|
|
dgHugeVector inter;
|
|
if (((pointSide[i0] == -1) && (pointSide[i1] == 1)) || ((pointSide[i0] == 1) && (pointSide[i1] == -1))) {
|
|
// inter = Interpolate (plane, p0, p1);
|
|
dgHugeVector dp(p1 - p0);
|
|
dgGoogol den(plane % dp);
|
|
dgGoogol num = plane.EvaluePlane(p0);
|
|
NEWTON_ASSERT(fabs(num.GetAproximateValue()) > dgFloat64(0.0f));
|
|
dgGoogol ti(num / den);
|
|
inter = p0 - dp.Scale(num / den);
|
|
}
|
|
|
|
if (pointSide[i1] == -1) {
|
|
if (pointSide[i0] == 1) {
|
|
rightFace[rightCount] = inter;
|
|
rightCount++;
|
|
}
|
|
} else {
|
|
if ((pointSide[i1] == 1) && (pointSide[i0] == -1)) {
|
|
rightFace[rightCount] = inter;
|
|
rightCount++;
|
|
}
|
|
rightFace[rightCount] = p1;
|
|
rightCount++;
|
|
}
|
|
|
|
if (pointSide[i1] == 1) {
|
|
if (pointSide[i0] == -1) {
|
|
leftFace[leftCount] = inter;
|
|
leftCount++;
|
|
}
|
|
} else {
|
|
if ((pointSide[i1] == -1) && (pointSide[i0] == 1)) {
|
|
leftFace[leftCount] = inter;
|
|
leftCount++;
|
|
}
|
|
leftFace[leftCount] = p1;
|
|
leftCount++;
|
|
}
|
|
NEWTON_ASSERT(leftCount < dgInt32((sizeof(leftFace) / sizeof(leftFace[0])) - 1));
|
|
NEWTON_ASSERT(rightCount < dgInt32((sizeof(rightFace) / sizeof(rightFace[0])) - 1));
|
|
|
|
i0 = i1;
|
|
i1++;
|
|
p0 = p1;
|
|
}
|
|
|
|
leftCount = RemoveDuplicates(leftCount, leftFace);
|
|
rightCount = RemoveDuplicates(rightCount, rightFace);
|
|
if ((leftCount >= 3) && CheckFaceArea(leftCount, leftFace)) {
|
|
*leftOut = new (GetAllocator()) dgMeshTreeCSGFace(GetAllocator(),
|
|
leftCount, leftFace);
|
|
}
|
|
|
|
if ((rightCount >= 3) && CheckFaceArea(rightCount, rightFace)) {
|
|
*rightOut = new (GetAllocator()) dgMeshTreeCSGFace(GetAllocator(),
|
|
rightCount, rightFace);
|
|
}
|
|
NEWTON_ASSERT(*leftOut || *rightOut);
|
|
}
|
|
}
|
|
|
|
dgMeshEffectSolidTree::dgMeshEffectSolidTree(dgPlaneType type) : m_planeType(type), m_back(NULL), m_front(NULL), m_plane(0.0, 0.0, 0.0, 0.0) {
|
|
}
|
|
|
|
dgMeshEffectSolidTree::dgMeshEffectSolidTree(const dgMeshEffect &mesh,
|
|
dgEdge *const face) : m_planeType(m_divider), m_back(new (mesh.GetAllocator()) dgMeshEffectSolidTree(m_solid)), m_front(new (mesh.GetAllocator()) dgMeshEffectSolidTree(m_empty)), m_plane(BuildPlane(mesh, face)) {
|
|
}
|
|
|
|
dgMeshEffectSolidTree::dgMeshEffectSolidTree(const dgHugeVector &plane,
|
|
dgMemoryAllocator *const allocator) : m_planeType(m_divider), m_back(new (allocator) dgMeshEffectSolidTree(m_solid)), m_front(new (allocator) dgMeshEffectSolidTree(m_empty)), m_plane(plane) {
|
|
}
|
|
|
|
dgMeshEffectSolidTree::~dgMeshEffectSolidTree() {
|
|
if (m_front) {
|
|
delete m_front;
|
|
}
|
|
|
|
if (m_back) {
|
|
delete m_back;
|
|
}
|
|
}
|
|
|
|
dgHugeVector dgMeshEffectSolidTree::BuildPlane(const dgMeshEffect &mesh,
|
|
dgEdge *const face) const {
|
|
dgEdge *edge = face;
|
|
dgHugeVector plane(dgFloat32(0.0f), dgFloat32(0.0f), dgFloat32(0.0f),
|
|
dgFloat32(0.0f));
|
|
|
|
dgHugeVector p0(mesh.m_points[edge->m_incidentVertex]);
|
|
|
|
edge = edge->m_next;
|
|
dgHugeVector p1(mesh.m_points[edge->m_incidentVertex]);
|
|
|
|
dgHugeVector e1(p1 - p0);
|
|
for (edge = edge->m_next; edge != face; edge = edge->m_next) {
|
|
dgHugeVector p2(mesh.m_points[edge->m_incidentVertex]);
|
|
dgHugeVector e2(p2 - p0);
|
|
plane += e1 * e2;
|
|
e1 = e2;
|
|
}
|
|
|
|
plane.m_w = (p0 % plane) * dgGoogol(-1.0);
|
|
return plane;
|
|
}
|
|
|
|
void dgMeshEffectSolidTree::AddFace(const dgMeshEffect &mesh,
|
|
dgEdge *const face) {
|
|
dgBigVector normal(
|
|
mesh.FaceNormal(face, &mesh.m_points[0][0], sizeof(dgBigVector)));
|
|
dgFloat64 mag2 = normal % normal;
|
|
|
|
if (mag2 > dgFloat32(1.0e-14f)) {
|
|
dgMeshTreeCSGFace *faces[DG_MESH_EFFECT_BOLLEAN_STACK];
|
|
dgMeshEffectSolidTree *pool[DG_MESH_EFFECT_BOLLEAN_STACK];
|
|
|
|
dgHugeVector plane(BuildPlane(mesh, face));
|
|
|
|
dgInt32 stack = 1;
|
|
pool[0] = this;
|
|
faces[0] = new (mesh.GetAllocator()) dgMeshTreeCSGFace(mesh, face);
|
|
|
|
while (stack) {
|
|
|
|
stack--;
|
|
dgMeshEffectSolidTree *const root = pool[stack];
|
|
NEWTON_ASSERT(root->m_planeType == m_divider);
|
|
|
|
dgMeshTreeCSGFace *const curve = faces[stack];
|
|
NEWTON_ASSERT(curve->CheckConvex(plane));
|
|
|
|
dgMeshTreeCSGFace *backOut;
|
|
dgMeshTreeCSGFace *frontOut;
|
|
curve->Clip(root->m_plane, &backOut, &frontOut);
|
|
|
|
if ((backOut == NULL) && (frontOut == NULL)) {
|
|
curve->Release();
|
|
} else {
|
|
if (backOut && frontOut) {
|
|
dgHugeVector backArea(backOut->FaceNormal());
|
|
dgHugeVector frontArea(frontOut->FaceNormal());
|
|
|
|
dgFloat64 backMag = (backArea % backArea).GetAproximateValue();
|
|
dgFloat64 frontMag = (frontArea % frontArea).GetAproximateValue();
|
|
if (backMag > frontMag) {
|
|
if (backMag > (frontMag * dgFloat64(1.0e6))) {
|
|
frontOut->Release();
|
|
frontOut = NULL;
|
|
}
|
|
} else {
|
|
if (frontMag > (backMag * dgFloat64(1.0e6))) {
|
|
backOut->Release();
|
|
backOut = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (backOut) {
|
|
if (root->m_back->m_planeType != m_divider) {
|
|
backOut->Release();
|
|
delete root->m_back;
|
|
root->m_back = new (mesh.GetAllocator()) dgMeshEffectSolidTree(
|
|
plane, mesh.GetAllocator());
|
|
} else {
|
|
faces[stack] = backOut;
|
|
pool[stack] = root->m_back;
|
|
stack++;
|
|
NEWTON_ASSERT(stack < dgInt32(sizeof(pool) / sizeof(pool[0])));
|
|
}
|
|
}
|
|
|
|
if (frontOut) {
|
|
if (root->m_front->m_planeType != m_divider) {
|
|
frontOut->Release();
|
|
delete root->m_front;
|
|
root->m_front = new (mesh.GetAllocator()) dgMeshEffectSolidTree(
|
|
plane, mesh.GetAllocator());
|
|
} else {
|
|
faces[stack] = frontOut;
|
|
pool[stack] = root->m_front;
|
|
stack++;
|
|
NEWTON_ASSERT(stack < dgInt32(sizeof(pool) / sizeof(pool[0])));
|
|
}
|
|
}
|
|
}
|
|
|
|
curve->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
dgMeshEffectSolidTree::dgPlaneType dgMeshEffectSolidTree::GetPointSide(
|
|
const dgHugeVector &point) const {
|
|
const dgMeshEffectSolidTree *root = this;
|
|
|
|
NEWTON_ASSERT(root);
|
|
while (root->m_planeType == dgMeshEffectSolidTree::m_divider) {
|
|
dgGoogol test = root->m_plane.EvaluePlane(point);
|
|
dgFloat64 dist = test.GetAproximateValue();
|
|
|
|
if (fabs(dist) < dgFloat64(1.0e-16f)) {
|
|
dgPlaneType isBackSide = root->m_back->GetPointSide(point);
|
|
dgPlaneType isFrontSide = root->m_front->GetPointSide(point);
|
|
return (isBackSide == isFrontSide) ? isFrontSide : m_divider;
|
|
|
|
} else if (dist > dgFloat64(0.0f)) {
|
|
root = root->m_front;
|
|
} else {
|
|
NEWTON_ASSERT(dist < dgFloat64(0.0f));
|
|
root = root->m_back;
|
|
}
|
|
}
|
|
return root->m_planeType;
|
|
}
|
|
|
|
dgMeshEffectSolidTree::dgPlaneType dgMeshEffectSolidTree::GetFaceSide(
|
|
const dgMeshTreeCSGFace *const face) const {
|
|
dgHugeVector center(0.0, 0.0, 0.0, 0.0);
|
|
for (dgMeshTreeCSGFace::dgListNode *node = face->GetFirst(); node;
|
|
node = node->GetNext()) {
|
|
const dgHugeVector &point = node->GetInfo();
|
|
center += point;
|
|
}
|
|
center = center.Scale(dgGoogol(1.0) / dgGoogol(face->GetCount()));
|
|
dgPlaneType faceSide(GetPointSide(center));
|
|
return faceSide;
|
|
}
|