/* 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 >::const_iterator start, std::vector >::const_iterator end, boost::shared_ptr& partOperation ) { bool allNeg = true; for (Instances::const_iterator iter = start; iter != end; ++iter) { if (!RBX::Instance::fastSharedDynamicCast(*iter)) { allNeg = false; break; } } //shared_ptr partOperation; try { if (allNeg) partOperation = Creatable::create(); else partOperation = Creatable::create(); 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(); return false; } CSGDictionaryService* dictionaryService = ServiceProvider::create< CSGDictionaryService >(mDatamodel); NonReplicatedCSGDictionaryService* nrDictionaryService = ServiceProvider::create(mDatamodel); if (FFlag::CSGDelayParentingOperationToEnd) { Instances selectionCopy(start, end); for (auto iter : selectionCopy) { iter->setParent(partOperation.get()); if (shared_ptr partOp = RBX::Instance::fastSharedDynamicCast(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 partOp = RBX::Instance::fastSharedDynamicCast(*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(); 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 >::const_iterator start, std::vector >::const_iterator end, std::vector >& toSelect ) { std::vector > toRemove; bool success = negateSelection(toRemove, toSelect, start, end); if (!success) { for (std::vector >::iterator iter = toSelect.begin(); iter != toSelect.end(); ++iter) (*iter)->setParent(NULL); return true; } for (std::vector >::iterator iter = toRemove.begin(); iter != toRemove.end(); ++iter) (*iter)->setParent(NULL); return true; } bool RBX::CSGOperations::doSeparate( std::vector >::const_iterator start, std::vector >::const_iterator end, std::vector >& ungroupedItems ) { // First make a copy of the selection list Instances itemsToUngroup; for (std::vector >::const_iterator iter = start; iter != end; ++iter) { if (shared_ptr o = RBX::Instance::fastSharedDynamicCast(*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, Instances instances) { bool negativeChildren = true; for (Instances::const_iterator iter = instances.begin(); iter != instances.end(); ++iter) { if (!Instance::fastSharedDynamicCast(*iter)) { negativeChildren = false; break; } } if (Instance::fastSharedDynamicCast(partOperation) || negativeChildren) return setUnionMesh(partOperation, instances.begin(), instances.end(), true); else if (shared_ptr negateOperation = Instance::fastSharedDynamicCast(partOperation)) return setNegateMesh(negateOperation, *instances.begin()); return false; } bool RBX::CSGOperations::recalculateMesh(shared_ptr partOperation) { std::stringstream ss(partOperation->peekChildData(mDatamodel).value()); Instances instances; SerializerBinary::deserialize(ss, instances); for (std::vector >::const_iterator iter = instances.begin(); iter != instances.end(); ++iter) { if (shared_ptr childOperation = Instance::fastSharedDynamicCast(*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 vertices; std::vector indices; std::vector meshVertices; std::vector 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 >::const_iterator start, std::vector >::const_iterator end, boost::unordered_map, CoordinateFrame> originalCFrames) { for (std::vector >::const_iterator iter = start; iter != end; ++iter) if (shared_ptr partInstance = Instance::fastSharedDynamicCast(*iter)) if (originalCFrames.count(*iter)) partInstance->setCoordinateFrame(originalCFrames.at(*iter)); } static void warnIfChildInstance(shared_ptr instance, shared_ptr partInstance, const std::string& operationType) { if (instance->fastDynamicCast()) 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, 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 finalPartOperation, std::vector >::const_iterator start, std::vector >::const_iterator end, bool isChildRecalculation) { boost::scoped_ptr positiveMesh; boost::scoped_ptr 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, CoordinateFrame> originalCFrames; for (std::vector >::const_iterator iter = start; iter != end; ++iter) { if (!FFlag::CSGDelayParentingOperationToEnd) { if (parentToSet != (*iter)->getParent()) parentToSet = NULL; } if (shared_ptr partInstance = Instance::fastSharedDynamicCast(*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 meshData; if (shared_ptr partOperation = Instance::fastSharedDynamicCast(*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 = Instance::fastSharedDynamicCast(*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 = Instance::fastSharedDynamicCast(*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 = Instance::fastSharedDynamicCast(*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 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 >::const_iterator iter = start; iter != end; ++iter) { if (boost::shared_ptr partInstance = Instance::fastSharedDynamicCast(*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, shared_ptr instance) { boost::scoped_ptr model; CSGDictionaryService* dictionaryService = FFlag::CSGDelayParentingOperationToEnd ? mDatamodel->create() : ServiceProvider::create< CSGDictionaryService >(DataModel::get(negateOperation.get())); NonReplicatedCSGDictionaryService* nrDictionaryService = FFlag::CSGDelayParentingOperationToEnd ? mDatamodel->create() : ServiceProvider::create(DataModel::get(negateOperation.get())); if (shared_ptr partInstance = Instance::fastSharedDynamicCast(instance)) partInstance->destroyJoints(); if (shared_ptr unionOperation = Instance::fastSharedDynamicCast(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 = Instance::fastSharedDynamicCast(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 = Instance::fastSharedDynamicCast(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(negateOperation->getMesh().get()); if (!negateOperation->checkDecompExists()) negateOperation->createPhysicsData(&meshData); else negateOperation->createPlaceholderPhysicsData(); return true; } bool RBX::CSGOperations::negateSelection(Instances& toRemove, Instances& toSelect, std::vector >::const_iterator start, std::vector >::const_iterator end) { CSGDictionaryService* dictionaryService = ServiceProvider::create< CSGDictionaryService >(mDatamodel); NonReplicatedCSGDictionaryService* nrDictionaryService = ServiceProvider::create(mDatamodel); for (std::vector >::const_iterator iter = start; iter != end; ++iter) { if (!(Instance::fastSharedDynamicCast(*iter) || Instance::fastSharedDynamicCast(*iter) || Instance::fastSharedDynamicCast(*iter))) { operationFailed("Warning", "Only parts, union parts, and negative parts are supported by negation."); return false; } } for (std::vector >::const_iterator iter = start; iter != end; ++iter) { if (shared_ptr negateOperation = Instance::fastSharedDynamicCast(*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 pInst = Instance::fastSharedDynamicCast(*iter)) { shared_ptr 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 pOperation = Instance::fastSharedDynamicCast(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 groupInstance = ModelInstance::createInstance(); shared_ptr 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& wInstance, Instances &separatedItems, Instances &itemsToDelete) { CSGDictionaryService* dictionaryService = ServiceProvider::create(DataModel::get(wInstance.get())); NonReplicatedCSGDictionaryService* nrDictionaryService = ServiceProvider::create(DataModel::get(wInstance.get())); bool didSomethingHere = false; if (!dictionaryService || !nrDictionaryService) return didSomethingHere; if (shared_ptr partOperation = Instance::fastSharedDynamicCast(wInstance)) { bool isNegate = false; if (Instance::fastSharedDynamicCast(partOperation)) isNegate = true; Instances instances; dictionaryService->retrieveData(*partOperation); nrDictionaryService->retrieveData(*partOperation); std::stringstream ss(partOperation->getChildDataBlocking().value()); SerializerBinary::deserialize(ss, instances); for (std::vector >::const_iterator iter = instances.begin(); iter != instances.end(); ++iter) { if (shared_ptr partOperationChild = Instance::fastSharedDynamicCast(*iter)) if (!recalculateMesh(partOperationChild)) return didSomethingHere; bool childNegate = false; if (Instance::fastSharedDynamicCast(*iter)) childNegate = true; if (shared_ptr part = Instance::fastSharedDynamicCast(*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; }