mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-20 12:44:46 +00:00
363 lines
9.2 KiB
C++
363 lines
9.2 KiB
C++
#include "SceneGroup.h"
|
|
|
|
#include "gameBase/gameConnection.h"
|
|
#include "gfx/gfxDrawUtil.h"
|
|
#include "gfx/gfxTransformSaver.h"
|
|
#include "gui/editor/inspector/group.h"
|
|
#include "gui/worldEditor/editor.h"
|
|
#include "math/mathIO.h"
|
|
#include "physics/physicsShape.h"
|
|
#include "renderInstance/renderPassManager.h"
|
|
#include "scene/sceneRenderState.h"
|
|
|
|
IMPLEMENT_CO_NETOBJECT_V1(SceneGroup);
|
|
|
|
ConsoleDocClass(SceneGroup,
|
|
"@brief A collection of arbitrary objects which can be allocated and manipulated as a group.\n\n"
|
|
|
|
"%SceneGroup always points to a (.SceneGroup) file which defines its objects. In "
|
|
"fact more than one %SceneGroup can reference this file and both will update "
|
|
"if the file is modified.\n\n"
|
|
|
|
"%SceneGroup is a very simple object and only exists on the server. When it is "
|
|
"created it allocates children objects by reading the (.SceneGroup) file like "
|
|
"a list of instructions. It then sets their transform relative to the %SceneGroup "
|
|
"and Torque networking handles the rest by ghosting the new objects to clients. "
|
|
"%SceneGroup itself is not ghosted.\n\n"
|
|
|
|
"@ingroup enviroMisc"
|
|
);
|
|
|
|
SceneGroup::SceneGroup()
|
|
{
|
|
// Not ghosted unless we're editing
|
|
mNetFlags.clear(Ghostable | ScopeAlways);
|
|
|
|
mTypeMask |= StaticObjectType;
|
|
}
|
|
|
|
SceneGroup::~SceneGroup()
|
|
{
|
|
}
|
|
|
|
void SceneGroup::initPersistFields()
|
|
{
|
|
docsURL;
|
|
|
|
addGroup("SceneGroup");
|
|
endGroup("SceneGroup");
|
|
|
|
Parent::initPersistFields();
|
|
}
|
|
|
|
bool SceneGroup::onAdd()
|
|
{
|
|
if (!Parent::onAdd())
|
|
return false;
|
|
|
|
mObjBox.set(Point3F(-0.5f, -0.5f, -0.5f),
|
|
Point3F(0.5f, 0.5f, 0.5f));
|
|
|
|
resetWorldBox();
|
|
|
|
// Not added to the scene unless we are editing.
|
|
if (gEditingMission)
|
|
onEditorEnable();
|
|
|
|
addToScene();
|
|
|
|
return true;
|
|
}
|
|
|
|
void SceneGroup::onRemove()
|
|
{
|
|
removeFromScene();
|
|
Parent::onRemove();
|
|
}
|
|
|
|
void SceneGroup::onEditorEnable()
|
|
{
|
|
if (isClientObject())
|
|
return;
|
|
|
|
// Just in case we are already in the scene, lets not cause an assert.
|
|
if (mContainer != NULL)
|
|
return;
|
|
|
|
// Enable ghosting so we can see this on the client.
|
|
mNetFlags.set(Ghostable);
|
|
setScopeAlways();
|
|
addToScene();
|
|
|
|
Parent::onEditorEnable();
|
|
}
|
|
|
|
void SceneGroup::onEditorDisable()
|
|
{
|
|
if (isClientObject())
|
|
return;
|
|
|
|
// Just in case we are not in the scene, lets not cause an assert.
|
|
if (mContainer == NULL)
|
|
return;
|
|
|
|
// Do not need this on the client if we are not editing.
|
|
removeFromScene();
|
|
mNetFlags.clear(Ghostable);
|
|
clearScopeAlways();
|
|
|
|
Parent::onEditorDisable();
|
|
}
|
|
|
|
void SceneGroup::inspectPostApply()
|
|
{
|
|
Parent::inspectPostApply();
|
|
}
|
|
|
|
void SceneGroup::onInspect(GuiInspector* inspector)
|
|
{
|
|
Parent::onInspect(inspector);
|
|
|
|
//Put the SubScene group before everything that'd be SubScene-effecting, for orginazational purposes
|
|
GuiInspectorGroup* sceneGroupGrp = inspector->findExistentGroup(StringTable->insert("Editing"));
|
|
if (!sceneGroupGrp)
|
|
return;
|
|
|
|
GuiControl* stack = dynamic_cast<GuiControl*>(sceneGroupGrp->findObjectByInternalName(StringTable->insert("Stack")));
|
|
|
|
//Regen bounds button
|
|
GuiInspectorField* regenFieldGui = sceneGroupGrp->createInspectorField();
|
|
regenFieldGui->init(inspector, sceneGroupGrp);
|
|
|
|
regenFieldGui->setSpecialEditField(true);
|
|
regenFieldGui->setTargetObject(this);
|
|
|
|
StringTableEntry fldnm = StringTable->insert("RegenerateBounds");
|
|
|
|
regenFieldGui->setSpecialEditVariableName(fldnm);
|
|
|
|
regenFieldGui->setInspectorField(NULL, fldnm);
|
|
regenFieldGui->setDocs("");
|
|
|
|
stack->addObject(regenFieldGui);
|
|
|
|
GuiButtonCtrl* regenButton = new GuiButtonCtrl();
|
|
regenButton->registerObject();
|
|
regenButton->setDataField(StringTable->insert("profile"), NULL, "ToolsGuiButtonProfile");
|
|
regenButton->setText("Regenerate Bounds");
|
|
regenButton->resize(Point2I::Zero, regenFieldGui->getExtent());
|
|
regenButton->setHorizSizing(GuiControl::horizResizeWidth);
|
|
regenButton->setVertSizing(GuiControl::vertResizeHeight);
|
|
|
|
char rgBuffer[512];
|
|
dSprintf(rgBuffer, 512, "%d.recalculateBounds();", this->getId());
|
|
regenButton->setConsoleCommand(rgBuffer);
|
|
|
|
regenFieldGui->addObject(regenButton);
|
|
}
|
|
|
|
void SceneGroup::setTransform(const MatrixF& mat)
|
|
{
|
|
if (isServerObject())
|
|
{
|
|
setMaskBits(TransformMask);
|
|
|
|
MatrixF newXform = mat;
|
|
MatrixF oldXform = getTransform();
|
|
oldXform.affineInverse();
|
|
|
|
MatrixF offset;
|
|
offset.mul(newXform, oldXform);
|
|
|
|
// Update all child transforms
|
|
for (SimSetIterator itr(this); *itr; ++itr)
|
|
{
|
|
SceneObject* child = dynamic_cast<SceneObject*>(*itr);
|
|
if (child)
|
|
{
|
|
MatrixF childMat;
|
|
|
|
//add the "offset" caused by the parents change, and add it to it's own
|
|
// This is needed by objects that update their own render transform thru interpolate tick
|
|
// Mostly for stationary objects.
|
|
childMat.mul(offset, child->getTransform());
|
|
child->setTransform(childMat);
|
|
|
|
PhysicsShape* childPS = dynamic_cast<PhysicsShape*>(child);
|
|
if (childPS)
|
|
childPS->storeRestorePos();
|
|
}
|
|
}
|
|
}
|
|
|
|
Parent::setTransform(mat);
|
|
}
|
|
|
|
void SceneGroup::setRenderTransform(const MatrixF& mat)
|
|
{
|
|
MatrixF newXform = mat;
|
|
MatrixF oldXform = getRenderTransform();
|
|
oldXform.affineInverse();
|
|
|
|
MatrixF offset;
|
|
offset.mul(newXform, oldXform);
|
|
|
|
// Update all child transforms
|
|
for (SimSetIterator itr(this); *itr; ++itr)
|
|
{
|
|
SceneObject* child = dynamic_cast<SceneObject*>(*itr);
|
|
if (child)
|
|
{
|
|
MatrixF childMat;
|
|
|
|
//add the "offset" caused by the parents change, and add it to it's own
|
|
// This is needed by objects that update their own render transform thru interpolate tick
|
|
// Mostly for stationary objects.
|
|
childMat.mul(offset, child->getRenderTransform());
|
|
child->setRenderTransform(childMat);
|
|
|
|
PhysicsShape* childPS = dynamic_cast<PhysicsShape*>(child);
|
|
if (childPS)
|
|
childPS->storeRestorePos();
|
|
}
|
|
}
|
|
|
|
Parent::setRenderTransform(mat);
|
|
}
|
|
|
|
void SceneGroup::addObject(SimObject* object)
|
|
{
|
|
Parent::addObject(object);
|
|
|
|
// Recalculate the bounding box from scratch (simpler but potentially costly)
|
|
recalculateBoundingBox();
|
|
}
|
|
|
|
void SceneGroup::removeObject(SimObject* object)
|
|
{
|
|
Parent::removeObject(object);
|
|
|
|
// Recalculate the bounding box from scratch (simpler but potentially costly)
|
|
recalculateBoundingBox();
|
|
}
|
|
|
|
void SceneGroup::recalculateBoundingBox()
|
|
{
|
|
if (empty())
|
|
return;
|
|
|
|
// Reset the bounding box
|
|
Box3F bounds;
|
|
|
|
bounds.minExtents.set(1e10, 1e10, 1e10);
|
|
bounds.maxExtents.set(-1e10, -1e10, -1e10);
|
|
|
|
// Extend the bounding box to include each child's bounding box
|
|
for (SimSetIterator itr(this); *itr; ++itr)
|
|
{
|
|
SceneObject* child = dynamic_cast<SceneObject*>(*itr);
|
|
if (child)
|
|
{
|
|
const Box3F& childBox = child->getWorldBox();
|
|
|
|
bounds.minExtents.setMin(childBox.minExtents);
|
|
bounds.maxExtents.setMax(childBox.maxExtents);
|
|
}
|
|
}
|
|
|
|
MatrixF newTrans = mObjToWorld;
|
|
newTrans.setPosition(bounds.getCenter());
|
|
Parent::setTransform(newTrans);
|
|
|
|
mObjScale = Point3F(bounds.len_x(), bounds.len_y(), bounds.len_z());
|
|
mWorldBox = bounds;
|
|
resetObjectBox();
|
|
|
|
setMaskBits(TransformMask);
|
|
}
|
|
|
|
U32 SceneGroup::packUpdate(NetConnection* conn, U32 mask, BitStream* stream)
|
|
{
|
|
U32 retMask = Parent::packUpdate(conn, mask, stream);
|
|
|
|
mathWrite(*stream, mObjBox);
|
|
|
|
if (stream->writeFlag(mask & TransformMask))
|
|
{
|
|
mathWrite(*stream, getTransform());
|
|
mathWrite(*stream, getScale());
|
|
}
|
|
|
|
return retMask;
|
|
}
|
|
|
|
void SceneGroup::unpackUpdate(NetConnection* conn, BitStream* stream)
|
|
{
|
|
Parent::unpackUpdate(conn, stream);
|
|
|
|
mathRead(*stream, &mObjBox);
|
|
resetWorldBox();
|
|
|
|
// TransformMask
|
|
if (stream->readFlag())
|
|
{
|
|
mathRead(*stream, &mObjToWorld);
|
|
mathRead(*stream, &mObjScale);
|
|
|
|
setTransform(mObjToWorld);
|
|
}
|
|
}
|
|
|
|
bool SceneGroup::buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F& box, const SphereF& sphere)
|
|
{
|
|
Vector<SceneObject*> foundObjects;
|
|
if (empty())
|
|
{
|
|
Con::warnf("SceneGroup::buildPolyList() - SceneGroup %s is empty!", getName());
|
|
return false;
|
|
}
|
|
findObjectByType(foundObjects);
|
|
|
|
for (S32 i = 0; i < foundObjects.size(); i++)
|
|
{
|
|
foundObjects[i]->buildPolyList(context, polyList, box, sphere);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SceneGroup::buildExportPolyList(ColladaUtils::ExportData* exportData, const Box3F& box, const SphereF& sphere)
|
|
{
|
|
Vector<SceneObject*> foundObjects;
|
|
findObjectByType(foundObjects);
|
|
|
|
for (S32 i = 0; i < foundObjects.size(); i++)
|
|
{
|
|
foundObjects[i]->buildExportPolyList(exportData, box, sphere);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void SceneGroup::getUtilizedAssets(Vector<StringTableEntry>* usedAssetsList)
|
|
{
|
|
//if (empty())
|
|
return;
|
|
|
|
Vector<SceneObject*> foundObjects;
|
|
findObjectByType(foundObjects);
|
|
|
|
for (S32 i = 0; i < foundObjects.size(); i++)
|
|
{
|
|
SceneObject* child = foundObjects[i];
|
|
|
|
child->getUtilizedAssets(usedAssetsList);
|
|
}
|
|
}
|
|
|
|
DefineEngineMethod(SceneGroup, recalculateBounds, void, (), ,
|
|
"Recalculates the SceneGroups' bounds and centerpoint.\n")
|
|
{
|
|
object->recalculateBoundingBox();
|
|
}
|