mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-04 20:57:49 +00:00
1519 lines
43 KiB
C++
1519 lines
43 KiB
C++
/* Copyright 2014 ROBLOX Corporation, All Rights Reserved */
|
|
|
|
#include "CSGKernel.h"
|
|
#include "V8DataModel/CSGMesh.h"
|
|
#include <algorithm>
|
|
#include <boost/tokenizer.hpp>
|
|
#include <boost/algorithm/string.hpp>
|
|
#include <boost/filesystem/operations.hpp>
|
|
#include <boost/thread/once.hpp>
|
|
#include <boost/uuid/uuid.hpp>
|
|
#include <boost/uuid/random_generator.hpp>
|
|
#include <boost/uuid/uuid_io.hpp>
|
|
#include <fstream>
|
|
#include <streambuf>
|
|
#include <sstream>
|
|
|
|
#include "g3d/g3dmath.h"
|
|
#include "g3d/Ray.h"
|
|
#include "g3d/CollisionDetection.h"
|
|
#include "g3d/vectorMath.h"
|
|
|
|
#include <math.h>
|
|
#include <map>
|
|
|
|
#include <sgCore.h>
|
|
#include "util/FileSystem.h"
|
|
#include "FastLog.h"
|
|
|
|
#if defined(_WIN32) && !defined(RBX_PLATFORM_DURANGO)
|
|
#include "../Win/LogManager.h"
|
|
#endif
|
|
|
|
FASTFLAGVARIABLE(CSGExportFailure, false);
|
|
|
|
static std::string lastFileError = "";
|
|
|
|
using namespace G3D;
|
|
|
|
namespace RBX {
|
|
|
|
CSGMesh* CSGMeshFactorySgCore::createMesh()
|
|
{
|
|
return new CSGMeshSgCore;
|
|
}
|
|
|
|
void CSGMeshSgCore::weldMesh(bool positionOnly)
|
|
{
|
|
std::vector<CSGVertex> rawTris;
|
|
rawTris.resize(indices.size());
|
|
|
|
for (unsigned int i = 0; i < indices.size(); i++)
|
|
{
|
|
rawTris[i] = vertices[indices[i]];
|
|
}
|
|
|
|
std::vector<unsigned int> emptyIndices;
|
|
indices.swap(emptyIndices);
|
|
indices.reserve(rawTris.size());
|
|
std::vector<CSGVertex> emptyVec;
|
|
vertices.swap(emptyVec);
|
|
|
|
for (unsigned int i = 0; i < rawTris.size(); i++)
|
|
{
|
|
bool found = false;
|
|
unsigned int foundIndex = 0;
|
|
for (unsigned int u = 0; u < vertices.size(); u++)
|
|
{
|
|
if ((rawTris[i].pos - vertices[u].pos).length() < 0.001f)
|
|
{
|
|
if (positionOnly)
|
|
{
|
|
found = true;
|
|
foundIndex = u;
|
|
}
|
|
else if ((rawTris[i].normal - vertices[u].normal).length() < 0.001f &&
|
|
rawTris[i].color == vertices[u].color &&
|
|
rawTris[i].uv == vertices[u].uv)
|
|
{
|
|
found = true;
|
|
foundIndex = u;
|
|
}
|
|
}
|
|
}
|
|
if (found)
|
|
{
|
|
indices.push_back(foundIndex);
|
|
continue;
|
|
}
|
|
unsigned indexOffset = unsigned(vertices.size());
|
|
vertices.push_back(rawTris[i]);
|
|
indices.push_back(indexOffset);
|
|
}
|
|
}
|
|
|
|
CSGMeshSgCore::EditData::EditData(CSGMeshSgCore* meshIn)
|
|
: shape(0)
|
|
, mesh(meshIn)
|
|
{}
|
|
|
|
CSGMeshSgCore::EditData::~EditData()
|
|
{
|
|
destroy();
|
|
}
|
|
|
|
CSGMeshSgCore::EditData::EditData(const CSGMeshSgCore::EditData& editData)
|
|
: shape(0)
|
|
, mesh(editData.mesh)
|
|
{
|
|
shape = editData.clone();
|
|
}
|
|
|
|
CSGMeshSgCore::EditData& CSGMeshSgCore::EditData::operator=(const CSGMeshSgCore::EditData& editData)
|
|
{
|
|
setShape(editData.clone());
|
|
return *this;
|
|
}
|
|
|
|
sgCObject* CSGMeshSgCore::EditData::clone() const
|
|
{
|
|
// The sgCObject* clone function does not do a true clone.
|
|
// Use the object to bit array code path instead to get a true hierarchy clone.
|
|
return mesh->brepFromBinaryString(mesh->getBRepBinaryString());
|
|
}
|
|
|
|
void CSGMeshSgCore::EditData::destroy()
|
|
{
|
|
if (shape)
|
|
sgDeleteObject(shape);
|
|
shape = 0;
|
|
}
|
|
|
|
void CSGMeshSgCore::EditData::setShape(sgCObject* shapeIn)
|
|
{
|
|
destroy();
|
|
shape = shapeIn;
|
|
}
|
|
|
|
void initKernelOnce()
|
|
{
|
|
sgInitKernel();
|
|
sgC3DObject::AutoTriangulate(false, ::SG_VERTEX_TRIANGULATION);
|
|
}
|
|
|
|
void initKernel()
|
|
{
|
|
static boost::once_flag flag = BOOST_ONCE_INIT;
|
|
boost::call_once(&initKernelOnce, flag);
|
|
}
|
|
|
|
CSGMeshSgCore::CSGMeshSgCore()
|
|
: editData(this)
|
|
{
|
|
initKernel();
|
|
}
|
|
|
|
CSGMeshSgCore::CSGMeshSgCore(const CSGMeshSgCore& mesh)
|
|
: editData(this)
|
|
{
|
|
initKernel();
|
|
|
|
vertices = mesh.vertices;
|
|
indices = mesh.indices;
|
|
version = mesh.version;
|
|
badMesh = mesh.badMesh;
|
|
editData = mesh.editData;
|
|
|
|
for (unsigned i = 0; i < 6; ++i)
|
|
{
|
|
decalIndexRemap[i] = mesh.decalIndexRemap[i];
|
|
decalVertexRemap[i] = mesh.decalVertexRemap[i];
|
|
}
|
|
}
|
|
|
|
CSGMeshSgCore::~CSGMeshSgCore()
|
|
{
|
|
}
|
|
|
|
CSGMeshSgCore& CSGMeshSgCore::operator=(const CSGMeshSgCore& mesh)
|
|
{
|
|
vertices = mesh.vertices;
|
|
indices = mesh.indices;
|
|
version = mesh.version;
|
|
badMesh = mesh.badMesh;
|
|
editData = mesh.editData;
|
|
return *this;
|
|
}
|
|
|
|
void removeAllDXFFiles()
|
|
{
|
|
boost::filesystem::path path = RBX::FileSystem::getUserDirectory(true, RBX::DirAppData, "logs");
|
|
boost::system::error_code ec;
|
|
|
|
if (path.empty())
|
|
return;
|
|
|
|
for (boost::filesystem::directory_iterator iter(path, ec), endIter; iter != endIter; ++iter)
|
|
{
|
|
if (0 == iter->path().extension().compare(boost::filesystem::path(".dxf"))) // ugh
|
|
boost::filesystem::remove(iter->path(), ec);
|
|
}
|
|
}
|
|
|
|
void removePreviousErrorFiles()
|
|
{
|
|
if (!lastFileError.empty())
|
|
{
|
|
std::remove((lastFileError + "A.dxf").c_str());
|
|
std::remove((lastFileError + "B.dxf").c_str());
|
|
}
|
|
else
|
|
{
|
|
removeAllDXFFiles();
|
|
}
|
|
|
|
lastFileError = "";
|
|
}
|
|
|
|
void logError(sgCObject* obj1, sgCObject* obj2, bool unionOperation = true)
|
|
{
|
|
#if defined(_WIN32) && !defined(RBX_PLATFORM_DURANGO)
|
|
removePreviousErrorFiles();
|
|
|
|
std::string path = MainLogManager::getMainLogManager()->MakeLogFileName(unionOperation ? "_csgU" : "_csgN");
|
|
path = path.substr(0, path.size() - 4);
|
|
lastFileError = path;
|
|
|
|
sgGetScene()->AttachObject(obj1);
|
|
sgFileManager::ExportDXF(sgGetScene(), (path + "A.dxf").c_str());
|
|
sgGetScene()->DetachObject(obj1);
|
|
|
|
sgGetScene()->AttachObject(obj2);
|
|
sgFileManager::ExportDXF(sgGetScene(), (path + "B.dxf").c_str());
|
|
sgGetScene()->DetachObject(obj2);
|
|
#endif
|
|
}
|
|
|
|
void gather3DObjects(sgCObject* obj, std::vector<sgC3DObject*>& objects);
|
|
|
|
void gatherGroup(sgCGroup* group, std::vector<sgC3DObject*>& objects)
|
|
{
|
|
sgCObject* curObj = group->GetChildrenList()->GetHead();
|
|
while (curObj)
|
|
{
|
|
gather3DObjects(curObj, objects);
|
|
curObj = group->GetChildrenList()->GetNext(curObj);
|
|
}
|
|
}
|
|
|
|
void gather3DObjects(sgCObject* obj, std::vector<sgC3DObject*>& objects)
|
|
{
|
|
switch(obj->GetType())
|
|
{
|
|
case SG_OT_GROUP:
|
|
{
|
|
gatherGroup(reinterpret_cast<sgCGroup*>(obj), objects);
|
|
break;
|
|
}
|
|
case SG_OT_3D:
|
|
{
|
|
objects.push_back(reinterpret_cast<sgC3DObject*>(obj->Clone()));
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void applyMatrixTo3DObjects(sgCObject* obj, const sgCMatrix& matrix);
|
|
|
|
void applyMatrixToGroup(sgCGroup* group, const sgCMatrix& matrix)
|
|
{
|
|
sgCObject* curObj = group->GetChildrenList()->GetHead();
|
|
while (curObj)
|
|
{
|
|
applyMatrixTo3DObjects(curObj, matrix);
|
|
curObj = group->GetChildrenList()->GetNext(curObj);
|
|
}
|
|
}
|
|
|
|
void applyMatrixTo3DObjects(sgCObject* obj, const sgCMatrix& matrix)
|
|
{
|
|
switch(obj->GetType())
|
|
{
|
|
case SG_OT_GROUP:
|
|
{
|
|
applyMatrixToGroup(reinterpret_cast<sgCGroup*>(obj), matrix);
|
|
break;
|
|
}
|
|
case SG_OT_3D:
|
|
{
|
|
sgC3DObject* obj3D = reinterpret_cast<sgC3DObject*>(obj);
|
|
|
|
obj3D->Transform(matrix);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void applyColorTo3DObjects(sgCObject* obj, const Vector3& value);
|
|
|
|
void applyColorToGroup(sgCGroup* group, const Vector3& value)
|
|
{
|
|
sgCObject* curObj = group->GetChildrenList()->GetHead();
|
|
while (curObj)
|
|
{
|
|
applyColorTo3DObjects(curObj, value);
|
|
curObj = group->GetChildrenList()->GetNext(curObj);
|
|
}
|
|
}
|
|
|
|
void applyColorTo3DObjects(sgCObject* obj, const Vector3& value)
|
|
{
|
|
switch(obj->GetType())
|
|
{
|
|
case SG_OT_GROUP:
|
|
{
|
|
applyColorToGroup(reinterpret_cast<sgCGroup*>(obj), value);
|
|
break;
|
|
}
|
|
case SG_OT_3D:
|
|
{
|
|
sgC3DObject* obj3D = reinterpret_cast<sgC3DObject*>(obj);
|
|
SG_POINT color;
|
|
color.x = value.x;
|
|
color.y = value.y;
|
|
color.z = value.z;
|
|
obj3D->SetColor(color);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void applyScaleTo3DObjects(sgCObject* obj, const Vector3& scale);
|
|
|
|
void applyScaleToGroup(sgCGroup* group, const Vector3& scale)
|
|
{
|
|
sgCObject* curObj = group->GetChildrenList()->GetHead();
|
|
while (curObj)
|
|
{
|
|
applyScaleTo3DObjects(curObj, scale);
|
|
curObj = group->GetChildrenList()->GetNext(curObj);
|
|
}
|
|
}
|
|
|
|
void applyScaleTo3DObjects(sgCObject* obj, const Vector3& scale)
|
|
{
|
|
switch(obj->GetType())
|
|
{
|
|
case SG_OT_GROUP:
|
|
{
|
|
applyScaleToGroup(reinterpret_cast<sgCGroup*>(obj), scale);
|
|
break;
|
|
}
|
|
case SG_OT_3D:
|
|
{
|
|
sgC3DObject* obj3D = reinterpret_cast<sgC3DObject*>(obj);
|
|
SG_POINT scaleDp;
|
|
scaleDp.x = scale.x;
|
|
scaleDp.y = scale.y;
|
|
scaleDp.z = scale.z;
|
|
obj3D->Scale(scaleDp);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void applyTranslationTo3DObjects(sgCObject* obj, const Vector3& translation);
|
|
|
|
void applyTranslationToGroup(sgCGroup* group, const Vector3& translation)
|
|
{
|
|
sgCObject* curObj = group->GetChildrenList()->GetHead();
|
|
while (curObj)
|
|
{
|
|
applyTranslationTo3DObjects(curObj, translation);
|
|
curObj = group->GetChildrenList()->GetNext(curObj);
|
|
}
|
|
}
|
|
|
|
void applyTranslationTo3DObjects(sgCObject* obj, const Vector3& translation)
|
|
{
|
|
switch(obj->GetType())
|
|
{
|
|
case SG_OT_GROUP:
|
|
{
|
|
applyTranslationToGroup(reinterpret_cast<sgCGroup*>(obj), translation);
|
|
break;
|
|
}
|
|
case SG_OT_3D:
|
|
{
|
|
sgC3DObject* obj3D = reinterpret_cast<sgC3DObject*>(obj);
|
|
SG_POINT translationDp;
|
|
translationDp.x = translation.x;
|
|
translationDp.y = translation.y;
|
|
translationDp.z = translation.z;
|
|
obj3D->Translate(translationDp);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void calcFlatNormal(CSGVertex& vertA, CSGVertex& vertB, CSGVertex& vertC)
|
|
{
|
|
Vector3 normal = (vertB.pos - vertA.pos).cross(vertC.pos - vertA.pos);
|
|
|
|
// Keep the magnitude of the cross product to use as a weighting for the
|
|
// average.
|
|
vertA.normal = vertB.normal = vertC.normal = normal;
|
|
}
|
|
|
|
void calcFlatTangent(CSGVertex& vertA, CSGVertex& vertB, CSGVertex& vertC)
|
|
{
|
|
const Vector3& v1 = vertA.pos;
|
|
const Vector3& v2 = vertB.pos;
|
|
const Vector3& v3 = vertC.pos;
|
|
const Vector2& w1 = vertA.uv;
|
|
const Vector2& w2 = vertB.uv;
|
|
const Vector2& w3 = vertC.uv;
|
|
|
|
float x1 = v2.x - v1.x;
|
|
float x2 = v3.x - v1.x;
|
|
float y1 = v2.y - v1.y;
|
|
float y2 = v3.y - v1.y;
|
|
float z1 = v2.z - v1.z;
|
|
float z2 = v3.z - v1.z;
|
|
float s1 = w2.x - w1.x;
|
|
float s2 = w3.x - w1.x;
|
|
float t1 = w2.y - w1.y;
|
|
float t2 = w3.y - w1.y;
|
|
|
|
float r = (s1 * t2 - s2 * t1);
|
|
if (r != 0)
|
|
r = 1.0f / r;
|
|
else
|
|
r = 1.0f;
|
|
|
|
Vector3 sdir = Vector3((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r);
|
|
|
|
vertA.tangent = vertB.tangent = vertC.tangent = sdir;
|
|
}
|
|
|
|
void averageNormal(Vector3& resultNormal,
|
|
const Vector3& posA,
|
|
const Vector3& posB,
|
|
const Vector3& normalA,
|
|
const Vector3& normalB)
|
|
{
|
|
static const float cosAngle = cos(G3D::toRadians(40));
|
|
|
|
Vector3 distP = posB - posA;
|
|
const float eps = 0.1f;
|
|
|
|
if (fabs(distP.x) < eps &&
|
|
fabs(distP.y) < eps &&
|
|
fabs(distP.z) < eps)
|
|
{
|
|
float dotProd = normalA.unit().dot(normalB.unit());
|
|
if (dotProd > cosAngle)
|
|
{
|
|
resultNormal += normalB;
|
|
}
|
|
}
|
|
}
|
|
|
|
void calcSmoothNormal(CSGVertex& vert, Vector3& normal, const std::vector<CSGVertex>& vertices, std::vector<unsigned int>& indices)
|
|
{
|
|
for (size_t i = 0; i < vertices.size(); i++)
|
|
{
|
|
const CSGVertex& testVert = vertices[i];
|
|
averageNormal(normal, vert.pos, testVert.pos, vert.normal, testVert.normal);
|
|
}
|
|
}
|
|
|
|
void calcSmoothTangent(CSGVertex& vert, Vector3& tangent, const std::vector<CSGVertex>& vertices, std::vector<unsigned int>& indices)
|
|
{
|
|
for (size_t i = 0; i < vertices.size(); i++)
|
|
{
|
|
const CSGVertex& testVert = vertices[i];
|
|
averageNormal(tangent, vert.pos, testVert.pos, vert.tangent, testVert.tangent);
|
|
}
|
|
}
|
|
|
|
void calcUV(CSGVertex& vert)
|
|
{
|
|
Vector3& Pt = vert.pos;
|
|
Vector3 unitNormal = vert.normal.unit();
|
|
|
|
if (fabs(unitNormal.x) > fabs(unitNormal.y) &&
|
|
fabs(unitNormal.x) > fabs(unitNormal.z))
|
|
{
|
|
vert.extra.r = unitNormal.x > 0 ? CSGVertex::UV_BOX_X : CSGVertex::UV_BOX_X_NEG;
|
|
}
|
|
else if (fabs(unitNormal.y) > fabs(unitNormal.z))
|
|
{
|
|
vert.extra.r = unitNormal.y > 0 ? CSGVertex::UV_BOX_Y : CSGVertex::UV_BOX_Y_NEG;
|
|
}
|
|
else
|
|
{
|
|
vert.extra.r = unitNormal.z > 0 ? CSGVertex::UV_BOX_Z : CSGVertex::UV_BOX_Z_NEG;
|
|
}
|
|
|
|
vert.uv = vert.generateUv(vert.pos);
|
|
}
|
|
|
|
void calcFlat( Vector3& normal, unsigned int& uvr,
|
|
CSGVertex& vertA, CSGVertex& vertB, CSGVertex& vertC)
|
|
{
|
|
normal = (vertB.pos - vertA.pos).cross(vertC.pos - vertA.pos);
|
|
|
|
if (fabs(normal.x) > fabs(normal.y) &&
|
|
fabs(normal.x) > fabs(normal.z))
|
|
{
|
|
uvr = normal.x > 0 ? CSGVertex::UV_BOX_X : CSGVertex::UV_BOX_X_NEG;
|
|
}
|
|
else if (fabs(normal.y) > fabs(normal.z))
|
|
{
|
|
uvr = normal.y > 0 ? CSGVertex::UV_BOX_Y : CSGVertex::UV_BOX_Y_NEG;
|
|
}
|
|
else
|
|
{
|
|
uvr = normal.z > 0 ? CSGVertex::UV_BOX_Z : CSGVertex::UV_BOX_Z_NEG;
|
|
}
|
|
}
|
|
|
|
void calcFlatTangent(Vector3& tangent, CSGVertex& vertA, CSGVertex& vertB, CSGVertex& vertC)
|
|
{
|
|
const Vector3& v1 = vertA.pos;
|
|
const Vector3& v2 = vertB.pos;
|
|
const Vector3& v3 = vertC.pos;
|
|
const Vector2& w1 = vertA.uv;
|
|
const Vector2& w2 = vertB.uv;
|
|
const Vector2& w3 = vertC.uv;
|
|
|
|
float x1 = v2.x - v1.x;
|
|
float x2 = v3.x - v1.x;
|
|
float y1 = v2.y - v1.y;
|
|
float y2 = v3.y - v1.y;
|
|
float z1 = v2.z - v1.z;
|
|
float z2 = v3.z - v1.z;
|
|
float s1 = w2.x - w1.x;
|
|
float s2 = w3.x - w1.x;
|
|
float t1 = w2.y - w1.y;
|
|
float t2 = w3.y - w1.y;
|
|
|
|
float r = (s1 * t2 - s2 * t1);
|
|
if (r != 0)
|
|
r = 1.0f / r;
|
|
else
|
|
r = 1.0f;
|
|
|
|
tangent = Vector3((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r);
|
|
}
|
|
|
|
bool triangulateObject(sgCObject* obj, unsigned int& counter, std::vector<CSGVertex>& vertices, std::vector<unsigned int>& indices);
|
|
|
|
bool triangulateGroup(sgCGroup* group, unsigned int& counter, std::vector<CSGVertex>& vertices, std::vector<unsigned int>& indices)
|
|
{
|
|
sgCObject* curObj = group->GetChildrenList()->GetHead();
|
|
while (curObj)
|
|
{
|
|
triangulateObject(curObj, counter, vertices, indices);
|
|
curObj = group->GetChildrenList()->GetNext(curObj);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool triangulate3D(sgC3DObject* object, unsigned int& index, std::vector<CSGVertex>& vertices, std::vector<unsigned int>& indices)
|
|
{
|
|
object->Triangulate(SG_VERTEX_TRIANGULATION);
|
|
{
|
|
SG_MATERIAL material;
|
|
material.MaterialIndex = 0;
|
|
material.TextureUVType = SG_CUBE_UV_TYPE;
|
|
material.TextureScaleU = 1;
|
|
material.TextureScaleV = 1;
|
|
material.TextureShiftU = 0;
|
|
material.TextureShiftV = 0;
|
|
material.TextureSmooth = false;
|
|
material.TextureMult = true;
|
|
material.MixColorType = SG_BLEND_MIX_TYPE;
|
|
object->SetMaterial(material);
|
|
}
|
|
|
|
const SG_ALL_TRIANGLES* triangles = reinterpret_cast<sgC3DObject*>(object)->GetTriangles();
|
|
|
|
if (triangles)
|
|
{
|
|
for(int i = 0, j=0; i < 3*triangles->nTr; i += 3, j+=6)
|
|
{
|
|
CSGVertex vertA;
|
|
vertA.color = Color4uint8(triangles->allColors[i].x*255, triangles->allColors[i].y*255, triangles->allColors[i].z*255, 255);
|
|
vertA.pos = Vector3(triangles->allVertex[i].x, triangles->allVertex[i].y, triangles->allVertex[i].z);
|
|
|
|
CSGVertex vertB;
|
|
vertB.color = Color4uint8(triangles->allColors[i+1].x*255, triangles->allColors[i+1].y*255, triangles->allColors[i+1].z*255, 255);
|
|
vertB.pos = Vector3(triangles->allVertex[i+1].x, triangles->allVertex[i+1].y, triangles->allVertex[i+1].z);
|
|
|
|
CSGVertex vertC;
|
|
vertC.color = Color4uint8(triangles->allColors[i+2].x*255, triangles->allColors[i+2].y*255, triangles->allColors[i+2].z*255, 255);
|
|
vertC.pos = Vector3(triangles->allVertex[i+2].x, triangles->allVertex[i+2].y, triangles->allVertex[i+2].z);
|
|
|
|
vertices.push_back(vertA);
|
|
indices.push_back(index++);
|
|
vertices.push_back(vertB);
|
|
indices.push_back(index++);
|
|
vertices.push_back(vertC);
|
|
indices.push_back(index++);
|
|
}
|
|
}
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
bool triangulateObject(sgCObject* obj, unsigned int& counter, std::vector<CSGVertex>& vertices, std::vector<unsigned int>& indices)
|
|
{
|
|
switch(obj->GetType())
|
|
{
|
|
case SG_OT_GROUP:
|
|
return triangulateGroup(reinterpret_cast<sgCGroup*>(obj), counter, vertices, indices);
|
|
case SG_OT_3D:
|
|
return triangulate3D(reinterpret_cast<sgC3DObject*>(obj), counter, vertices, indices);
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CSGMeshSgCore::newTriangulate()
|
|
{
|
|
unsigned int counter = 0;
|
|
|
|
vertices.clear();
|
|
indices.clear();
|
|
if (!editData.getShape())
|
|
return true;
|
|
triangulateObject(editData.getShape(), counter, vertices, indices);
|
|
size_t nIndices = indices.size();
|
|
int nFaces = nIndices / 3;
|
|
std::vector<Color4uint8> flatColors;
|
|
flatColors.resize(nFaces);
|
|
int n = 0;
|
|
for (size_t i = 0; i < nIndices; i+=3, n++)
|
|
{
|
|
flatColors[n] = vertices[indices[i ]].color;
|
|
}
|
|
size_t maxFacesPerVertex = clusterVertices( 0.001f );
|
|
for ( auto i = indices.begin(); i != indices.end(); )
|
|
{
|
|
int i0, i1, i2;
|
|
i0 = *i;
|
|
i1 = *(i+1);
|
|
i2 = *(i+2);
|
|
if ( i0 == i1 ||
|
|
i1 == i2 ||
|
|
i2 == i0 )
|
|
{
|
|
std::vector<Color4uint8>::iterator c = flatColors.begin();
|
|
auto x = i - indices.begin();
|
|
c += x/3;
|
|
flatColors.erase( c );
|
|
i = indices.erase(i, i+3);
|
|
}
|
|
else
|
|
i += 3;
|
|
}
|
|
std::vector<int> vertexEdges;
|
|
vertexEdges.resize( vertices.size(), -1 );
|
|
if ( !makeHalfEdges( vertexEdges ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
nIndices = indices.size();
|
|
nFaces = nIndices / 3;
|
|
|
|
std::vector<Vector3> flatNormals;
|
|
std::vector<unsigned int> flatUVrs;
|
|
|
|
flatNormals.resize(nFaces);
|
|
flatUVrs.resize(nFaces);
|
|
|
|
n = 0;
|
|
for (size_t i = 0; i < nIndices; i+=3, n++)
|
|
{
|
|
CSGVertex& vertA = vertices[indices[i ]];
|
|
CSGVertex& vertB = vertices[indices[i+1]];
|
|
CSGVertex& vertC = vertices[indices[i+2]];
|
|
calcFlat(flatNormals[n], flatUVrs[n],
|
|
vertA, vertB, vertC);
|
|
}
|
|
|
|
// find creased edges
|
|
static const float cosAngle = cos(G3D::toRadians(40));
|
|
for ( size_t e = 0; e < halfEdges.size(); e++ )
|
|
{
|
|
CSGHalfEdge& hE = halfEdges[e];
|
|
if ( !hE.creaseSet )
|
|
{
|
|
if ( hE.oppEdge >= 0 )
|
|
{
|
|
CSGHalfEdge& hEo = halfEdges[hE.oppEdge];
|
|
int f1 = hE.face;
|
|
int f2 = hEo.face;
|
|
float dotProd = flatNormals[f1].unit().dot(flatNormals[f2].unit());
|
|
if (dotProd < cosAngle)
|
|
hE.creaseFlag |= CSGHalfEdge::normalCrease;
|
|
if ( flatUVrs[f1] != flatUVrs[f2] )
|
|
hE.creaseFlag |= CSGHalfEdge::uvCrease;
|
|
if ( flatColors[f1] != flatColors[f2] )
|
|
hE.creaseFlag |= CSGHalfEdge::colorCrease;
|
|
hE.creaseSet = true;
|
|
hEo.creaseFlag = hE.creaseFlag;
|
|
hEo.creaseSet = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// duplicate vertices for creases, put them in circular lists
|
|
size_t nVerts = vertices.size();
|
|
std::vector<triangulationVertex> triVerts;
|
|
triVerts.resize(nVerts);
|
|
|
|
int *vertFaces = new int[maxFacesPerVertex];
|
|
unsigned int *creaseFlags = new unsigned int[maxFacesPerVertex];
|
|
int *creaseFaces = new int[maxFacesPerVertex];
|
|
for ( size_t iVert = 0; iVert < nVerts; iVert++ )
|
|
{
|
|
int vertexFaceCount = 0;
|
|
int creaseFaceCount = 0;
|
|
CSGVertex& currentVertexRef = vertices[iVert];
|
|
triangulationVertex& currentTriVertexRef = triVerts[iVert];
|
|
currentTriVertexRef.neighborVert[0] = currentTriVertexRef.neighborVert[1] = iVert;
|
|
int firstVertexHalfEdge = vertexEdges[iVert];
|
|
currentVertexRef.extra.r = flatUVrs[halfEdges[firstVertexHalfEdge].face];
|
|
currentVertexRef.generateUv();
|
|
CSGVertex currentVertex = currentVertexRef;
|
|
int iterateVertexHalfEdge = firstVertexHalfEdge;
|
|
do
|
|
{
|
|
CSGHalfEdge& hE = halfEdges[iterateVertexHalfEdge];
|
|
if ( hE.oppEdge < 0 )
|
|
{
|
|
delete[] vertFaces;
|
|
delete[] creaseFlags;
|
|
delete[] creaseFaces;
|
|
return false;
|
|
}
|
|
CSGHalfEdge& hEo = halfEdges[hE.oppEdge];
|
|
int f = hEo.face;
|
|
if ( hE.creaseFlag != hEo.creaseFlag )
|
|
{
|
|
delete[] vertFaces;
|
|
delete[] creaseFlags;
|
|
delete[] creaseFaces;
|
|
return false;
|
|
}
|
|
if ( hE.creaseFlag != 0 )
|
|
{
|
|
creaseFlags[creaseFaceCount] = hE.creaseFlag;
|
|
creaseFaces[creaseFaceCount++] = vertexFaceCount;
|
|
}
|
|
vertFaces[vertexFaceCount++] = f;
|
|
iterateVertexHalfEdge = hEo.nextEdge;
|
|
}
|
|
while ( iterateVertexHalfEdge != firstVertexHalfEdge );
|
|
if ( creaseFaceCount > 1 )
|
|
{
|
|
int newIVert = vertices.size();
|
|
currentTriVertexRef.duplicateCount = creaseFaceCount;
|
|
currentTriVertexRef.neighborCreaseFlag[0] = creaseFlags[creaseFaceCount-1];
|
|
currentTriVertexRef.neighborCreaseFlag[1] = creaseFlags[0];
|
|
currentTriVertexRef.neighborVert[0] = newIVert + creaseFaceCount-2;
|
|
currentTriVertexRef.neighborVert[1] = newIVert;
|
|
int firstVert = iVert;
|
|
int previ = iVert;
|
|
int newNverts = newIVert + creaseFaceCount-1;
|
|
vertices.resize( newNverts, currentVertex );
|
|
triVerts.resize( newNverts );
|
|
for ( int iCreaseFace = 0; iCreaseFace < creaseFaceCount-1; iCreaseFace++, newIVert++ )
|
|
{
|
|
CSGVertex& newvx = vertices[newIVert];
|
|
triangulationVertex& newVt = triVerts[newIVert];
|
|
newVt.duplicateCount = creaseFaceCount;
|
|
newVt.neighborVert[0] = previ;
|
|
newVt.neighborVert[1] = iCreaseFace == creaseFaceCount-2 ? firstVert : newIVert+1;
|
|
newVt.neighborCreaseFlag[0] = creaseFlags[iCreaseFace];
|
|
newVt.neighborCreaseFlag[1] = creaseFlags[iCreaseFace+1];
|
|
int currentCreaseFace = creaseFaces[iCreaseFace];
|
|
int nextCreaseFace = creaseFaces[iCreaseFace+1];
|
|
newvx.extra.r = flatUVrs[vertFaces[currentCreaseFace]];
|
|
newvx.generateUv();
|
|
for ( int cv = currentCreaseFace; cv < nextCreaseFace; cv++ )
|
|
{
|
|
int faceIndex0 = vertFaces[cv] * 3;
|
|
bool rplcd = false;
|
|
for ( int r = 0; !rplcd && r < 3; r++ )
|
|
if ( rplcd = (indices[faceIndex0+r] == iVert) )
|
|
indices[faceIndex0+r] = newIVert;
|
|
}
|
|
previ = newIVert;
|
|
}
|
|
}
|
|
}
|
|
delete[] vertFaces;
|
|
delete[] creaseFlags;
|
|
delete[] creaseFaces;
|
|
|
|
nVerts = vertices.size();
|
|
std::vector<Vector3> normals;
|
|
std::vector<Vector3> tangents;
|
|
normals.resize(nVerts);
|
|
tangents.resize(nVerts);
|
|
|
|
n = 0;
|
|
for (size_t i = 0; i < nIndices; i+=3, n++)
|
|
{
|
|
Vector3 fNormal = flatNormals[n];
|
|
normals[indices[i ]] += fNormal;
|
|
normals[indices[i+1]] += fNormal;
|
|
normals[indices[i+2]] += fNormal;
|
|
|
|
Vector3 flatTangent;
|
|
calcFlatTangent( flatTangent, vertices[indices[i ]],
|
|
vertices[indices[i+1]],
|
|
vertices[indices[i+2]] );
|
|
tangents[indices[i ]] += flatTangent;
|
|
tangents[indices[i+1]] += flatTangent;
|
|
tangents[indices[i+2]] += flatTangent;
|
|
|
|
auto fColor = flatColors[n];
|
|
vertices[indices[i ]].color = fColor;
|
|
vertices[indices[i+1]].color = fColor;
|
|
vertices[indices[i+2]].color = fColor;
|
|
}
|
|
|
|
std::vector<Vector3> cNormals;
|
|
std::vector<Vector3> cTangents;
|
|
cNormals.resize(nVerts);
|
|
cTangents.resize(nVerts);
|
|
|
|
// average normals and tangents over non-specific creases
|
|
std::vector<Vector3>* cNormalsTangents[2] = { &cNormals, &cTangents };
|
|
Vector3 vectorNormalTangent[2];
|
|
unsigned int creasFlagNT[2];
|
|
creasFlagNT[0] = CSGHalfEdge::normalCrease;
|
|
creasFlagNT[1] = CSGHalfEdge::uvCrease;
|
|
for ( size_t iVert = 0; iVert < nVerts; iVert++ )
|
|
{
|
|
vectorNormalTangent[0] = normals[iVert];
|
|
vectorNormalTangent[1] = tangents[iVert];
|
|
cNormals[iVert] += normals[iVert];
|
|
cTangents[iVert] += tangents[iVert];
|
|
CSGVertex vert = vertices[iVert];
|
|
int vertDuplicateCount = triVerts[iVert].duplicateCount;
|
|
if ( vertDuplicateCount > 1 )
|
|
{
|
|
int nc;
|
|
bool creased;
|
|
|
|
for ( int normtang = 0; normtang < 2; normtang++ ) //do for normal and tangent
|
|
{
|
|
nc = 0;
|
|
for ( int neighbor = 0; neighbor < 2; neighbor++ ) // walk both ways
|
|
{
|
|
creased = false;
|
|
CSGVertex wVert = vert;
|
|
int nextVert = iVert;
|
|
while ( !creased && nc < vertDuplicateCount-1 )
|
|
{
|
|
if ( !( creased = ( triVerts[nextVert].neighborCreaseFlag[neighbor] & creasFlagNT[normtang] ) != 0 ) )
|
|
{
|
|
nextVert = triVerts[nextVert].neighborVert[neighbor];
|
|
(*cNormalsTangents[normtang])[nextVert] += vectorNormalTangent[normtang];
|
|
nc++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (size_t i = 0; i < nVerts; i++)
|
|
{
|
|
CSGVertex& vert = vertices[i];
|
|
vert.tangent = cTangents[i].directionOrZero();
|
|
vert.normal = cNormals[i].directionOrZero();
|
|
}
|
|
|
|
computeDecalRemap();
|
|
|
|
return true;
|
|
}
|
|
|
|
void CSGMeshSgCore::triangulate()
|
|
{
|
|
unsigned int counter = 0;
|
|
|
|
vertices.clear();
|
|
indices.clear();
|
|
if (!editData.getShape())
|
|
return;
|
|
triangulateObject(editData.getShape(), counter, vertices, indices);
|
|
|
|
std::vector<Vector3> normals;
|
|
std::vector<Vector3> tangents;
|
|
|
|
normals.resize(vertices.size());
|
|
tangents.resize(vertices.size());
|
|
|
|
for (size_t i = 0; i < indices.size(); i+=3)
|
|
{
|
|
CSGVertex& vertA = vertices[indices[i]];
|
|
CSGVertex& vertB = vertices[indices[i+1]];
|
|
CSGVertex& vertC = vertices[indices[i+2]];
|
|
calcFlatNormal(vertA, vertB, vertC);
|
|
calcUV(vertA);
|
|
calcUV(vertB);
|
|
calcUV(vertC);
|
|
calcFlatTangent(vertA, vertB, vertC);
|
|
}
|
|
|
|
for (size_t i = 0; i < vertices.size(); i++)
|
|
{
|
|
CSGVertex& vert = vertices[i];
|
|
calcSmoothNormal(vert, normals[i], vertices, indices);
|
|
calcSmoothTangent(vert, tangents[i], vertices, indices);
|
|
}
|
|
|
|
for (size_t i = 0; i < vertices.size(); i++)
|
|
{
|
|
CSGVertex& vert = vertices[i];
|
|
if (tangents[i].length() > 0)
|
|
vert.tangent = tangents[i].unit();
|
|
if (normals[i].length() > 0)
|
|
vert.normal = normals[i].unit();
|
|
}
|
|
|
|
weldMesh();
|
|
}
|
|
|
|
bool CSGMeshSgCore::sgCoreUnion(const CSGMeshSgCore& a, const CSGMeshSgCore& b)
|
|
{
|
|
if (!a.editData.getShape() || !b.editData.getShape())
|
|
return false;
|
|
|
|
std::vector<sgC3DObject*> objects;
|
|
gather3DObjects(a.editData.getShape(), objects);
|
|
gather3DObjects(b.editData.getShape(), objects);
|
|
|
|
for (size_t i = 0; i < objects.size(); i++)
|
|
{
|
|
if (objects[i] == NULL)
|
|
continue;
|
|
|
|
for (size_t o = 0; o < objects.size(); o++)
|
|
{
|
|
if (i == o)
|
|
continue;
|
|
|
|
if (objects[o] == NULL)
|
|
continue;
|
|
|
|
if (objects[i] == NULL)
|
|
break;
|
|
|
|
int errcode = 0;
|
|
|
|
if (FFlag::CSGExportFailure)
|
|
logError(objects[i], objects[o]);
|
|
|
|
sgCGroup* group = sgBoolean::Union(*objects[i], *objects[o], errcode);
|
|
|
|
if (errcode == 2)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (group)
|
|
{
|
|
int numChildren = group->GetChildrenList()->GetCount();
|
|
std::vector<sgCObject*> allChildren(numChildren);
|
|
group->BreakGroup(&allChildren[0]);
|
|
sgDeleteObject(group);
|
|
|
|
for (size_t r = 0; r < allChildren.size(); r++)
|
|
{
|
|
objects.push_back((sgC3DObject*)allChildren[r]);
|
|
}
|
|
|
|
sgDeleteObject(objects[i]);
|
|
sgDeleteObject(objects[o]);
|
|
objects[i] = NULL;
|
|
objects[o] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
size_t shrinkSize = 0;
|
|
for (size_t i = 0; i < objects.size(); i++)
|
|
{
|
|
if (objects[i] == NULL)
|
|
continue;
|
|
|
|
objects[shrinkSize] = objects[i];
|
|
shrinkSize++;
|
|
}
|
|
objects.resize(shrinkSize);
|
|
|
|
if (objects.size() > 0)
|
|
{
|
|
editData.setShape(sgCGroup::CreateGroup((sgCObject**)&objects[0], int(objects.size())));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CSGMeshSgCore::sgCoreSubtract(const CSGMeshSgCore& a, const CSGMeshSgCore& b)
|
|
{
|
|
if (!a.editData.getShape() || !b.editData.getShape())
|
|
return false;
|
|
|
|
std::vector<sgC3DObject*> objectsA;
|
|
gather3DObjects(a.editData.getShape(), objectsA);
|
|
std::vector<sgC3DObject*> objectsB;
|
|
gather3DObjects(b.editData.getShape(), objectsB);
|
|
|
|
for (size_t i = 0; i < objectsA.size(); i++)
|
|
{
|
|
if (objectsA[i] == NULL)
|
|
continue;
|
|
|
|
bool matchFound = false;
|
|
|
|
for (size_t o = 0; o < objectsB.size(); o++)
|
|
{
|
|
if (objectsA[i] == NULL || objectsB[o] == NULL)
|
|
continue;
|
|
|
|
int errcode = 0;
|
|
|
|
if (FFlag::CSGExportFailure)
|
|
logError(objectsA[i], objectsB[o], false);
|
|
|
|
sgCGroup* group = sgBoolean::Sub(*objectsA[i], *objectsB[o], errcode);
|
|
|
|
if (errcode == 4)
|
|
{
|
|
sgDeleteObject(objectsA[i]);
|
|
objectsA[i] = 0;
|
|
}
|
|
else if (errcode > 1)
|
|
{
|
|
return false;
|
|
}
|
|
else if (group)
|
|
{
|
|
int numChildren = group->GetChildrenList()->GetCount();
|
|
std::vector<sgCObject*> allChildren(numChildren);
|
|
group->BreakGroup(&allChildren[0]);
|
|
|
|
sgDeleteObject(group);
|
|
sgDeleteObject(objectsA[i]);
|
|
objectsA[i] = 0;
|
|
|
|
if (allChildren.size() > 0)
|
|
objectsA[i] = (sgC3DObject*)allChildren[0];
|
|
|
|
for (size_t r = 1; r < allChildren.size(); r++)
|
|
{
|
|
objectsA.push_back((sgC3DObject*)allChildren[r]);
|
|
}
|
|
|
|
matchFound = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (size_t i = 0; i < objectsB.size(); i++)
|
|
{
|
|
sgDeleteObject(objectsB[i]);
|
|
}
|
|
|
|
size_t shrinkSize = 0;
|
|
for (size_t i = 0; i < objectsA.size(); i++)
|
|
{
|
|
if (objectsA[i] == NULL)
|
|
continue;
|
|
|
|
objectsA[shrinkSize] = objectsA[i];
|
|
shrinkSize++;
|
|
}
|
|
objectsA.resize(shrinkSize);
|
|
|
|
if (objectsA.size() > 0)
|
|
{
|
|
editData.setShape(sgCGroup::CreateGroup((sgCObject**)&objectsA[0], int(objectsA.size())));
|
|
}
|
|
else
|
|
{
|
|
editData.destroy();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CSGMeshSgCore::unionMesh(const CSGMesh* a, const CSGMesh* b)
|
|
{
|
|
const CSGMeshSgCore* sgMeshA = dynamic_cast<const CSGMeshSgCore*>(a);
|
|
const CSGMeshSgCore* sgMeshB = dynamic_cast<const CSGMeshSgCore*>(b);
|
|
|
|
if (!sgMeshA || !sgMeshB)
|
|
return false;
|
|
|
|
return sgCoreUnion(*sgMeshA, *sgMeshB);
|
|
}
|
|
|
|
bool CSGMeshSgCore::intersectMesh(const CSGMesh* a, const CSGMesh* b)
|
|
{
|
|
// Not implemented yet.
|
|
return false;
|
|
}
|
|
|
|
bool CSGMeshSgCore::subractMesh(const CSGMesh* a, const CSGMesh* b)
|
|
{
|
|
const CSGMeshSgCore* sgMeshA = dynamic_cast<const CSGMeshSgCore*>(a);
|
|
const CSGMeshSgCore* sgMeshB = dynamic_cast<const CSGMeshSgCore*>(b);
|
|
|
|
if (!sgMeshA || !sgMeshB)
|
|
return false;
|
|
|
|
return sgCoreSubtract(*sgMeshA, *sgMeshB);
|
|
}
|
|
|
|
void CSGMeshSgCore::applyCoordinateFrame(CoordinateFrame cFrame)
|
|
{
|
|
if (!editData.getShape())
|
|
return;
|
|
|
|
float dmatrix[16];
|
|
|
|
dmatrix[0] = cFrame.rotation[0][0];
|
|
dmatrix[1] = cFrame.rotation[0][1];
|
|
dmatrix[2] = cFrame.rotation[0][2];
|
|
dmatrix[3] = 0.0;
|
|
|
|
dmatrix[4] = cFrame.rotation[1][0];
|
|
dmatrix[5] = cFrame.rotation[1][1];
|
|
dmatrix[6] = cFrame.rotation[1][2];
|
|
dmatrix[7] = 0.0;
|
|
|
|
dmatrix[8] = cFrame.rotation[2][0];
|
|
dmatrix[9] = cFrame.rotation[2][1];
|
|
dmatrix[10] = cFrame.rotation[2][2];
|
|
dmatrix[11] = 0.0;
|
|
|
|
dmatrix[12] = 0.0;
|
|
dmatrix[13] = 0.0;
|
|
dmatrix[14] = 0.0;
|
|
dmatrix[15] = 1.0;
|
|
|
|
sgCMatrix matrix(dmatrix);
|
|
|
|
applyMatrixTo3DObjects(editData.getShape(), matrix);
|
|
applyTranslationTo3DObjects(editData.getShape(), cFrame.translation);
|
|
}
|
|
|
|
void CSGMeshSgCore::applyTranslation(const G3D::Vector3& trans)
|
|
{
|
|
if (!editData.getShape())
|
|
return;
|
|
applyTranslationTo3DObjects(editData.getShape(), trans);
|
|
}
|
|
|
|
void CSGMeshSgCore::applyScale(const G3D::Vector3& scale)
|
|
{
|
|
if (!editData.getShape())
|
|
return;
|
|
|
|
applyScaleTo3DObjects(editData.getShape(), scale);
|
|
}
|
|
|
|
void CSGMeshSgCore::applyColor(const G3D::Vector3& color)
|
|
{
|
|
if (!editData.getShape())
|
|
return;
|
|
|
|
applyColorTo3DObjects(editData.getShape(), color);
|
|
}
|
|
|
|
void CSGMeshSgCore::buildBRep()
|
|
{
|
|
if (vertices.size() == 0 || indices.size() < 3)
|
|
return;
|
|
|
|
std::vector<SG_VERT> points;
|
|
std::vector<SG_INDEX_TRIANGLE> triIndices;
|
|
|
|
for (size_t i = 0; i < vertices.size(); i++)
|
|
{
|
|
SG_VERT point;
|
|
point.x = vertices[i].pos.x;
|
|
point.y = vertices[i].pos.y;
|
|
point.z = vertices[i].pos.z;
|
|
point.r = float(vertices[i].color.r)/255.0f;
|
|
point.g = float(vertices[i].color.g)/255.0f;
|
|
point.b = float(vertices[i].color.b)/255.0f;
|
|
points.push_back(point);
|
|
}
|
|
|
|
for (size_t i = 0; i < indices.size(); i+=3)
|
|
{
|
|
SG_INDEX_TRIANGLE index;
|
|
index.ver_indexes[0] = indices[i];
|
|
index.ver_indexes[1] = indices[i+1];
|
|
index.ver_indexes[2] = indices[i+2];
|
|
triIndices.push_back(index);
|
|
}
|
|
|
|
editData.setShape(sgFileManager::ObjectFromTriangles(&points[0], int(points.size()), &triIndices[0], int(triIndices.size()), 45.0f * float(pi()) / 180.0f));
|
|
|
|
if (!editData.getShape())
|
|
return;
|
|
}
|
|
|
|
std::string CSGMeshSgCore::getBRepBinaryString() const
|
|
{
|
|
if (!editData.getShape())
|
|
return "";
|
|
|
|
unsigned long arraySize = 0;
|
|
const char* objectByteArray = (const char*)sgFileManager::ObjectToBitArray(editData.getShape(), arraySize);
|
|
|
|
if (arraySize == 0)
|
|
return "";
|
|
|
|
std::stringstream stream;
|
|
stream.write(reinterpret_cast<const char*>(&version), sizeof(version));
|
|
stream.write(reinterpret_cast<const char*>(&arraySize), sizeof(unsigned long));
|
|
stream.write(objectByteArray, arraySize);
|
|
|
|
return stream.str();
|
|
}
|
|
|
|
sgCObject* CSGMeshSgCore::brepFromBinaryString(const std::string& str) const
|
|
{
|
|
if (str.empty())
|
|
return NULL;
|
|
|
|
int brepVersion;
|
|
std::stringstream stream(str);
|
|
stream.read(reinterpret_cast<char*>(&brepVersion), sizeof(brepVersion));
|
|
|
|
unsigned long arraySize = 0;
|
|
stream.read(reinterpret_cast<char*>(&arraySize), sizeof(unsigned long));
|
|
|
|
std::string objectByteArray;
|
|
objectByteArray.resize(arraySize);
|
|
|
|
stream.read(&objectByteArray[0], arraySize);
|
|
|
|
sgCObject* object = sgFileManager::BitArrayToObject((const void*)(objectByteArray.c_str()), (unsigned long)(objectByteArray.size()));
|
|
|
|
return object;
|
|
}
|
|
|
|
|
|
void CSGMeshSgCore::setBRepFromBinaryString(const std::string& str)
|
|
{
|
|
if (str.empty())
|
|
return;
|
|
|
|
sgCObject* object = brepFromBinaryString(str);
|
|
|
|
if (object)
|
|
{
|
|
editData.setShape(object);
|
|
}
|
|
}
|
|
|
|
bool CSGMeshSgCore::isValid() const
|
|
{
|
|
return editData.getShape() != NULL;
|
|
}
|
|
|
|
CSGMesh* CSGMeshSgCore::clone() const
|
|
{
|
|
CSGMesh* mesh = new CSGMeshSgCore(*this);
|
|
return mesh;
|
|
}
|
|
|
|
CSGClustering::CSGClustering( std::vector<unsigned int>& indices, std::vector<CSGVertex>& vertices,
|
|
const Vector3& minimumExtentsPosition, float invres ):
|
|
m_indices(indices),
|
|
m_vertices(vertices),
|
|
m_minpos( minimumExtentsPosition ),
|
|
m_invres( invres )
|
|
{
|
|
clusters.reserve( m_indices.size() );
|
|
}
|
|
|
|
CSGClustering::IPosClassMap::iterator CSGClustering::addPos( uint64 key, int indx )
|
|
{
|
|
IPosClassMap::iterator it = posclasses.find(key);
|
|
if ( it == posclasses.end() )
|
|
{
|
|
VertexCluster vc;
|
|
vc.posclasses.insert( key );
|
|
vc.indices.insert( indx );
|
|
int ci = clusters.size();
|
|
clusters.push_back( vc );
|
|
auto ip = posclasses.insert( IPosClassMap::value_type( key, ci ) );
|
|
it = ip.first;
|
|
}
|
|
else
|
|
{
|
|
clusters[(it->second)].indices.insert( indx );
|
|
}
|
|
return it;
|
|
}
|
|
|
|
void CSGClustering::mergeClasses( IPosClassMap::iterator it[8] )
|
|
{
|
|
std::set<int>ci;
|
|
for ( int i = 0; i < 8; i++ )
|
|
ci.insert( it[i]->second );
|
|
if ( ci.size() > 1 )
|
|
{
|
|
auto cit = ci.begin();
|
|
int ci0 = *cit;
|
|
VertexCluster& vc0 = clusters[ci0];
|
|
cit++;
|
|
while ( cit != ci.end() )
|
|
{
|
|
VertexCluster& vc1 = clusters[*cit];
|
|
for ( auto it = vc1.indices.begin(); it != vc1.indices.end(); it++ )
|
|
{
|
|
vc0.indices.insert( *it );
|
|
}
|
|
for ( auto it = vc1.posclasses.begin(); it != vc1.posclasses.end(); it++ )
|
|
{
|
|
vc0.posclasses.insert( *it );
|
|
}
|
|
vc1.indices.clear();
|
|
vc1.posclasses.clear();
|
|
cit++;
|
|
}
|
|
for ( int i = 0; i < 8; i++ )
|
|
it[i]->second = ci0;
|
|
}
|
|
}
|
|
|
|
void CSGClustering::addPosClasses( G3D::uint64 key[8], int indx )
|
|
{
|
|
IPosClassMap::iterator it[8];
|
|
for ( unsigned int i = 0; i < 8; i++ )
|
|
{
|
|
it[i] = addPos( key[i], indx );
|
|
}
|
|
mergeClasses( it );
|
|
}
|
|
|
|
uint64 CSGClustering::makeKey( const v3i2& v, unsigned int ii )
|
|
{
|
|
unsigned int i0, i1, i2;
|
|
uint64 li = ii & 7;
|
|
i0 = ii & 1; ii >>= 1;
|
|
i1 = ii & 1; ii >>= 1;
|
|
i2 = ii & 1;
|
|
uint64 x = static_cast<uint64>( v[i0].x );
|
|
uint64 y = static_cast<uint64>( v[i1].y );
|
|
uint64 z = static_cast<uint64>( v[i2].z );
|
|
return ( ( li << 60 ) | ( (z & 0xfffffULL) << 40 ) | ( (y & 0xfffffULL) << 20 ) | (x & 0xfffffULL) );
|
|
}
|
|
|
|
void CSGClustering::cluster()
|
|
{
|
|
for ( unsigned int i = 0; i < m_indices.size(); i++ )
|
|
{
|
|
const Vector3& vpos = m_vertices[m_indices[i]].pos;
|
|
const Vector3int32 ivpos = Vector3int32::floor ( ( vpos - m_minpos ) * m_invres );
|
|
const Vector3int32 iclass0 = ivpos >> 1;
|
|
const Vector3int32 iclass1 = ivpos - iclass0;
|
|
const v3i2 iclass = { iclass0, iclass1 };
|
|
uint64 pkey[8];
|
|
for ( unsigned int p = 0; p < 8; p++ )
|
|
{
|
|
pkey[p] = makeKey( iclass, p );
|
|
}
|
|
addPosClasses( pkey, i );
|
|
}
|
|
}
|
|
|
|
size_t CSGClustering::extractVertices()
|
|
{
|
|
std::vector<CSGVertex> newVertices;
|
|
|
|
size_t maxFaceCount = 0;
|
|
for ( unsigned int i = 0; i < clusters.size(); i++ )
|
|
{
|
|
if ( !clusters[i].indices.empty() )
|
|
{
|
|
auto it = clusters[i].indices.begin();
|
|
unsigned int vi = newVertices.size();
|
|
newVertices.push_back( m_vertices[m_indices[*it]] );
|
|
maxFaceCount = max( maxFaceCount, clusters[i].indices.size() );
|
|
for( ; it != clusters[i].indices.end(); it++ )
|
|
{
|
|
m_indices[*it] = vi;
|
|
}
|
|
}
|
|
}
|
|
m_vertices.swap( newVertices );
|
|
return maxFaceCount;
|
|
}
|
|
|
|
size_t CSGMeshSgCore::clusterVertices( float resolution )
|
|
{
|
|
makeExtents();
|
|
|
|
const Vector3& minimumExtentsPosition = extents.min();
|
|
float szsz = extents.size().max();
|
|
float invres = 1.0f / resolution;
|
|
//float maxres = 2097150.0f * szsz; // 0x1ffffe
|
|
float maxres = 1048574.0f * szsz; // 0xffffe
|
|
if ( invres > maxres )
|
|
invres = maxres;
|
|
CSGClustering clustering( indices, vertices, minimumExtentsPosition, invres );
|
|
clustering.cluster();
|
|
return clustering.extractVertices();
|
|
}
|
|
|
|
void CSGMeshSgCore::makeExtents()
|
|
{
|
|
extents = Extents::negativeMaxExtents();
|
|
|
|
for ( std::vector<CSGVertex>::const_iterator iter = vertices.begin(); iter != vertices.end(); ++iter)
|
|
extents.expandToContain( (*iter).pos );
|
|
}
|
|
|
|
void CSGMeshSgCore::translate( const G3D::Vector3& translation )
|
|
{
|
|
for ( size_t v = 0; v < vertices.size(); v++ )
|
|
{
|
|
vertices[v].pos += translation;
|
|
}
|
|
extents.shift(translation );
|
|
}
|
|
|
|
static const int hix3p[] = {2,0,1};
|
|
static const int hix3n[] = {1,2,0};
|
|
typedef boost::unordered_map <unsigned int,int> HalfEdgeMap;
|
|
|
|
CSGHalfEdge::CSGHalfEdge():
|
|
creaseFlag(0),
|
|
creaseSet(false),
|
|
oppEdge(-1)
|
|
{
|
|
}
|
|
|
|
bool CSGMeshSgCore::makeHalfEdges( std::vector< int>& vertexEdges )
|
|
{
|
|
HalfEdgeMap oppositeEdgesMap;
|
|
|
|
unsigned int nIndices = indices.size();
|
|
halfEdges.resize( nIndices );
|
|
CSGHalfEdge hE;
|
|
unsigned int nFaces = nIndices / 3;
|
|
int iFace3 = 0;
|
|
for ( unsigned int iFace = 0; iFace < nFaces; iFace++, iFace3 += 3 )
|
|
{
|
|
for ( int h = 0; h < 3; h++ )
|
|
{
|
|
int currentIndex = iFace3 + h;
|
|
CSGHalfEdge& hE = halfEdges[currentIndex];
|
|
hE.face = iFace;
|
|
unsigned int currentVertex, nextVertex;
|
|
int nextIndex = iFace3 + hix3n[h];
|
|
currentVertex = hE.startVert = indices[currentIndex];
|
|
if ( vertexEdges[currentVertex] == -1 )
|
|
{
|
|
vertexEdges[currentVertex] = currentIndex;
|
|
}
|
|
nextVertex = indices[nextIndex];
|
|
hE.prevEdge = iFace3 + hix3p[h]; // prevIndex
|
|
hE.nextEdge = nextIndex;
|
|
auto inspair = oppositeEdgesMap.insert( HalfEdgeMap::value_type( currentVertex<<16 | nextVertex, currentIndex ) );
|
|
if( ! inspair.second )
|
|
{
|
|
return false;
|
|
}
|
|
HalfEdgeMap::iterator oppositeEdgeIterator = oppositeEdgesMap.find( nextVertex<<16 | currentVertex );
|
|
if ( oppositeEdgeIterator != oppositeEdgesMap.end() )
|
|
{
|
|
int oppositeEdgeIndex = oppositeEdgeIterator->second;
|
|
hE.oppEdge = oppositeEdgeIndex;
|
|
halfEdges[oppositeEdgeIndex].oppEdge = currentIndex;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
G3D::Vector3 CSGMeshSgCore::extentsCenter()
|
|
{
|
|
return extents.center();
|
|
}
|
|
G3D::Vector3 CSGMeshSgCore::extentsSize()
|
|
{
|
|
return extents.size();
|
|
}
|
|
|
|
} // namespace RBX
|