mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-04 20:57:49 +00:00
922 lines
35 KiB
C++
922 lines
35 KiB
C++
/* Copyright 2014 ROBLOX Corporation, All Rights Reserved */
|
|
#include "stdafx.h"
|
|
|
|
#include "CSGOperations.h"
|
|
#include "Util/RobloxGoogleAnalytics.h"
|
|
#include "v8datamodel/PartOperation.h"
|
|
#include "V8DataModel/CSGDictionaryService.h"
|
|
#include "V8Xml/SerializerBinary.h"
|
|
#include "../Rendering/GfxRender/GeometryGenerator.h"
|
|
#include "V8Datamodel/SpecialMesh.h"
|
|
#include "v8DataModel/Workspace.h"
|
|
#include "v8DataModel/DataModel.h"
|
|
#include "V8DataModel/NonReplicatedCSGDictionaryService.h"
|
|
#include "V8DataModel/ChangeHistory.h"
|
|
#include "../CSG/CSGKernel.h"
|
|
|
|
DYNAMIC_FASTFLAG(MaterialPropertiesEnabled)
|
|
|
|
FASTFLAGVARIABLE(CSGMeshColorToolsEnabled, false)
|
|
FASTFLAGVARIABLE(StudioCSGChildDataNoSizeLimit, false)
|
|
FASTFLAGVARIABLE(CSGNewTriangulateFallBack, true)
|
|
FASTFLAGVARIABLE(CSGDelayParentingOperationToEnd, false)
|
|
|
|
RBX::CSGOperations::CSGOperations( DataModel* pDatamodel, MessageCallback failedFun )
|
|
: mDatamodel(pDatamodel)
|
|
, operationFailed(failedFun)
|
|
{
|
|
}
|
|
|
|
bool RBX::CSGOperations::doUnion(
|
|
std::vector<boost::shared_ptr<RBX::Instance> >::const_iterator start,
|
|
std::vector<boost::shared_ptr<RBX::Instance> >::const_iterator end,
|
|
boost::shared_ptr<PartOperation>& partOperation )
|
|
{
|
|
bool allNeg = true;
|
|
for (Instances::const_iterator iter = start; iter != end; ++iter)
|
|
{
|
|
if (!RBX::Instance::fastSharedDynamicCast<NegateOperation>(*iter))
|
|
{
|
|
allNeg = false;
|
|
break;
|
|
}
|
|
}
|
|
//shared_ptr<RBX::PartOperation> partOperation;
|
|
try
|
|
{
|
|
if (allNeg)
|
|
partOperation = Creatable<Instance>::create<NegateOperation>();
|
|
else
|
|
partOperation = Creatable<Instance>::create<UnionOperation>();
|
|
if (!FFlag::CSGDelayParentingOperationToEnd)
|
|
partOperation->setParent(mDatamodel->getWorkspace());
|
|
}
|
|
catch (std::exception &)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Instance* newOperationParent = NULL;
|
|
if (FFlag::CSGDelayParentingOperationToEnd)
|
|
{
|
|
newOperationParent = getCommonParentOrWorkspace(start, end);
|
|
}
|
|
|
|
//Convert to mesh
|
|
if (!setUnionMesh(partOperation, start, end, false))
|
|
{
|
|
partOperation->setParent(NULL);
|
|
partOperation = boost::shared_ptr<RBX::PartOperation>();
|
|
return false;
|
|
}
|
|
|
|
CSGDictionaryService* dictionaryService = ServiceProvider::create< CSGDictionaryService >(mDatamodel);
|
|
NonReplicatedCSGDictionaryService* nrDictionaryService = ServiceProvider::create<NonReplicatedCSGDictionaryService>(mDatamodel);
|
|
|
|
if (FFlag::CSGDelayParentingOperationToEnd)
|
|
{
|
|
Instances selectionCopy(start, end);
|
|
for (auto iter : selectionCopy)
|
|
{
|
|
iter->setParent(partOperation.get());
|
|
|
|
if (shared_ptr<PartOperation> partOp = RBX::Instance::fastSharedDynamicCast<PartOperation>(iter))
|
|
{
|
|
dictionaryService->retrieveData(*partOp);
|
|
nrDictionaryService->retrieveData(*partOp);
|
|
|
|
partOp->setMeshData(RBX::BinaryString(""));
|
|
partOp->setPhysicsData(RBX::BinaryString(""));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (RBX::Instances::const_iterator iter = start; iter != end; ++iter)
|
|
{
|
|
(*iter)->setParent(partOperation.get());
|
|
|
|
if (shared_ptr<PartOperation> partOp = RBX::Instance::fastSharedDynamicCast<PartOperation>(*iter))
|
|
{
|
|
dictionaryService->retrieveData(*partOp);
|
|
nrDictionaryService->retrieveData(*partOp);
|
|
|
|
partOp->setMeshData(RBX::BinaryString(""));
|
|
partOp->setPhysicsData(RBX::BinaryString(""));
|
|
}
|
|
}
|
|
}
|
|
|
|
//Serialize children
|
|
std::stringstream ss;
|
|
SerializerBinary::serialize(ss, partOperation.get());
|
|
|
|
BinaryString childDataBinaryStr(ss.str());
|
|
|
|
if (!FFlag::StudioCSGChildDataNoSizeLimit && childDataBinaryStr.value().size() > PartOperation::getMaximumMeshStreamSize())
|
|
{
|
|
char buf[128];
|
|
sprintf( buf, "This union is too complex. There is a %d triangle limit for unions. Please undo the last change.", (int)RBX::PartOperation::getMaximumTriangleCount());
|
|
partOperation->setParent(NULL);
|
|
partOperation = boost::shared_ptr<RBX::PartOperation>();
|
|
operationFailed("Union Rejected", buf);
|
|
return true;
|
|
}
|
|
|
|
partOperation->setChildData(childDataBinaryStr);
|
|
|
|
if (!FFlag::CSGDelayParentingOperationToEnd)
|
|
{
|
|
dictionaryService->storeData(*partOperation);
|
|
nrDictionaryService->storeData(*partOperation);
|
|
}
|
|
|
|
dictionaryService->insertMesh(*partOperation);
|
|
|
|
//Remove Children
|
|
while(partOperation->numChildren())
|
|
partOperation->getChild(0)->setParent(NULL);
|
|
|
|
if (FFlag::CSGDelayParentingOperationToEnd)
|
|
{
|
|
RBXASSERT(partOperation->getParent() == NULL);
|
|
RBXASSERT(newOperationParent != NULL);
|
|
partOperation->setParent(newOperationParent);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool RBX::CSGOperations::doNegate(
|
|
std::vector<boost::shared_ptr<RBX::Instance> >::const_iterator start,
|
|
std::vector<boost::shared_ptr<RBX::Instance> >::const_iterator end,
|
|
std::vector<shared_ptr<RBX::Instance> >& toSelect )
|
|
{
|
|
std::vector<shared_ptr<RBX::Instance> > toRemove;
|
|
|
|
bool success = negateSelection(toRemove, toSelect, start, end);
|
|
|
|
if (!success)
|
|
{
|
|
for (std::vector<shared_ptr<RBX::Instance> >::iterator iter = toSelect.begin(); iter != toSelect.end(); ++iter)
|
|
(*iter)->setParent(NULL);
|
|
return true;
|
|
}
|
|
|
|
for (std::vector<shared_ptr<RBX::Instance> >::iterator iter = toRemove.begin(); iter != toRemove.end(); ++iter)
|
|
(*iter)->setParent(NULL);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool RBX::CSGOperations::doSeparate(
|
|
std::vector<boost::shared_ptr<RBX::Instance> >::const_iterator start,
|
|
std::vector<boost::shared_ptr<RBX::Instance> >::const_iterator end,
|
|
std::vector<shared_ptr<RBX::Instance> >& ungroupedItems )
|
|
{
|
|
// First make a copy of the selection list
|
|
Instances itemsToUngroup;
|
|
for (std::vector<boost::shared_ptr<RBX::Instance> >::const_iterator iter = start; iter != end; ++iter)
|
|
{
|
|
if (shared_ptr<PartOperation> o = RBX::Instance::fastSharedDynamicCast<PartOperation>(*iter))
|
|
itemsToUngroup.push_back(o);
|
|
}
|
|
|
|
// Now iterate over the objects to be ungrouped and ungroup them
|
|
Instances itemsToDelete;
|
|
bool didSomething = false;
|
|
for ( Instances::const_iterator iter = itemsToUngroup.begin(); iter != itemsToUngroup.end(); iter++ )
|
|
didSomething |= separate( (*iter), ungroupedItems, itemsToDelete );
|
|
|
|
if (didSomething)
|
|
{
|
|
for (Instances::const_iterator iter = itemsToDelete.begin(); iter != itemsToDelete.end(); ++iter)
|
|
(*iter)->setParent(NULL);
|
|
}
|
|
else
|
|
debugAssertM(0, "Calling seperate command without checking is-enabled");
|
|
return true;
|
|
}
|
|
|
|
bool RBX::CSGOperations::setOperationMesh(shared_ptr<PartOperation> partOperation, Instances instances)
|
|
{
|
|
bool negativeChildren = true;
|
|
|
|
for (Instances::const_iterator iter = instances.begin(); iter != instances.end(); ++iter)
|
|
{
|
|
if (!Instance::fastSharedDynamicCast<NegateOperation>(*iter))
|
|
{
|
|
negativeChildren = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Instance::fastSharedDynamicCast<UnionOperation>(partOperation) || negativeChildren)
|
|
return setUnionMesh(partOperation, instances.begin(), instances.end(), true);
|
|
else if (shared_ptr<NegateOperation> negateOperation = Instance::fastSharedDynamicCast<NegateOperation>(partOperation))
|
|
return setNegateMesh(negateOperation, *instances.begin());
|
|
|
|
return false;
|
|
}
|
|
|
|
bool RBX::CSGOperations::recalculateMesh(shared_ptr<PartOperation> partOperation)
|
|
{
|
|
std::stringstream ss(partOperation->peekChildData(mDatamodel).value());
|
|
Instances instances;
|
|
SerializerBinary::deserialize(ss, instances);
|
|
|
|
for (std::vector<boost::shared_ptr<Instance> >::const_iterator iter = instances.begin(); iter != instances.end(); ++iter)
|
|
{
|
|
if (shared_ptr<PartOperation> childOperation = Instance::fastSharedDynamicCast<PartOperation>(*iter))
|
|
{
|
|
bool opSuccess = recalculateMesh(childOperation);
|
|
|
|
if (!opSuccess)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Color3 partColor = partOperation->getColor3();
|
|
CoordinateFrame cFrame = partOperation->getCoordinateFrame();
|
|
Vector3 operationSize = partOperation->getPartSizeUi();
|
|
PartMaterial partMaterial = partOperation->getRenderMaterial();
|
|
float transparency = partOperation->getTransparencyXml();
|
|
|
|
bool opSuccess = setOperationMesh(partOperation, instances);
|
|
|
|
if (!opSuccess)
|
|
return false;
|
|
|
|
partOperation->setCoordinateFrame(cFrame);
|
|
|
|
partOperation->setPartSizeXml(operationSize);
|
|
partOperation->setColor3(partColor);
|
|
partOperation->setRenderMaterial(partMaterial);
|
|
partOperation->setTransparency(transparency);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool RBX::CSGOperations::createPartMesh(PartInstance* part, CSGMesh* modelData)
|
|
{
|
|
std::vector<Graphics::GeometryGenerator::Vertex> vertices;
|
|
std::vector<unsigned short> indices;
|
|
|
|
std::vector<CSGVertex> meshVertices;
|
|
std::vector<unsigned int> meshIndices;
|
|
|
|
{
|
|
Graphics::GeometryGenerator geomGen;
|
|
|
|
geomGen.addCSGPrimitive(part);
|
|
|
|
vertices.resize(geomGen.getVertexCount());
|
|
indices.resize(geomGen.getIndexCount());
|
|
meshVertices.resize(geomGen.getVertexCount());
|
|
meshIndices.resize(geomGen.getIndexCount());
|
|
}
|
|
|
|
if (vertices.size() == 0 || indices.size() < 3)
|
|
return false;
|
|
|
|
{
|
|
Graphics::GeometryGenerator geomGen(&vertices[0], &indices[0]);
|
|
geomGen.addCSGPrimitive(part);
|
|
}
|
|
|
|
for (size_t i = 0; i < vertices.size(); i++)
|
|
{
|
|
meshVertices[i].pos = vertices[i].pos;
|
|
meshVertices[i].uv = vertices[i].uv;
|
|
meshVertices[i].color = vertices[i].color;
|
|
meshVertices[i].uvStuds = vertices[i].uvStuds;
|
|
meshVertices[i].normal = vertices[i].normal;
|
|
meshVertices[i].tangent = vertices[i].tangent;
|
|
}
|
|
|
|
for (size_t i = 0; i < indices.size(); i++)
|
|
{
|
|
meshIndices[i] = (unsigned int)indices[i];
|
|
}
|
|
|
|
modelData->set(meshVertices, meshIndices);
|
|
modelData->weldMesh(true);
|
|
modelData->buildBRep();
|
|
|
|
return true;
|
|
}
|
|
|
|
void RBX::CSGOperations::resetInstanceCFrame(
|
|
std::vector<boost::shared_ptr<Instance> >::const_iterator start,
|
|
std::vector<boost::shared_ptr<Instance> >::const_iterator end,
|
|
boost::unordered_map<boost::shared_ptr<Instance>, CoordinateFrame> originalCFrames)
|
|
{
|
|
for (std::vector<boost::shared_ptr<Instance> >::const_iterator iter = start; iter != end; ++iter)
|
|
if (shared_ptr<PartInstance> partInstance = Instance::fastSharedDynamicCast<PartInstance>(*iter))
|
|
if (originalCFrames.count(*iter))
|
|
partInstance->setCoordinateFrame(originalCFrames.at(*iter));
|
|
}
|
|
|
|
static void warnIfChildInstance(shared_ptr<RBX::Instance> instance,
|
|
shared_ptr<RBX::PartInstance> partInstance,
|
|
const std::string& operationType)
|
|
{
|
|
if (instance->fastDynamicCast<RBX::DataModelMesh>())
|
|
return;
|
|
|
|
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_ERROR, "Warning: Child object \"%s\" of parent \"%s\" is not supported by %s.", instance->getFullName().c_str(), partInstance->getFullName().c_str(), operationType.c_str());
|
|
}
|
|
|
|
void RBX::CSGOperations::warnIfChildInstances(shared_ptr<PartInstance> partInstance, const std::string& operationType)
|
|
{
|
|
partInstance->visitDescendants(boost::bind(&warnIfChildInstance, _1, partInstance, operationType));
|
|
}
|
|
|
|
const char* RBX::CSGOperations::unionFailedMsg = "This union is not currently solvable. Try offseting the parts from each other or try unioning each part individually.";
|
|
|
|
bool RBX::CSGOperations::setUnionMesh(shared_ptr<PartOperation> finalPartOperation,
|
|
std::vector<boost::shared_ptr<Instance> >::const_iterator start,
|
|
std::vector<boost::shared_ptr<Instance> >::const_iterator end,
|
|
bool isChildRecalculation)
|
|
{
|
|
boost::scoped_ptr<CSGMesh> positiveMesh;
|
|
boost::scoped_ptr<CSGMesh> negativeMesh;
|
|
|
|
DataModel* dataModel = FFlag::CSGDelayParentingOperationToEnd ? mDatamodel : DataModel::get(finalPartOperation.get());
|
|
|
|
CSGDictionaryService* dictionaryService = ServiceProvider::create< CSGDictionaryService >(dataModel);
|
|
|
|
Instance* parentToSet = FFlag::CSGDelayParentingOperationToEnd ? NULL : (*start)->getParent();
|
|
|
|
CoordinateFrame primaryDelta;
|
|
bool primaryDeltaSet = false;
|
|
|
|
boost::unordered_map<boost::shared_ptr<Instance>, CoordinateFrame> originalCFrames;
|
|
|
|
for (std::vector<boost::shared_ptr<Instance> >::const_iterator iter = start; iter != end; ++iter)
|
|
{
|
|
if (!FFlag::CSGDelayParentingOperationToEnd)
|
|
{
|
|
if (parentToSet != (*iter)->getParent())
|
|
parentToSet = NULL;
|
|
}
|
|
|
|
if (shared_ptr<PartInstance> partInstance = Instance::fastSharedDynamicCast<PartInstance>(*iter))
|
|
{
|
|
partInstance->destroyJoints();
|
|
|
|
if (!isChildRecalculation)
|
|
{
|
|
if (!primaryDeltaSet)
|
|
{
|
|
primaryDeltaSet = true;
|
|
primaryDelta = CoordinateFrame(partInstance->getCoordinateFrame().rotation.inverse(), -partInstance->getCoordinateFrame().translation);
|
|
}
|
|
originalCFrames.emplace(*iter, partInstance->getCoordinateFrame());
|
|
partInstance->setCoordinateFrame(CoordinateFrame(primaryDelta.rotation) * (partInstance->getCoordinateFrame() + primaryDelta.translation));
|
|
}
|
|
}
|
|
|
|
boost::scoped_ptr<CSGMesh> meshData;
|
|
if (shared_ptr<PartOperation> partOperation = Instance::fastSharedDynamicCast<PartOperation>(*iter))
|
|
{
|
|
if (!partOperation->getMesh())
|
|
continue;
|
|
|
|
if (!partOperation->getMesh()->isValid())
|
|
{
|
|
partOperation->refreshMesh();
|
|
if (!partOperation->getMesh()->isValid())
|
|
{
|
|
if (!recalculateMesh(partOperation))
|
|
{
|
|
if (!isChildRecalculation)
|
|
resetInstanceCFrame(start, end, originalCFrames);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
meshData.reset(partOperation->getMesh()->clone());
|
|
|
|
meshData->applyScale(partOperation->getSizeDifference());
|
|
|
|
if (FFlag::CSGMeshColorToolsEnabled && partOperation->getUsePartColor())
|
|
meshData->applyColor(Vector3(partOperation->getColor3().r, partOperation->getColor3().g, partOperation->getColor3().b));
|
|
|
|
meshData->applyCoordinateFrame(partOperation->getCoordinateFrame());
|
|
}
|
|
|
|
if (shared_ptr<UnionOperation> unionOperation = Instance::fastSharedDynamicCast<UnionOperation>(*iter))
|
|
{
|
|
warnIfChildInstances(unionOperation, "union");
|
|
|
|
if (!positiveMesh)
|
|
{
|
|
finalPartOperation->setColor(unionOperation->getColor());
|
|
finalPartOperation->setRenderMaterial(unionOperation->getRenderMaterial());
|
|
finalPartOperation->setReflectance(unionOperation->getReflectance());
|
|
finalPartOperation->setTransparency(unionOperation->getTransparencyUi());
|
|
finalPartOperation->setAnchored(unionOperation->getAnchored());
|
|
finalPartOperation->setCanCollide(unionOperation->getCanCollide());
|
|
finalPartOperation->setElasticity(unionOperation->getElasticity());
|
|
finalPartOperation->setFriction(unionOperation->getFriction());
|
|
if (DFFlag::MaterialPropertiesEnabled)
|
|
{
|
|
finalPartOperation->setPhysicalProperties(unionOperation->getPhysicalProperties());
|
|
}
|
|
positiveMesh.swap(meshData);
|
|
}
|
|
else
|
|
{
|
|
if (!positiveMesh->unionMesh(positiveMesh.get(), meshData.get()))
|
|
{
|
|
if (!isChildRecalculation)
|
|
resetInstanceCFrame(start, end, originalCFrames);
|
|
finalPartOperation->setParent(NULL);
|
|
operationFailed("Union Unsolvable -1", unionFailedMsg);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else if (shared_ptr<NegateOperation> negateOperation = Instance::fastSharedDynamicCast<NegateOperation>(*iter))
|
|
{
|
|
warnIfChildInstances(negateOperation, "union");
|
|
|
|
if (!negativeMesh)
|
|
{
|
|
negativeMesh.swap(meshData);
|
|
}
|
|
else
|
|
{
|
|
if (!negativeMesh->unionMesh(negativeMesh.get(), meshData.get()))
|
|
{
|
|
if (!isChildRecalculation)
|
|
resetInstanceCFrame(start, end, originalCFrames);
|
|
finalPartOperation->setParent(NULL);
|
|
operationFailed("Union Unsolvable -2", unionFailedMsg);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else if (shared_ptr<PartInstance> partInstance = Instance::fastSharedDynamicCast<PartInstance>(*iter))
|
|
{
|
|
warnIfChildInstances(partInstance, "union");
|
|
if (!positiveMesh)
|
|
{
|
|
finalPartOperation->setColor(partInstance->getColor());
|
|
finalPartOperation->setRenderMaterial(partInstance->getRenderMaterial());
|
|
finalPartOperation->setReflectance(partInstance->getReflectance());
|
|
finalPartOperation->setTransparency(partInstance->getTransparencyUi());
|
|
finalPartOperation->setAnchored(partInstance->getAnchored());
|
|
finalPartOperation->setCanCollide(partInstance->getCanCollide());
|
|
finalPartOperation->setElasticity(partInstance->getElasticity());
|
|
finalPartOperation->setFriction(partInstance->getFriction());
|
|
if (DFFlag::MaterialPropertiesEnabled)
|
|
{
|
|
finalPartOperation->setPhysicalProperties(partInstance->getPhysicalProperties());
|
|
}
|
|
|
|
positiveMesh.reset(CSGMeshFactory::singleton()->createMesh());
|
|
|
|
bool partMeshCreated = createPartMesh(partInstance.get(), positiveMesh.get());
|
|
|
|
if (partMeshCreated)
|
|
{
|
|
positiveMesh->applyCoordinateFrame(partInstance->getCoordinateFrame());
|
|
}
|
|
else
|
|
{
|
|
if (!isChildRecalculation)
|
|
resetInstanceCFrame(start, end, originalCFrames);
|
|
finalPartOperation->setParent(NULL);
|
|
operationFailed("Warning", "This part or mesh type is not supported.");
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
boost::scoped_ptr<CSGMesh> model(CSGMeshFactory::singleton()->createMesh());
|
|
bool partMeshCreated = createPartMesh(partInstance.get(), model.get());
|
|
if (partMeshCreated)
|
|
{
|
|
model->applyCoordinateFrame(partInstance->getCoordinateFrame());
|
|
if (!positiveMesh->unionMesh(positiveMesh.get(), model.get()))
|
|
{
|
|
if (!isChildRecalculation)
|
|
resetInstanceCFrame(start, end, originalCFrames);
|
|
finalPartOperation->setParent(NULL);
|
|
operationFailed("Union Unsolvable -3", unionFailedMsg);
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!isChildRecalculation)
|
|
resetInstanceCFrame(start, end, originalCFrames);
|
|
finalPartOperation->setParent(NULL);
|
|
operationFailed("Warning", "This part or mesh type is not supported.");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!isChildRecalculation)
|
|
resetInstanceCFrame(start, end, originalCFrames);
|
|
finalPartOperation->setParent(NULL);
|
|
operationFailed("Warning", "Only parts, union parts, and negative parts are supported by union.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!positiveMesh && !negativeMesh)
|
|
{
|
|
if (!isChildRecalculation)
|
|
resetInstanceCFrame(start, end, originalCFrames);
|
|
finalPartOperation->setParent(NULL);
|
|
operationFailed("Union Unsolvable -6", unionFailedMsg);
|
|
return false;
|
|
}
|
|
|
|
if (negativeMesh)
|
|
{
|
|
if (!positiveMesh)
|
|
{
|
|
positiveMesh.swap(negativeMesh);
|
|
negativeMesh.reset();
|
|
}
|
|
else if (!positiveMesh->subractMesh(positiveMesh.get(), negativeMesh.get()))
|
|
{
|
|
if (!isChildRecalculation)
|
|
resetInstanceCFrame(start, end, originalCFrames);
|
|
finalPartOperation->setParent(NULL);
|
|
operationFailed("Union Unsolvable -7", unionFailedMsg);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!positiveMesh->isValid())
|
|
{
|
|
if (!isChildRecalculation)
|
|
resetInstanceCFrame(start, end, originalCFrames);
|
|
finalPartOperation->setParent(NULL);
|
|
operationFailed("Union Unsolvable -8", unionFailedMsg);
|
|
return false;
|
|
}
|
|
if ( !positiveMesh->newTriangulate() )
|
|
{
|
|
if (FFlag::CSGNewTriangulateFallBack)
|
|
positiveMesh->triangulate();
|
|
else
|
|
{
|
|
if (!isChildRecalculation)
|
|
resetInstanceCFrame(start, end, originalCFrames);
|
|
operationFailed("Union Unsolvable -9", unionFailedMsg);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
size_t numTriangles = positiveMesh->getIndices().size() / 3;
|
|
|
|
if (numTriangles > PartOperation::getMaximumTriangleCount())
|
|
{
|
|
char buf[128];
|
|
sprintf( buf, "This union would be %d triangles. There is a %d triangle limit for unions.", (int)numTriangles, (int)PartOperation::getMaximumTriangleCount());
|
|
if (!isChildRecalculation)
|
|
resetInstanceCFrame(start, end, originalCFrames);
|
|
finalPartOperation->setParent(NULL);
|
|
operationFailed("Union Rejected", buf);
|
|
return false;
|
|
}
|
|
|
|
if (positiveMesh->toBinaryString().size() > PartOperation::getMaximumMeshStreamSize())
|
|
{
|
|
char buf[128];
|
|
sprintf( buf, "This union is too complex. There is a %d triangle limit for unions.", (int)PartOperation::getMaximumTriangleCount());
|
|
if (!isChildRecalculation)
|
|
resetInstanceCFrame(start, end, originalCFrames);
|
|
finalPartOperation->setParent(NULL);
|
|
operationFailed("Union Rejected", buf);
|
|
return false;
|
|
}
|
|
|
|
G3D::Vector3 trans = positiveMesh->extentsCenter();
|
|
finalPartOperation->setCoordinateFrame(trans);
|
|
|
|
for (std::vector<boost::shared_ptr<Instance> >::const_iterator iter = start; iter != end; ++iter)
|
|
{
|
|
if (boost::shared_ptr<PartInstance> partInstance = Instance::fastSharedDynamicCast<PartInstance>(*iter))
|
|
{
|
|
CoordinateFrame tmpFrame = partInstance->getCoordinateFrame();
|
|
tmpFrame.translation = tmpFrame.translation - trans;
|
|
partInstance->setCoordinateFrame(tmpFrame);
|
|
}
|
|
}
|
|
if (!FFlag::CSGDelayParentingOperationToEnd && dataModel)
|
|
finalPartOperation->setParent(parentToSet != NULL ? parentToSet : dataModel->getWorkspace());
|
|
finalPartOperation->setPartSizeXml(positiveMesh->extentsSize());
|
|
finalPartOperation->setInitialSize(finalPartOperation->getPartSizeUi());
|
|
|
|
G3D::Vector3 negTrans = -trans;
|
|
positiveMesh->applyTranslation(negTrans);
|
|
positiveMesh->translate(negTrans);
|
|
|
|
dictionaryService->retrieveMeshData(*finalPartOperation);
|
|
finalPartOperation->setMesh(positiveMesh->clone());
|
|
|
|
if (!finalPartOperation->checkDecompExists() && !finalPartOperation->createPhysicsData(positiveMesh.get()))
|
|
{
|
|
if (!isChildRecalculation)
|
|
resetInstanceCFrame(start, end, originalCFrames);
|
|
finalPartOperation->setParent(NULL);
|
|
operationFailed("Union Rejected", "This union's Physics Data is too complex, simplify the model and try again");
|
|
return false;
|
|
}
|
|
|
|
if (!isChildRecalculation)
|
|
{
|
|
//Move back to original position
|
|
CoordinateFrame identityFrameDelta = CoordinateFrame(-finalPartOperation->getCoordinateFrame().translation);
|
|
identityFrameDelta = CoordinateFrame(primaryDelta.rotation).inverse() * identityFrameDelta;
|
|
|
|
finalPartOperation->setCoordinateFrame(CoordinateFrame(primaryDelta.rotation.inverse(), G3D::Vector3::zero()));
|
|
finalPartOperation->setCoordinateFrame(finalPartOperation->getCoordinateFrame() - identityFrameDelta.translation - primaryDelta.translation);
|
|
}
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
bool RBX::CSGOperations::setNegateMesh(shared_ptr<NegateOperation> negateOperation, shared_ptr<Instance> instance)
|
|
{
|
|
boost::scoped_ptr<CSGMesh> model;
|
|
|
|
CSGDictionaryService* dictionaryService = FFlag::CSGDelayParentingOperationToEnd ?
|
|
mDatamodel->create<CSGDictionaryService>() :
|
|
ServiceProvider::create< CSGDictionaryService >(DataModel::get(negateOperation.get()));
|
|
NonReplicatedCSGDictionaryService* nrDictionaryService = FFlag::CSGDelayParentingOperationToEnd ?
|
|
mDatamodel->create<NonReplicatedCSGDictionaryService>() : ServiceProvider::create<NonReplicatedCSGDictionaryService>(DataModel::get(negateOperation.get()));
|
|
|
|
if (shared_ptr<PartInstance> partInstance = Instance::fastSharedDynamicCast<PartInstance>(instance))
|
|
partInstance->destroyJoints();
|
|
|
|
if (shared_ptr<UnionOperation> unionOperation = Instance::fastSharedDynamicCast<UnionOperation>(instance))
|
|
{
|
|
warnIfChildInstances(unionOperation, "negation");
|
|
|
|
dictionaryService->retrieveData(*unionOperation);
|
|
nrDictionaryService->retrieveData(*unionOperation);
|
|
|
|
if (!unionOperation->getMesh() || !unionOperation->getMesh()->isValid())
|
|
{
|
|
if (!recalculateMesh(unionOperation))
|
|
return false;
|
|
}
|
|
|
|
model.reset(unionOperation->getMesh()->clone());
|
|
|
|
model->applyScale(unionOperation->getSizeDifference());
|
|
negateOperation->setInitialSize(unionOperation->getPartSizeUi());
|
|
|
|
negateOperation->setCoordinateFrame(unionOperation->getCoordinateFrame());
|
|
negateOperation->setPartSizeXml(unionOperation->getPartSizeUi());
|
|
}
|
|
else if (shared_ptr<PartInstance> partInstance = Instance::fastSharedDynamicCast<PartInstance>(instance))
|
|
{
|
|
warnIfChildInstances(partInstance, "negation");
|
|
|
|
negateOperation->setCoordinateFrame(partInstance->getCoordinateFrame());
|
|
negateOperation->setPartSizeXml(partInstance->getPartSizeUi());
|
|
negateOperation->setInitialSize(negateOperation->getPartSizeUi());
|
|
|
|
partInstance->getColor3();
|
|
|
|
model.reset(CSGMeshFactory::singleton()->createMesh());
|
|
|
|
bool partMeshCreated = createPartMesh(partInstance.get(), model.get());
|
|
|
|
if (!partMeshCreated)
|
|
{
|
|
operationFailed("Warning", "This part or mesh type is not supported.");
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
operationFailed("Warning", "Only parts, union parts, and negative parts are supported for Negation.");
|
|
return false;
|
|
}
|
|
|
|
if (!model || !model->isValid())
|
|
{
|
|
operationFailed("Warning", "Could not create model.");
|
|
return false;
|
|
}
|
|
|
|
if (boost::shared_ptr<PartInstance> partInstance = Instance::fastSharedDynamicCast<PartInstance>(instance))
|
|
{
|
|
CoordinateFrame tmpFrame = partInstance->getCoordinateFrame();
|
|
tmpFrame.translation = tmpFrame.translation - negateOperation->getCoordinateFrame().translation;
|
|
partInstance->setCoordinateFrame(tmpFrame);
|
|
}
|
|
|
|
|
|
if ( !model->newTriangulate() )
|
|
{
|
|
if (FFlag::CSGNewTriangulateFallBack)
|
|
model->triangulate();
|
|
else
|
|
{
|
|
operationFailed("Union Unsolvable -10", unionFailedMsg);
|
|
return false;
|
|
}
|
|
}
|
|
negateOperation->setMesh(model->clone());
|
|
// Temporary, Remove and fix after Merging with CO
|
|
CSGMeshSgCore meshData = *static_cast<CSGMeshSgCore*>(negateOperation->getMesh().get());
|
|
if (!negateOperation->checkDecompExists())
|
|
negateOperation->createPhysicsData(&meshData);
|
|
else
|
|
negateOperation->createPlaceholderPhysicsData();
|
|
return true;
|
|
}
|
|
|
|
bool RBX::CSGOperations::negateSelection(Instances& toRemove,
|
|
Instances& toSelect,
|
|
std::vector<boost::shared_ptr<Instance> >::const_iterator start,
|
|
std::vector<boost::shared_ptr<Instance> >::const_iterator end)
|
|
{
|
|
CSGDictionaryService* dictionaryService = ServiceProvider::create< CSGDictionaryService >(mDatamodel);
|
|
NonReplicatedCSGDictionaryService* nrDictionaryService = ServiceProvider::create<NonReplicatedCSGDictionaryService>(mDatamodel);
|
|
|
|
for (std::vector<boost::shared_ptr<Instance> >::const_iterator iter = start; iter != end; ++iter)
|
|
{
|
|
if (!(Instance::fastSharedDynamicCast<PartOperation>(*iter) ||
|
|
Instance::fastSharedDynamicCast<NegateOperation>(*iter) ||
|
|
Instance::fastSharedDynamicCast<PartInstance>(*iter)))
|
|
|
|
{
|
|
operationFailed("Warning", "Only parts, union parts, and negative parts are supported by negation.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
for (std::vector<boost::shared_ptr<Instance> >::const_iterator iter = start; iter != end; ++iter)
|
|
{
|
|
if (shared_ptr<NegateOperation> negateOperation = Instance::fastSharedDynamicCast<NegateOperation>(*iter))
|
|
{
|
|
dictionaryService->retrieveData(*negateOperation);
|
|
nrDictionaryService->retrieveData(*negateOperation);
|
|
Instances instances;
|
|
std::stringstream ss(negateOperation->getChildDataBlocking().value());
|
|
SerializerBinary::deserialize(ss, instances);
|
|
|
|
Instances separatedItems;
|
|
Instances itemsToDelete;
|
|
bool didSomething = separate( negateOperation, separatedItems, itemsToDelete );
|
|
|
|
if (didSomething)
|
|
{
|
|
toSelect.insert(toSelect.end(), separatedItems.begin(), separatedItems.end());
|
|
toRemove.insert(toRemove.end(), itemsToDelete.begin(), itemsToDelete.end());
|
|
}
|
|
|
|
toRemove.push_back(negateOperation);
|
|
}
|
|
else if (shared_ptr<PartInstance> pInst = Instance::fastSharedDynamicCast<PartInstance>(*iter))
|
|
{
|
|
shared_ptr<NegateOperation> negateOperation;
|
|
try
|
|
{
|
|
negateOperation = NegateOperation::createInstance();
|
|
if (!FFlag::CSGDelayParentingOperationToEnd)
|
|
negateOperation->setParent(pInst->getParent());
|
|
toSelect.push_back(negateOperation);
|
|
}
|
|
catch (std::exception &)
|
|
{
|
|
operationFailed("Error", "Could not create part instance.");
|
|
return false;
|
|
}
|
|
|
|
if (shared_ptr<PartOperation> pOperation = Instance::fastSharedDynamicCast<PartOperation>(pInst))
|
|
{
|
|
dictionaryService->retrieveData(*pOperation);
|
|
nrDictionaryService->retrieveData(*pOperation);
|
|
|
|
pOperation->setMeshData(BinaryString(""));
|
|
pOperation->setPhysicsData(BinaryString(""));
|
|
|
|
negateOperation->setUsePartColor(pOperation->getUsePartColor());
|
|
negateOperation->setColor(pOperation->getColor());
|
|
}
|
|
|
|
bool validNegation = setNegateMesh(negateOperation, pInst);
|
|
|
|
if (!validNegation)
|
|
return false;
|
|
|
|
shared_ptr<ModelInstance> groupInstance = ModelInstance::createInstance();
|
|
shared_ptr<Instance> dupe = pInst->clone(SerializationCreator);
|
|
dupe->setParent(groupInstance.get());
|
|
|
|
std::stringstream ss;
|
|
SerializerBinary::serialize(ss, groupInstance.get());
|
|
negateOperation->setChildData(BinaryString(ss.str()));
|
|
groupInstance->setParent(NULL);
|
|
|
|
if (FFlag::CSGDelayParentingOperationToEnd)
|
|
{
|
|
negateOperation->setParent(pInst->getParent());
|
|
}
|
|
else
|
|
{
|
|
dictionaryService->storeData(*negateOperation);
|
|
nrDictionaryService->storeData(*negateOperation);
|
|
}
|
|
|
|
toRemove.push_back(pInst);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool RBX::CSGOperations::separate(const shared_ptr<Instance>& wInstance,
|
|
Instances &separatedItems,
|
|
Instances &itemsToDelete)
|
|
{
|
|
CSGDictionaryService* dictionaryService = ServiceProvider::create<CSGDictionaryService>(DataModel::get(wInstance.get()));
|
|
NonReplicatedCSGDictionaryService* nrDictionaryService = ServiceProvider::create<NonReplicatedCSGDictionaryService>(DataModel::get(wInstance.get()));
|
|
|
|
bool didSomethingHere = false;
|
|
if (!dictionaryService || !nrDictionaryService)
|
|
return didSomethingHere;
|
|
|
|
if (shared_ptr<PartOperation> partOperation = Instance::fastSharedDynamicCast<PartOperation>(wInstance))
|
|
{
|
|
bool isNegate = false;
|
|
|
|
if (Instance::fastSharedDynamicCast<NegateOperation>(partOperation))
|
|
isNegate = true;
|
|
|
|
Instances instances;
|
|
dictionaryService->retrieveData(*partOperation);
|
|
nrDictionaryService->retrieveData(*partOperation);
|
|
std::stringstream ss(partOperation->getChildDataBlocking().value());
|
|
SerializerBinary::deserialize(ss, instances);
|
|
|
|
for (std::vector<boost::shared_ptr<Instance> >::const_iterator iter = instances.begin(); iter != instances.end(); ++iter)
|
|
{
|
|
if (shared_ptr<PartOperation> partOperationChild = Instance::fastSharedDynamicCast<PartOperation>(*iter))
|
|
if (!recalculateMesh(partOperationChild))
|
|
return didSomethingHere;
|
|
|
|
bool childNegate = false;
|
|
|
|
if (Instance::fastSharedDynamicCast<NegateOperation>(*iter))
|
|
childNegate = true;
|
|
|
|
if (shared_ptr<PartInstance> part = Instance::fastSharedDynamicCast<PartInstance>(*iter))
|
|
{
|
|
CoordinateFrame newFrame = part->getCoordinateFrame();
|
|
|
|
if (!isNegate || childNegate)
|
|
newFrame.rotation = partOperation->getCoordinateFrame().rotation * newFrame.rotation;
|
|
else
|
|
newFrame.rotation = partOperation->getCoordinateFrame().rotation;
|
|
|
|
newFrame.translation *= partOperation->getSizeDifference();
|
|
newFrame.translation = (partOperation->getCoordinateFrame().rotation * newFrame.translation) + partOperation->getCoordinateFrame().translation;
|
|
|
|
part->setCoordinateFrame(newFrame);
|
|
part->setPartSizeXml(part->getPartSizeUi() * partOperation->getSizeDifference());
|
|
separatedItems.push_back(part);
|
|
}
|
|
|
|
(*iter)->setParent(wInstance->getParent());
|
|
}
|
|
|
|
itemsToDelete.push_back(partOperation);
|
|
|
|
didSomethingHere = true;
|
|
}
|
|
return didSomethingHere;
|
|
}
|
|
|
|
RBX::Instance* RBX::CSGOperations::getCommonParentOrWorkspace(Instances::const_iterator start,
|
|
Instances::const_iterator end)
|
|
{
|
|
Instance* parentMatch = (*start)->getParent();
|
|
for (Instances::const_iterator itr = start; itr != end; ++itr)
|
|
{
|
|
if (parentMatch != (*itr)->getParent())
|
|
{
|
|
parentMatch = mDatamodel->getWorkspace();
|
|
break;
|
|
}
|
|
}
|
|
return parentMatch;
|
|
}
|