diff --git a/Engine/source/T3D/convexShape.h b/Engine/source/T3D/convexShape.h index 413877a36..62b38f354 100644 --- a/Engine/source/T3D/convexShape.h +++ b/Engine/source/T3D/convexShape.h @@ -201,6 +201,8 @@ public: /// @} + String getMaterialName() { return mMaterialName; } + protected: void _updateMaterial(); diff --git a/Engine/source/gui/worldEditor/guiConvexShapeEditorCtrl.cpp b/Engine/source/gui/worldEditor/guiConvexShapeEditorCtrl.cpp index 02c2138e3..ecae9f76d 100644 --- a/Engine/source/gui/worldEditor/guiConvexShapeEditorCtrl.cpp +++ b/Engine/source/gui/worldEditor/guiConvexShapeEditorCtrl.cpp @@ -46,6 +46,10 @@ #include "core/volume.h" #include "gui/worldEditor/worldEditor.h" #include "T3D/prefab.h" +#include "T3D/trigger.h" +#include "T3D/zone.h" +#include "T3D/portal.h" +#include "math/mPolyhedron.impl.h" IMPLEMENT_CONOBJECT( GuiConvexEditorCtrl ); @@ -161,6 +165,35 @@ void GuiConvexEditorCtrl::setVisible( bool val ) mGizmoProfile->flags = mSavedGizmoFlags; mSavedGizmoFlags = -1; } + + SimGroup* misGroup; + if (Sim::findObject("MissionGroup", misGroup)) + { + //Make our proxy objects "real" again + for (U32 i = 0; i < mProxyObjects.size(); ++i) + { + if (!mProxyObjects[i].shapeProxy || !mProxyObjects[i].targetObject) + continue; + + AbstractClassRep* classRep = AbstractClassRep::findClassRep(mProxyObjects[i].targetObjectClass); + if (!classRep) + { + Con::errorf("WorldEditor::createPolyhedralObject - No such class: %s", mProxyObjects[i].targetObjectClass); + continue; + } + + SceneObject* polyObj = createPolyhedralObject(mProxyObjects[i].targetObjectClass.c_str(), mProxyObjects[i].shapeProxy); + + misGroup->addObject(polyObj); + + //Now, remove the convex proxy + mProxyObjects[i].shapeProxy->deleteObject(); + mProxyObjects[i].targetObject->deleteObject(); + mProxyObjects.erase(i); + --i; + } + + } } else { @@ -188,6 +221,60 @@ void GuiConvexEditorCtrl::setVisible( bool val ) } updateGizmoPos(); mSavedGizmoFlags = mGizmoProfile->flags; + + SimGroup* misGroup; + if (Sim::findObject("MissionGroup", misGroup)) + { + for (U32 c = 0; c < misGroup->size(); ++c) + { + bool isTrigger = (misGroup->at(c)->getClassName() == StringTable->insert("Trigger")); + bool isZone = (misGroup->at(c)->getClassName() == StringTable->insert("Zone")); + bool isPortal = (misGroup->at(c)->getClassName() == StringTable->insert("Portal")); + bool isOccluder = (misGroup->at(c)->getClassName() == StringTable->insert("OcclusionVolume")); + + if (isZone || isPortal || isOccluder) + { + SceneObject* sceneObj = static_cast(misGroup->at(c)); + if (!sceneObj) + { + Con::errorf("WorldEditor::createConvexShapeFrom - Invalid object"); + continue; + } + + ConvexShape* proxyShape = createConvexShapeFrom(sceneObj); + + //Set the texture to a representatory one so we know what's what + if (isTrigger) + proxyShape->mMaterialName = "TriggerProxyMaterial"; + else if (isPortal) + proxyShape->mMaterialName = "PortalProxyMaterial"; + else if (isZone) + proxyShape->mMaterialName = "ZoneProxyMaterial"; + else if (isOccluder) + proxyShape->mMaterialName = "OccluderProxyMaterial"; + + proxyShape->_updateMaterial(); + + sceneObj->setHidden(true); + + //set up the proxy object + ConvexShapeProxy newProxy; + newProxy.shapeProxy = proxyShape; + newProxy.targetObject = sceneObj; + + if (isTrigger) + newProxy.targetObjectClass = "Trigger"; + else if (isPortal) + newProxy.targetObjectClass = "Portal"; + else if (isZone) + newProxy.targetObjectClass = "Zone"; + else + newProxy.targetObjectClass = "OcclusionVolume"; + + mProxyObjects.push_back(newProxy); + } + } + } } } @@ -438,6 +525,8 @@ void GuiConvexEditorCtrl::on3DMouseDragged(const Gui3DMouseEvent & event) setupShape( newShape ); + newShape->setField("material", mConvexSEL->getMaterialName()); + submitUndo( CreateShape, newShape ); setSelection( newShape, -1 ); @@ -2033,7 +2122,8 @@ ConvexShape* ConvexEditorCreateTool::extrudeShapeFromFace( ConvexShape *inShape, surf.mulL( worldToShape ); } - newShape->setField( "material", Parent::mEditor->mMaterialName ); + //newShape->setField( "material", Parent::mEditor->mMaterialName ); + newShape->setField("material", inShape->getMaterialName()); newShape->registerObject(); mEditor->updateShape( newShape ); @@ -2179,6 +2269,211 @@ void GuiConvexEditorCtrl::splitSelectedFace() updateGizmoPos(); } +SceneObject* GuiConvexEditorCtrl::createPolyhedralObject(const char* className, SceneObject* geometryProvider) +{ + if (!geometryProvider) + { + Con::errorf("WorldEditor::createPolyhedralObject - Invalid geometry provider!"); + return NULL; + } + + if (!className || !className[0]) + { + Con::errorf("WorldEditor::createPolyhedralObject - Invalid class name"); + return NULL; + } + + AbstractClassRep* classRep = AbstractClassRep::findClassRep(className); + if (!classRep) + { + Con::errorf("WorldEditor::createPolyhedralObject - No such class: %s", className); + return NULL; + } + + // We don't want the extracted poly list to be affected by the object's + // current transform and scale so temporarily reset them. + + MatrixF savedTransform = geometryProvider->getTransform(); + Point3F savedScale = geometryProvider->getScale(); + + geometryProvider->setTransform(MatrixF::Identity); + geometryProvider->setScale(Point3F(1.f, 1.f, 1.f)); + + // Extract the geometry. Use the object-space bounding volumes + // as we have moved the object to the origin for the moment. + + OptimizedPolyList polyList; + if (!geometryProvider->buildPolyList(PLC_Export, &polyList, geometryProvider->getObjBox(), geometryProvider->getObjBox().getBoundingSphere())) + { + Con::errorf("WorldEditor::createPolyhedralObject - Failed to extract geometry!"); + return NULL; + } + + // Restore the object's original transform. + + geometryProvider->setTransform(savedTransform); + geometryProvider->setScale(savedScale); + + // Create the object. + + SceneObject* object = dynamic_cast< SceneObject* >(classRep->create()); + if (!Object) + { + Con::errorf("WorldEditor::createPolyhedralObject - Could not create SceneObject with class '%s'", className); + return NULL; + } + + // Convert the polylist to a polyhedron. + + Polyhedron polyhedron = polyList.toPolyhedron(); + + // Add the vertex data. + + const U32 numPoints = polyhedron.getNumPoints(); + const Point3F* points = polyhedron.getPoints(); + + for (U32 i = 0; i < numPoints; ++i) + { + static StringTableEntry sPoint = StringTable->insert("point"); + object->setDataField(sPoint, NULL, EngineMarshallData(points[i])); + } + + // Add the plane data. + + const U32 numPlanes = polyhedron.getNumPlanes(); + const PlaneF* planes = polyhedron.getPlanes(); + + for (U32 i = 0; i < numPlanes; ++i) + { + static StringTableEntry sPlane = StringTable->insert("plane"); + const PlaneF& plane = planes[i]; + + char buffer[1024]; + dSprintf(buffer, sizeof(buffer), "%g %g %g %g", plane.x, plane.y, plane.z, plane.d); + + object->setDataField(sPlane, NULL, buffer); + } + + // Add the edge data. + + const U32 numEdges = polyhedron.getNumEdges(); + const Polyhedron::Edge* edges = polyhedron.getEdges(); + + for (U32 i = 0; i < numEdges; ++i) + { + static StringTableEntry sEdge = StringTable->insert("edge"); + const Polyhedron::Edge& edge = edges[i]; + + char buffer[1024]; + dSprintf(buffer, sizeof(buffer), "%i %i %i %i ", + edge.face[0], edge.face[1], + edge.vertex[0], edge.vertex[1] + ); + + object->setDataField(sEdge, NULL, buffer); + } + + // Set the transform. + + object->setTransform(savedTransform); + object->setScale(savedScale); + + // Register and return the object. + + if (!object->registerObject()) + { + Con::errorf("WorldEditor::createPolyhedralObject - Failed to register object!"); + delete object; + return NULL; + } + + return object; +} + +ConvexShape* GuiConvexEditorCtrl::createConvexShapeFrom(SceneObject* polyObject) +{ + if (!polyObject) + { + Con::errorf("WorldEditor::createConvexShapeFrom - Invalid object"); + return NULL; + } + + IScenePolyhedralObject* iPoly = dynamic_cast< IScenePolyhedralObject* >(polyObject); + if (!iPoly) + { + Con::errorf("WorldEditor::createConvexShapeFrom - Not a polyhedral object!"); + return NULL; + } + + // Get polyhedron. + + AnyPolyhedron polyhedron = iPoly->ToAnyPolyhedron(); + const U32 numPlanes = polyhedron.getNumPlanes(); + if (!numPlanes) + { + Con::errorf("WorldEditor::createConvexShapeFrom - Object returned no valid polyhedron"); + return NULL; + } + + // Create a ConvexShape. + + ConvexShape* shape = new ConvexShape(); + + // Add all planes. + + for (U32 i = 0; i < numPlanes; ++i) + { + const PlaneF& plane = polyhedron.getPlanes()[i]; + + // Polyhedron planes are facing inwards so we need to + // invert the normal here. + + Point3F normal = plane.getNormal(); + normal.neg(); + + // Turn the orientation of the plane into a quaternion. + // The normal is our up vector (that's what's expected + // by ConvexShape for the surface orientation). + + MatrixF orientation(true); + MathUtils::getMatrixFromUpVector(normal, &orientation); + const QuatF quat(orientation); + + // Get the plane position. + + const Point3F position = plane.getPosition(); + + // Turn everything into a "surface" property for the ConvexShape. + + char buffer[1024]; + dSprintf(buffer, sizeof(buffer), "%g %g %g %g %g %g %g", + quat.x, quat.y, quat.z, quat.w, + position.x, position.y, position.z + ); + + // Add the surface. + + static StringTableEntry sSurface = StringTable->insert("surface"); + shape->setDataField(sSurface, NULL, buffer); + } + + // Copy the transform. + + shape->setTransform(polyObject->getTransform()); + shape->setScale(polyObject->getScale()); + + // Register the shape. + + if (!shape->registerObject()) + { + Con::errorf("WorldEditor::createConvexShapeFrom - Could not register ConvexShape!"); + delete shape; + return NULL; + } + + return shape; +} + DefineEngineMethod( GuiConvexEditorCtrl, hollowSelection, void, (), , "" ) { object->hollowSelection(); diff --git a/Engine/source/gui/worldEditor/guiConvexShapeEditorCtrl.h b/Engine/source/gui/worldEditor/guiConvexShapeEditorCtrl.h index 0bb00c32f..dbf8f267f 100644 --- a/Engine/source/gui/worldEditor/guiConvexShapeEditorCtrl.h +++ b/Engine/source/gui/worldEditor/guiConvexShapeEditorCtrl.h @@ -114,6 +114,8 @@ public: void dropSelectionAtScreenCenter(); void splitSelectedFace(); + SceneObject* createPolyhedralObject(const char* className, SceneObject* geometryProvider); + ConvexShape* createConvexShapeFrom(SceneObject* polyObject); /// Interface with Tools. /// @{ @@ -192,6 +194,16 @@ protected: UndoAction *mLastUndo; UndoManager *mUndoManager; + struct ConvexShapeProxy + { + ConvexShape* shapeProxy; + SceneObject* targetObject; + String targetObjectClass; + bool dirty; + }; + + Vector mProxyObjects; + ConvexEditorTool *mActiveTool; ConvexEditorCreateTool *mCreateTool; }; diff --git a/Templates/Full/game/tools/convexEditor/images/occluderProxyImage.png b/Templates/Full/game/tools/convexEditor/images/occluderProxyImage.png new file mode 100644 index 000000000..51fe109f4 Binary files /dev/null and b/Templates/Full/game/tools/convexEditor/images/occluderProxyImage.png differ diff --git a/Templates/Full/game/tools/convexEditor/images/portalProxyImage.png b/Templates/Full/game/tools/convexEditor/images/portalProxyImage.png new file mode 100644 index 000000000..3828f437c Binary files /dev/null and b/Templates/Full/game/tools/convexEditor/images/portalProxyImage.png differ diff --git a/Templates/Full/game/tools/convexEditor/images/triggerProxyImage.png b/Templates/Full/game/tools/convexEditor/images/triggerProxyImage.png new file mode 100644 index 000000000..f21c4c9ad Binary files /dev/null and b/Templates/Full/game/tools/convexEditor/images/triggerProxyImage.png differ diff --git a/Templates/Full/game/tools/convexEditor/images/zoneProxyImage.png b/Templates/Full/game/tools/convexEditor/images/zoneProxyImage.png new file mode 100644 index 000000000..323e17498 Binary files /dev/null and b/Templates/Full/game/tools/convexEditor/images/zoneProxyImage.png differ diff --git a/Templates/Full/game/tools/convexEditor/materials.cs b/Templates/Full/game/tools/convexEditor/materials.cs new file mode 100644 index 000000000..c184f41e8 --- /dev/null +++ b/Templates/Full/game/tools/convexEditor/materials.cs @@ -0,0 +1,39 @@ +singleton Material( ZoneProxyMaterial ) +{ + mapTo = "ZoneProxyMaterial"; + diffuseMap[0] = "./images/zoneProxyImage"; + materialTag0 = "TestMaterial"; + translucent = true; + translucentBlendOp = "LerpAlpha"; + castShadows = false; +}; + +singleton Material( TriggerProxyMaterial ) +{ + mapTo = "TriggerProxyMaterial"; + diffuseMap[0] = "./images/triggerProxyImage"; + materialTag0 = "TestMaterial"; + translucent = true; + translucentBlendOp = "LerpAlpha"; + castShadows = false; +}; + +singleton Material( PortalProxyMaterial ) +{ + mapTo = "PortalProxyMaterial"; + diffuseMap[0] = "./images/portalProxyImage"; + materialTag0 = "TestMaterial"; + translucent = true; + translucentBlendOp = "LerpAlpha"; + castShadows = false; +}; + +singleton Material( OccluderProxyMaterial ) +{ + mapTo = "OccluderProxyMaterial"; + diffuseMap[0] = "./images/occluderProxyImage"; + materialTag0 = "TestMaterial"; + translucent = true; + translucentBlendOp = "LerpAlpha"; + castShadows = false; +}; \ No newline at end of file