Game Development Community

Place objects when loading the map

by Alfio Saitta · in Torque 3D Professional · 05/01/2010 (7:16 pm) · 9 replies

With Blender, i have exported as nodes, the position and rotation of certain objects in the scene. With an scripts, I extract very simply the values. But now I can not to read those values to create and move in the correct place the new objects, directly when loading the map.

I do not get anything with:
datablock TSShapeConstructor(TestLoaderDts)
{
   baseShape = "./TestLoader.dts";
};

function TestLoaderDts::onLoad(%this)
{
   echo("---------------------------------------------------");
   echo("-- Dump all ChildNodes with WorldSpace Transform --");
   echo("---------------------------------------------------");
   %nodeName = "LightsNodes";
   %count = %this.getNodeChildCount(%nodeName);
   for (%i = 0; %i < %count; %i++)
   {
      %childName = %this.getNodeChildName(%nodeName, %i);
      %pos = %this.getNodePosition(%childName, true);
      %rot = %this.getNodeRotation(%childName, true);
      echo(" *) " @ %childName @ ": POS= (" @ %pos @ ") - ROT= (" @ %rot @ ")");
   }
   echo("---------------------------------------------------");
}

But it works if i load the dts file in the ShapeEditor. From log:
---------------------------------------------------
-- Dump all ChildNodes with WorldSpace Transform --
---------------------------------------------------
 *) Lamp_001: POS= -7.70346 -7.56811 2.60694 - ROT= 1 0 0 0
 *) Spot_001: POS= 0 -5.41756 2.60694 - ROT= -1 0 0 0.163453
 *) Spot_002: POS= -5.21938 2.42546 2.60694 - ROT= 0 1 0 0.280033
---------------------------------------------------

#1
05/02/2010 (6:33 am)
Solved, just use the Singleton:
singleton TSShapeConstructor(TestLoaderDts)
{
   baseShape = "./TestLoader.dts";  
};
instead of:
datablock TSShapeConstructor(TestLoaderDts)
{
   baseShape = "./TestLoader.dts";  
};
#2
05/02/2010 (6:09 pm)
As I wrote above, I solved the problem of the script execution. But is normal, I stumbled upon another problem. With the script, I can read the values of the nodes in WorldSpace, but do not match the actual positions.

What's wrong?

function TestLoaderDts::onLoad(%this)
{
   ....
   %nodeName = "LightsNodes";
   %count = %this.getNodeChildCount(%nodeName);
   for (%i = 0; %i < %count; %i++)
   {
      %childName = %this.getNodeChildName(%nodeName, %i);
      %trans = %this.getNodeTransform(%childName, true);
      %pos = getWords(%trans, 0, 2);
      %rot = getWords(%trans, 3, 6);
      echo(" *) " @ %childName @ ": POS= " @ %pos @ " - ROT= " @ %rot);
      
      %NewLight = new PointLight(%childName) {
         ....
         position = %pos;
         rotation = %rot;
         ....
      };
      MissionCleanup.add(%NewLight);
   }
   ....
}

This is the model in ShapeEditor:
img710.imageshack.us/img710/7746/immaginemk.png
And this is the result:
img219.imageshack.us/img219/144/screenshot00200002.png
#3
05/03/2010 (7:57 am)
I'm not familiar with TSShapeConstructor, but it looks like your lights are being created with the correct relative positions and orientations with respect to each other. I bet getNodeTransform() returns the node's position in object space, so the lights end up positioned around the origin rather than your object in world space. If so, you need to transfrom their positions by your object's world transform. So after the object has been created in the world (I'm assuming onLoad() is executed before actual instantiation), try something like this for each light:

%worldTrans = myObject.getTransform();
%lightPos = myLight.getPosition();
%lightPos = MatrixMulPoint(%worldTrans, %lightPos);
myLight.setTransform(%lightPos);
myLight.setScale(myLight.getScale());    // Forces network update
#4
05/03/2010 (12:50 pm)
I've been researching what you're trying to do, and I'm not sure it can be done with the TSShapeConstructor. The reason being, the TSShapeConstructor executes before the object using the DTS is created; therefore, it does not have access to the world position of the nodes because the object has not been transformed into world space yet. Although the code snippet I posted above works after the object is created, there is no easy way (that I've found) to tie the lights created in the onLoad() method to the object for whom the TSShapeConstructor was called.

With that said, I think it would be much easier to modify the source code to accomplish your automatic placement of lights. For example, you could modify TSStatic::onAdd() to look for "LightsNodes" (if it's executing server-side). Here you have direct access to the TSShape and the object's world transform, so it should be easy to create the pointlights at the child node positions.
#5
05/05/2010 (8:42 am)
Now the coordinates returned by the main object is correct.

Nothing. I can not get out. And I find nothing in the documentation. :D

@Ryan: You're right, using the constructor is virtually impossible without changing the code.

singleton TSShapeConstructor(TestLoaderDts)
{
   baseShape = "./TestLoader.dts";  
};
function TestLoaderDts::onLoad(%this)
{
   echo("---------------------------------------------------");
   echo("-- Dump all ChildNodes with WorldSpace Transform --");
   echo("---------------------------------------------------");      
   %nodeName = "LightsNodes";   
   $MapNodeCount = %this.getNodeChildCount(%nodeName);  
   for (%i = 0; %i < $MapNodeCount; %i++)
   {
      %childName = %this.getNodeChildName(%nodeName, %i);
      %trans = %this.getNodeTransform(%childName, true);
      $MapLightPos[%i] = getWords(%trans, 0, 2);
      $MapLightRot[%i] = getWords(%trans, 3, 6);
      echo(" *) " @ %childName @ ": POS= " @ $MapLightPos[%i] @ " - ROT= " @ $MapLightRot[%i]);
   }
   echo("---------------------------------------------------");
}
function populateMap(%obj )
{
   %RootPosition = %obj.getposition();
   for (%i = 0; %i < $MapNodeCount; %i++)
   {
      %pos = MatrixMulPoint(%RootPosition, $MapLightPos[%i]);
      %rot = $MapLightRot[%i];
      echo ( "* MapLight_" @ %i @ ": POS = " @ %pos @ " ROT = " @ %rot);
      %NewLight = new PointLight(MapLight_ @ %i) {
         ....
         position = %pos;
         rotation = %rot;
         ....
      };
      MissionGroup.add(%NewLight);
      %NewLight.setTransform(%pos);
      %NewLight.setScale(%NewLight.getScale());
   }
}
#6
05/05/2010 (3:42 pm)
It may not be obvious from the TSShapeConstructor documentation, but TSShapeConstructor is just a normal SimObject, so you don't have to use the onLoad interface if that is not suitable for your purposes. The onLoad interface is useful for modifying the loaded shape before it is used by other game objects (Player, TSStatic, WheeledVehicle etc).

But there's no reason why you can't do something like this:

function populateMap(%obj)
{
   // Get a TSShapeConstructor for this object (use the ShapeEditor
   // utility functions to create one if it does not already exist).
   %shapePath = ShapeEditor.getObjectShapeFile(%obj);
   %shape = ShapeEditor.findConstructor(%shapePath);
   if (!isObject(%shape))
      %shape = ShapeEditor.createConstructor(%shapePath);
   if (!isObject(%shape))
   {
      echo("Failed to create TSShapeConstructor for " @ %obj.getId());
      return;
   }

   %rootTxfm = %obj.getTransform();

   %nodeName = "LightsNodes";
   %count = %shape.getNodeChildCount(%nodeName);
   for (%i = 0; %i < %count; %i++)
   {
      %childName = %shape.getNodeChildName(%nodeName, %i);

      // get node transform in object space, then transform to world
      // space
      %trans = %shape.getNodeTransform(%childName, true);
      %trans = MatrixMultiply(%rootTxfm, %trans);
      %pos = getWords(%trans, 0, 2);
      %rot = getWords(%trans, 3, 6);
      echo(" *) " @ %childName @ ": POS= " @ %pos @ " - ROT= " @ %rot);

      // create a new light at the desired transform
      %light = new PointLight(MapLight_ @ %i) {
         ....
         position = %pos;
         rotation = %rot;
         ....
      };  
      MissionGroup.add(%light);
   }  
}

Disclaimer: example only; not tested for syntax/logic errors.
#7
05/07/2010 (11:27 am)
@Chris: Thank you very much, your code works fine. My error was in calculating the matrix. Unfortunately, I never find the proper documentation. At this point I have to call up the complete code at the appropriate time. Thank you again
#8
05/07/2010 (2:58 pm)
Why not just add
Con::executef(this, "onAdd", Con::getIntArg(getId()));
to the TSShapeConstructors onAdd method (not sure why it doesn't exist there already). Then you could add your lights after it has been loaded and added to the scene.

you could then do this in TS:
function TestLoaderDts::onadd(%obj){
   // Get a TSShapeConstructor for this object (use the ShapeEditor
   // utility functions to create one if it does not already exist).
   %shapePath = ShapeEditor.getObjectShapeFile(%obj);
   %shape = ShapeEditor.findConstructor(%shapePath);
   if (!isObject(%shape))
      %shape = ShapeEditor.createConstructor(%shapePath);
   if (!isObject(%shape))
   {
      echo("Failed to create TSShapeConstructor for " @ %obj.getId());
      return;
   }

   %rootTxfm = %obj.getTransform();

   %nodeName = "LightsNodes";
   %count = %shape.getNodeChildCount(%nodeName);
   for (%i = 0; %i < %count; %i++)
   {
      %childName = %shape.getNodeChildName(%nodeName, %i);

      // get node transform in object space, then transform to world
      // space
      %trans = %shape.getNodeTransform(%childName, true);
      %trans = MatrixMultiply(%rootTxfm, %trans);
      %pos = getWords(%trans, 0, 2);
      %rot = getWords(%trans, 3, 6);
      echo(" *) " @ %childName @ ": POS= " @ %pos @ " - ROT= " @ %rot);

      // create a new light at the desired transform
      %light = new PointLight(MapLight_ @ %i) {
         ....
         position = %pos;
         rotation = %rot;
         ....
      };  
      MissionGroup.add(%light);
   }
}
#9
05/07/2010 (8:10 pm)
@Ryan: This wouldn't work because the TSShapeConstructor object is tied to the 3D shape resource (DTS or DAE file) rather than a specific game object (TSStatic, Player, WheeledVehicle etc). When the TSShapeConstructor object's onLoad method is called, it doesn't know what object (may be more than 1!) is going to use the 3D shape resource.

TSShapeConstructor::onLoad is used to modify the 3D shape resource in-memory before it is used by other game objects. If you want to do game object specific actions (as in this case), you need to do it once that game object has been created.