Contents



Introduction

IMPORTANT NOTE: All declarations of TSShapeConstructor should use the singleton keyword instead of the datablock keyword which was used in the past.


TSShapeConstructor is a special object used to modify a DTS or COLLADA shape model after is has been loaded by Torque, but before it is used by any other object. It is often used to share animations from DSQ files between shapes with a common skeleton, or to gain access to the inner details of a shape.


This document describes extensions to TSShapeConstructor which allow dynamic modification of a loaded 3space shape such as adding and manipulating nodes, geometry and sequences. Torque 3D also includes an in-engine tool called Shape Editor which provides a visual means of creating and editing TSShapeConstructor objects. Additional functionality can be added by defining an 'onLoad' TorqueScript function which is called when the TSShapeConstructor object is loaded.


It may also be used to correct a model that is missing the nodes and/or sequences required to function as a particular Torque object. A model used for a Player character, for example, should have an eye and a cam node, but these might not be present in a model not specifically created for Torque. TSShapeConstructor allows the missing nodes to be added and positioned so that the shape does not need to be re-worked or re-exported by an artist.


To use TSShapeConstructor, simply create a TorqueScript file in the same folder as your DTS or COLLADA model, with the same filename but .cs extension. For example, if your model file was called myShape.dts, you would create a file called myShape.cs. Edit this file with a text editor to define the TSShapeConstructor object and specify any operations to apply to the model after it has been loaded.


When Torque loads a DTS (.dts) or COLLADA (.dae) file, it first looks in the same folder for a TorqueScript file with the same filename (but .cs extension) in order to create the TSShapeConstructor object. Such scripts are executed automatically by Torque 3D, so there is no need to manually call exec("myShape.cs") from another script. You should avoid adding other object and datablock declarations to this script because it will be executed every time the model is loaded, which may cause unexpected results.


After Torque has loaded the model from the DTS or COLLADA file, it executes the TSShapeConstructor onLoad method to apply the defined changes to the shape. It should be noted that the changes are applied to the loaded model in memory rather than to the DTS or COLLADA file itself. This means the model can be re-exported to DTS or COLLADA without overwriting the TSShapeConstructor changes. TSShapeConstructor should be thought of as a post-export processing step, and is intended to be used alongside existing object and datablock setups.


TSShapeConstructor also includes features to aid in loading COLLADA models. Specifically, it allows the <up_axis> and <unit> elements to be overridden, and can also apply a custom prefix to the names of COLLADA materials as shown below. Prefixing material names is useful to avoid name clashes, particularly for 3D apps like Google SketchUp that export models with generic material names like "material0".


Code Sample 1

singleton TSShapeConstructor(MyShapeDae)
{
   baseShape = "./myShape.dae";
   upAxis = "DEFAULT";          // use <up_axis> defined in myShape.dae
   unit = "1.0";                // override <unit> to be 1.0
   matNamePrefix = "";          // no prefix applied to collada materials
};

singleton TSShapeConstructor(MyShape2Dae)
{
   baseShape = "./myShape2.dae";
   upAxis = "Y_AXIS";           // override <up_axis> to be Y_AXIS
   unit = -1;                   // use <unit> defined in myShape2.dae
   matNamePrefix = "myShape2_"; // prefix all collada material names with "myShape2_"
}


The name of the TSShapeConstructor object (MyShapeDae and MyShape2Dae in the code sample above) is up to you, but you should choose a name that does not conflict with other objects or datablocks. A common convention for TSShapeConstructor objects is to use the name of the shape file as the object name.


Note that DSQ sequences may still be specified within the TSShapeConstructor object as normal:


Code Sample 2

singleton TSShapeConstructor(PlayerDts)
{
   baseShape = "./player.dts";
   sequence = "~/data/shapes/players/animations/player_root.dsq root";
   sequence = "~/data/shapes/players/animations/player_forward.dsq run";
};

function PlayerDts::onLoad(%this)
{
   %this.addMesh("cube", "bounds", "Col-1");      // collision mesh
   %this.addSequence("walk", "walk_short", 1, 5); // add subsequence
   %this.addTrigger("walk_short", 3, 4);          // add trigger to sequence
   %this.renameSequence("walk", "walk_long");     // rename sequence
   %this.renameNode("Bip Pelvis", "pelvis");      // rename node
   %this.addNode("mount5", "Bip L Hand");         // add a new node
   %this.dumpShape();                             // dump shape to console
}

Terminology

A shape is made up of the following primitives:

  • node: A node is a place-holder for transform (position and rotation) information. Nodes are arranged in a parent/child hierarchy allowing complex skeleton structures to be built up.
  • object: An object is a collection of meshes, each at a different detail level. Each object is attached to a certain node, and is rendered at that nodes current transform.
  • mesh: A mesh is a piece of geometry, and may be a rigid body or a skin (vertex weighted mesh). Each mesh is associated with an object and a certain detail level.
  • sequence: A sequence is a set of keyframed node transforms and object states (eg. visibility).


Example 1: Adding a Collision Mesh To an Existing Shape

Imagine you have a model that you want to add to the scene as a StaticShape, but it is missing a collision mesh. TSShapeConstructor makes it simple to modify an existing DTS shape to add a collision (or line-of-sight collision) mesh.


First, define the StaticShapeData datablock as normal. Create a script called myShape.cs in the art/datablocks folder, and define the datablock:


Code Sample 3

datablock StaticShapeData(MyShapeData)
{
   category = "Misc";
   shapeFile = "art/shapes/myShape/myShape.dts";
};

We need to tell Torque to execute this script, so add exec("./myShape.cs"); to art/datablocks/datablockExec.cs.


Now we define the TSShapeConstructor object by adding the following script to a file called myShape.cs in the art/shapes/myShape folder:

Code Sample 4

singleton TSShapeConstructor(MyShapeDts)
{
   baseShape = "~/data/shapes/myShape/myShape.dts";
};

function MyShapeDts::onLoad(%this)
{
   %this.addMesh("cube", "bounds", "Col-1");
}


This script will add a cube mesh with the same center and dimensions as the original model using the "Col" detail level at size -1. The negative detail size means that the cube mesh will not be rendered in-game, and the use of the special "Col" name means that this mesh will be detected as a collision mesh by the Torque engine. See the function reference at the end of this document for more details on addMesh.


When a Torque mission is started, the following steps occur:

  1. On the server, Torque executes art/datablocks/datablockExec.cs, which in turn executes art/datablocks/myShape.cs
  2. Executing art/datablocks/myShape.cs causes the MyShapeData datablock to attempt to load the DTS file
  3. Before loading the DTS file, Torque notices andexecutes art/shapes/myShape/myShape.cs, which creates the TSShapeConstructor object
  4. After loading the DTS file, Torque executes MyShapeDts::onLoad, which adds the collision mesh to the shape
  5. The StaticShape object can now be added to the scene (it will appear in the world editor (in the "Misc" category under the Library/Scripted tab)


Example 2: Adding a Mesh From an Existing DTS File

The image below shows a boulder.dts shape in the Torque Show Tool Pro (TSTPro). The circled items indicate the geometry and material that will be copied into a different shape using TSShapeConstructor.


Image:ShapeConSample.jpg‎


The following shows how to include the boulder1 mesh in another shape:


Code Sample 5

singleton TSShapeConstructor(TestShapeDts)
{
   baseShape = "~/data/shapes/rocks/rock1.dts";
};

function TestShapeDts::onLoad(%this) {

  %this.addMesh("~/data/shapes/rocks/boulder.dts", "boulder1", "test128");
  %this.dumpShape();

}

The output of the dumpShape command is shown below. The detail level referenced in the output refers to the size the object must be on the screen before the specific mesh will be rendered. A mesh with a negative detail level is not rendered.

Shape Hierarchy:

   Details:
      detail128, Subtree 0, objectDetail 0, size 128
      detail2, Subtree 0, objectDetail 1, size 2
      collision-1, Subtree 0, objectDetail 2, size -1

   Subtrees:
      Subtree 0
         Rock2 --> Object Rock with following details:  2
               --> Object test with following details:  128
         col-1 --> Object col with following details:  -1

   Sequences:

   Material list:
   material #0: 'rock2'.
   material #1: 'MossyRock02'.

Note that the new detail ("detail128"), object ("test") and material ("MossyRock02") have been added to the normal rock1.dts shape.



Example 3: Auto-loading animations

Instead of manually specifying all of the animations to load, its easy to write some TorqueScript that will scan a folder for any matching animations and add them to the shape automatically. This example uses the addSequence function, and demonstrates the flexibility and power of TSShapeConstructor.


Code Sample 6

singleton TSShapeConstructor(ForgeSoldierDts)
{
   baseShape = "~/data/shapes/actors/ForgeSoldier/ForgeSolder.dts";
};

function ForgeSoldierDts::onLoad(%this)
{
   AddAnimations(%this);
}

function AddAnimations(%shape)
{
   // Scan for all player_*.dsq files and add the animations to the shape.
   // Sequence names are taken from the base name of the dsq file. eg.
   // player_run.dsq would add a sequence called 'run'.

   %filePatterns = "art/shapes/actors/animations/player_*.dsq";
   %fullPath = findFirstFileMultiExpr(%filePatterns);
   while (%fullPath !$= "")
   {
      // add this animation to the shape
      %fullPath = makeRelativePath(%fullPath, getMainDotCSDir());
      %seq_name = strreplace(filebase(%fullPath), "player_", "");
      %shape.addSequence(%fullpath, %seq_name);
      %fullPath = findNextFileMultiExpr(%filePatterns);
   }
}

Example 4: Splitting COLLADA animations

Many COLLADA exporters do not support the <animation_clip> element, meaning that animated models imported into Torque appear to have only a single sequence containing all of the animations. If you know the keyframe ranges used in the individual animations, TSShapeConstructor can be used to split this combined animation into individual sequences. This is most easily done using the Shape Editor tool, but can be done manually by specifying the source sequence name, new sequence name, start and end keyframes as follows:


Code Sample 7

singleton TSShapeConstructor(PlayerModelDts)
{
   baseShape = "~/data/shapes/collada/myPlayer.dae";
};

function PlayerModelDts::onLoad(%this)
{
   // Split animations from combined "ambient" sequence
   %this.addSequence("ambient", "root", 0, 15);
   %this.addSequence("ambient", "run", 16, 40);
   %this.addSequence("ambient", "back", 41, 55);
   %this.addSequence("ambient", "side", 56, 70);
   %this.addSequence("ambient", "death0", 80, 120);

   // Remove combined "ambient" sequence => we don't need it anymore
   %this.removeSequence("ambient");
}

Example 5: Rigid-body Player Character

Using the addNode and addMesh functions, it is possible to create a rigid-body (ie. non-skinned) player model compatible with the default animations, completely from TorqueScript. This is extremely powerful, and demonstrates the full range of functionality available.


The default player skeleton node transforms were obtained by adding the following code to the TSShapeConstructor onLoad function for a shape that already contained the default skeleton:


Code Sample 8

%count = %this.getNodeCount();
for (%i = 0; %i < %count; %i++)
{
   %name = %this.getNodeName(%i);
   echo("%shape.addNode(\"" @ %name @ "\", \"" @ %this.getNodeParentName(%name) @ "\", \"" @
         %this.getNodeTransform(%name) @ "\");");
}

The contents of the console can then be copied and pasted into a new script. The script below shows the player model creation process: first pick a dummy dts file (rock1.dts in this case), and delete its existing nodes and meshes. Then create the default player skeleton. Finally, some cube meshes are added at certain nodes to build up a rigid-body player character.


Code Sample 9

// Create the default Torque skeleton (compatible with the default player animations)
function CreateDefaultSkeleton(%shape)
{
   %shape.addNode("Bip01 Pelvis",      "",                  
      "-8.10647e-005 8.34974e-006 1.28118 0.00147092 0.999914 0.000312013 1.50833");
  
   %shape.addNode("Bip01 Spine",       "Bip01 Pelvis",      
      "0.117738 -0.000117005 0.00695224 0.995531 0 0.054662 0.178899");
  
   %shape.addNode("Bip01 Spine1",      "Bip01 Spine",       
      "0.149188 -0.000154936 5.87594e-006 0.158383 0.207314 0.965104 0.285376");
  
   %shape.addNode("Bip01 Spine2",      "Bip01 Spine1",      
      "0.202462 -0.000162536 2.93078e-005 0.731516 -0.35723 -0.579706 0.251143");
  
   %shape.addNode("Bip01 Neck",        "Bip01 Spine2",      
      "0.209867 -4.86846e-005 -2.05922e-005 -0.926274 0.0899337 -0.365008 0.431595");
  
   %shape.addNode("Bip01 Head",        "Bip01 Neck",        
      "0.067264 -6.98505e-009 -6.66524e-009 -0.139433 0.872649 -0.450268 0.089769");
  
   %shape.addNode("Eye",               "Bip01 Head",        
      "0.180021 0.33282 -0.00125568 -0.0139777 -0.999387 0.030751 1.57784");
  
   %shape.addNode("Bip01 L Clavicle",  "Bip01 Neck",        
      "0.0059334 0.0332864 0.0795624 -0.632128 -0.230228 -0.739819 2.58923");
  
   %shape.addNode("Bip01 L UpperArm",  "Bip01 L Clavicle",  
      "0.358891 -1.27006e-008 1.34618e-007 0.300944 -0.93298 0.197112 1.51847");
  
   %shape.addNode("Bip01 L Forearm",   "Bip01 L UpperArm",  
      "0.395474 -1.37648e-008 1.96789e-008 0 0 0.999871 0.850149");
  
   %shape.addNode("Bip01 L Hand",      "Bip01 L Forearm",   
      "0.363508 3.95569e-008 3.80099e-008 -0.975012 -0.0676349 0.211535 3.8945");
  
   %shape.addNode("Bip01 R Clavicle",  "Bip01 Neck",        
      "-0.00593357 -0.0331793 -0.0795624 0.631473 -0.0957054 -0.769459 3.0916");
  
   %shape.addNode("Bip01 R UpperArm",  "Bip01 R Clavicle",  
      "0.358891 -1.02138e-008 1.01681e-007 0.594451 0.803854 -0.017401 1.47305");
  
   %shape.addNode("Bip01 R Forearm",   "Bip01 R UpperArm",  
      "0.395474 6.19864e-008 1.31302e-008 0 0 0.999951 1.88239");
  
   %shape.addNode("Bip01 R Hand",      "Bip01 R Forearm",   
      "0.363508 1.03579e-008 -1.18848e-008 -0.976003 -0.105148 0.190568 1.71108");
  
   %shape.addNode("mount0",            "Bip01 R Hand",      
      "0.192386 0.0479768 0.000897807 0.0616579 0.0736096 0.995312 1.76131");
  
   %shape.addNode("mount2",            "Bip01 Spine2",      
      "0.101603 -0.255502 0.00682753 0.0431623 -0.998124 -0.0431623 1.57267");
  
   %shape.addNode("mount1",            "Bip01 Spine2",      
      "0.101603 -0.255502 0.00682756 0.0431623 -0.998124 -0.0431623 1.57267");
  
   %shape.addNode("Bip01 L Thigh",     "Bip01 Spine",       
      "-0.117582 -0.0342993 0.1847 -0.227865 -0.966002 -0.121932 3.27836");
  
   %shape.addNode("Bip01 L Calf",      "Bip01 L Thigh",     
      "0.567764 2.88694e-008 -2.61884e-009 0 0 0.999795 0.784881");
  
   %shape.addNode("Bip01 L Foot",      "Bip01 L Calf",      
      "0.659264 6.37417e-009 5.70856e-009 -0.0798252 0.398941 -0.913186 0.331842");
  
   %shape.addNode("Ski0",              "Bip01 L Foot",      
      "0.587469 -0.0426512 -0.0990439 0.750521 0.554827 0.358877 1.9778");
  
   %shape.addNode("Bip01 R Thigh",     "Bip01 Spine",       
      "-0.117896 0.0347 -0.198562 -0.134559 -0.982239 0.130533 2.9039");
  
   %shape.addNode("Bip01 R Calf",      "Bip01 R Thigh",     
      "0.567764 8.48371e-010 1.88575e-009 0 0 0.999915 0.836729");
  
   %shape.addNode("Bip01 R Foot",      "Bip01 R Calf",      
      "0.659264 -3.34983e-008 -2.20877e-008 0.0784375 -0.241304 -0.967069 0.567876");
  
   %shape.addNode("Ski1",              "Bip01 R Foot",      
      "0.600815 -0.0369749 0.0998079 -0.352371 -0.698146 -0.62318 3.91937");
  
   %shape.addNode("Unlink",            "",                  
      "-0.00586435 -0.000234549 0.056661 -0.579417 -0.577691 -0.574872 2.09521");
  
   %shape.addNode("Cam",               "Unlink",            
      "0.237214 2.06536 -0.00742381 0.999942 -0.00266823 -0.00189359 1.57655");
}

// Create a new cube mesh, then attach it to the specified node
function CreateCubeObject(%shape, %name, %node, %size)
{
   %shape.addMesh("cube", %size, %name @ "2");
   %shape.setObjectNode(%name, %node);
}

// Create a simple (rigid) player model using boxes for body parts
function CreateRigidPlayer(%shape)
{
   CreateCubeObject(%shape, "head",             "Eye",               "0.2 0.2 0.3");

   CreateCubeObject(%shape, "upper_torso",      "Bip01 Spine2",      "0.3 0.2 0.4");
   CreateCubeObject(%shape, "lower_torso",      "Bip01 Spine",       "0.3 0.2 0.35");

   CreateCubeObject(%shape, "left_thigh",       "Bip01 L Thigh",     "0.5 0.2 0.2 0.25 0 0");
   CreateCubeObject(%shape, "left_leg",         "Bip01 L Calf",      "0.4 0.2 0.2 0.25 0 0");
   CreateCubeObject(%shape, "left_foot",        "Bip01 L Foot",      "0.2 0.2 0.2");

   CreateCubeObject(%shape, "right_thigh",      "Bip01 R Thigh",     "0.5 0.2 0.2 0.25 0 0");
   CreateCubeObject(%shape, "right_leg",        "Bip01 R Calf",      "0.4 0.2 0.2 0.25 0 0");
   CreateCubeObject(%shape, "right_foot",       "Bip01 R Foot",      "0.2 0.2 0.2");

   CreateCubeObject(%shape, "left_upper_arm",   "Bip01 L UpperArm",  "0.3 0.15 0.15 0.15 0 0");
   CreateCubeObject(%shape, "left_forearm",     "Bip01 L Forearm",   "0.2 0.15 0.15 0.1 0 0");
   CreateCubeObject(%shape, "left_hand",        "Bip01 L Hand",      "0.15 0.15 0.15");

   CreateCubeObject(%shape, "right_upper_arm",  "Bip01 R UpperArm",  "0.3 0.15 0.15 0.15 0 0");
   CreateCubeObject(%shape, "right_forearm",    "Bip01 R Forearm",   "0.2 0.15 0.15 0.1 0 0");
   CreateCubeObject(%shape, "right_hand",       "Bip01 R Hand",      "0.15 0.15 0.15");

   // create collision mesh
   %shape.addMesh("cube", "bounds", "Col-1");
}

singleton TSShapeConstructor(TestModelDts)
{
   baseShape = "~/data/shapes/rocks/rock1.dts";
};

function TestModelDts::onLoad(%this)
{
   // remove the existing nodes and geometry so we start with a blank shape
   %count = %this.getObjectCount();
   for (%i = %count-1; %i >= 0; %i--)
      %this.removeObject(%this.getObjectName(%i));
   %count = %this.getNodeCount();
   for (%i = %count-1; %i >= 0; %i--)
      %this.removeNode(%this.getNodeName(%i));

   // create the player skeleton and rigid body parts
   CreateDefaultSkeleton(%this);
   CreateRigidPlayer(%this);

   // now load the default sequences
   %this.addSequence("~/data/shapes/players/animations/player_root.dsq",    "root");
   %this.addSequence("~/data/shapes/players/animations/player_forward.dsq", "run");

   %this.dumpShape();
}

// The player model definition

singleton PlayerData(TestModelData : DefaultPlayerData)
{
   renderFirstPerson = false;
   emap = true;

   //className = Armor;
   shapeFile = "~/data/shapes/rocks/rock1.dts";
};

PlayerDatasGroup.add(TestModelData);

This produces the following shape:

Shape Hierarchy:

   Details:
      detail2, Subtree 0, objectDetail 0, size 2
      collision-1, Subtree 0, objectDetail 1, size -1

   Subtrees:
      Subtree 0
         Bip01 Pelvis --> Object col with following details:  -1
            Bip01 Spine --> Object lower_torso with following details:  2
               Bip01 Spine1
                  Bip01 Spine2 --> Object upper_torso with following details:  2
                     Bip01 Neck
                        Bip01 Head
                           Eye --> Object head with following details:  2
                        Bip01 L Clavicle
                           Bip01 L UpperArm --> Object left_upper_arm with following details:  2
                              Bip01 L Forearm --> Object left_forearm with following details:  2
                                 Bip01 L Hand --> Object left_Hand with following details:  2
                        Bip01 R Clavicle
                           Bip01 R UpperArm --> Object right_upper_arm with following details:  2
                              Bip01 R Forearm --> Object right_forearm with following details:  2
                                 Bip01 R Hand --> Object right_hand with following details:  2
                                    Mount0
                     Mount2
                     Mount1
               Bip01 L Thigh --> Object left_thigh with following details:  2
                  Bip01 L Calf --> Object left_leg with following details:  2
                     Bip01 L Foot --> Object left_foot with following details:  2
                        Ski0
               Bip01 R Thigh --> Object right_thigh with following details:  2
                  Bip01 R Calf --> Object right_leg with following details:  2
                     Bip01 R Foot --> Object right_foot with following details:  2
                        Ski1
         Unlink
            Cam

   Sequences:
        0: Root (cyclic)
        1: run (cyclic)

TSShapeConstructor Commands


Miscellaneous

dumpShape([string])

This command dumps the shape hierarchy to the console or to a file. It is useful for reviewing the result of a series of construction commands.

Syntax

dumpShape([string filename])

  • filename: Optional. name of the file to dump the shape hierarchy to. If not specified, the shape hierarchy is dumped to the console.


Returns
No return value.

Examples

%this.dumpShape();		// dump to console
%this.dumpShape("./dump.txt");	// dump to file


saveShape(string)

This command saves the shape to a new DTS file. Handy after modifications via script.

Syntax

saveShape(string filename)

  • filename: name of the file to write the shape.


Returns
No return value.

Examples

%this.saveShape("./myShape.dts");


Nodes

getNodeCount()

This command returns the total number of nodes in the shape.

Syntax

getNodeCount()


Returns
S32

Examples

%count = %this.getNodeCount();


getNodeIndex(string)

This command retrieves the index of the named node. If a node with the specified name cannot be found, the command will return -1.

Syntax

getNodeIndex(string node_name);

  • node_name: the name of the node to get the index for


Returns
S32

Examples

// get the index of Bip01 Pelvis node in the shape
%index = %this.getNodeIndex("Bip01 Pelvis");


getNodeName(S32)

This command retrieves the name of the indexed node.

Syntax

getNodeName(S32 index);

  • index: the index of the node to get (valid range is 0 – (num_nodes-1))


Returns
string

Examples

// print the names of all the nodes in the shape
%count = %this.getNodeCount();
for (%i = 0; %i < %count; %i++)
   echo(%i SPC %this.getNodeName(%i));


getNodeParentName(S32)

This command retrieves the name of the nodes parent. If the node has no parent (ie. it is at the root level), this command will return an empty string.

Syntax

getNodeParentName(S32 index);

  • node_name: the name of the node to query


Returns
string

Examples

echo("Bip01 Pelvis parent = " @ %this.getNodeParentName("Bip01 Pelvis"));


getNodeChildCount(string)

This command retrieves the name of the nodes parent. If the node has no parent (ie. it is at the root level), this command will return an empty string.

Syntax

getNodeChildCount(string node_name);

  • node_name: the name of the parent node to query


Returns
S32

Examples

%count = %this.getNodeChildCount("Bip01 Pelvis");


getNodeChildName(string, S32)

This command retrieves the name of the indexed child node. Note that the indices in this case range from 0 to one less than the value returned by getNodeChildCount.

Syntax

getNodeChildName(string node_name, S32 child_index)

  • node_name: the name of the parent node to query
  • child_index: the index of the child node


Returns
string

Examples

function dumpNode(%shape, %name, %indent)
{
   echo(%indent @ %name);
   %count = %shape.getNodeChildCount(%name);
   for (%i = 0; %i < %count; %i++)
      dumpNode(%shape, %shape.getNodeChildName(%name, %i), %indent @ "   ");
}

function dumpShape(%shape)
{
   // recursively dump node hierarchy
   %count = %shape.getNodeCount();
   for (%i = 0; %i < %count; %i++)
   {
      // dump top level nodes
      %name = %shape.getNodeName(%i);
      if (%shape.getNodeParentName(%name) $= "")
         dumpNode(%shape, %name, "");
   }
}


getNodeObjectCount(string)

This command retrieves the number of objects attached to a node.

Syntax

getNodeObjectCount(string node_name)

  • node_name: the name of the node to query

Returns
S32

Examples

%count = %this.getNodeObjectCount("Bip01 Head");
                        


getNodeObjectName(string, S32)

This command is used to get the name of an object attached to the node. Note that the object indices range from 0 to the value returned by getNodeObjectCount.

Syntax

getNodeObjectName(string node_name, S32 object_index)

  • node_name: the name of the node to query
  • object_index: the index of the object


Returns
string

Examples

// print the names of all objects attached to the node
%count = %this.getNodeObjectCount("Bip01 Head");
for (%i = 0; %i < %count; %i++)
   echo(%this.getNodeObjectName("Bip01 Head", %i));


getNodeTransform(string)

This command is used to retrieve the base (ie. not animated) transform of a node.

Syntax

getNodeTransform(string node_name, [bool isworld])

  • node_name: the name of the node for which to get the transform
  • isworld: Optional. Flag indicating whether to retrieve the local-to-parent or the global transform. If not specified, the local-to-parent transform is returned.


Returns
string

  • "pos.x pos.y pos.z rotaxis.x rotaxis.y rotaxis.z rotangle"



Examples

%ret = %this.getNodeTransform("mount0");
%this.setNodeTransform("mount4", %ret);


setNodeTransform(string, string, [string])

This command is used to modify the base transform of a node. That is, the transform of the node when in the root (not-animated) pose.

Syntax

setNodeTransform(string node_name, string px py pz, string [rx ry rz ra], bool [isworld])

  • node_name: the name of the node to modify
  • px py pz: 3 values indicating the new node position
  • rx ry rz ra: Optional. 4 values indicating the new node orientation. The orientation is specified in axis-angle form. ie. the axis of rotation and the angle (in radians) to rotate around that axis. Default is "0 0 1 0".
  • isworld: Optional. Flag indicating whether to set the local-to-parent or the global transform. If not specified, the position and orientation are treated as relative to the node's parent.


Returns
boolean flag indicating success

Examples

%this.setNodeTransform("mount0", "0 0 1");
%this.setNodeTransform("mount0", "0 0 0 0 0 1 1.57");
%this.setNodeTransform("mount0", "1 0 0", true);


renameNode(string, string)

This command is used to rename a node.

Syntax

renameNode(string old_name, string new_name)

  • old_name: old node name
  • new_name: new name of the node


Returns
boolean flag indicating success

Examples

%this.renameNode("Bip01 L Hand", "mount5");


addNode(string, string, [string])

This command is used to add a new node to the shape. The new node may optionally be added as a child of an existing node.

Syntax

addNode(string name, string parent_name, [string px py pz [rx ry rz ra]], [bool isworld])

  • name: the name of the new node – must not be the same as an existing node.
  • parent_name: the name of an existing node to be the parent of the new node. If empty (""), the new node will be at the root level of the node hierarchy.
  • px py pz: Optional. 3 values indicating the position of the new node. Note that this is relative to the parent nodes transform (or the origin if there is no parent). Default is "0 0 0".
  • rx ry rz ra: Optional. 4 values indicating the new node orientation (note that this is relative to the parent nodes transform. The orientation is specified in axis-angle form. ie. the axis of rotation and the angle (in radians) to rotate around that axis. Default is "0 0 1 0".
  • isworld: Optional. Flag indicating whether to set the local-to-parent or the global transform. If not specified, the position and orientation are treated as relative to the node's parent.


Returns
boolean flag indicating success

Examples

%this.addNode("Nose", "Bip01 Head", "0 2 2");
%this.addNode("myRoot", "", "0 0 4 0 0 1 1.57");
%this.addNode("Nodes", "Bip01 Head", "0 2 0", true);


removeNode(string)

This command removes the named node from the shape, including from any sequences that use the node. Child nodes and objects attached to the node are re-assigned to the node's parent.

Syntax

removeNode(string node_name)

  • node_name: the name of the node to remove


Returns
boolean flag indicating success

Examples

%this.removeNode("Nose");


Objects

getObjectCount()

This command is used to get the total number of objects in the shape.

Syntax

getObjectCount()

Returns
S32

Examples

%count = %this.getObjectCount();


getObjectName(S32)

This command is used to get the name of the indexed object.

Syntax

getObjectName(S32 index)

  • index: the index of the object for which to get the name


Returns
string

Examples

// print the names of all objects in the shape
%count = %this.getObjectCount();
for (%i = 0; %i < %count; %i++)
   echo(%i SPC %this.getObjectName(%i));


getObjectNode(string)

This command is used to get the name of the node that an object is attached to. If the object is not attached to a node (ie. it is at the root level), this command will return an empty string.

Syntax

getObjectNode(string object_name)

  • object_name: the name of the object to query


Returns
string

Examples

echo("Hand is attached to " @ %this.getObjectNode("Hand"));


setObjectNode(string, string)

This command is used to set the node that an object is attached to. When the shape is rendered, the object geometry is rendered at the nodes current transform. Useful in mounting weapons to

Syntax

setObjectNode(string object_name, string node name)

  • object_name: the name of the object to modify
  • node_name: the name of the node to attach the object to


Returns
boolean flag indicating success

Examples

%this.setObjectNode("Hand", "Bip01 LeftHand");


renameObject(string, string)

This command is used to rename an object (all detail level meshes for the object will be renamed).

Syntax

renameObject(string old_name, string new_name)

  • old_name: old object name
  • new_name: new name of the object


Returns
boolean flag indicating success

Examples

%this.renameObject("MyBox", "Box");


removeObject(string)

This command is used to remove an object (including all meshes for that object) from the shape.

Syntax

removeObject(string object_name)

  • object_name: the name of the object to remove


Returns
boolean flag indicating success

Examples

// clear all objects in the shape
%count = %this.getObjectCount();
for (%i = %count-1; %i >= 0; %i--)
   %this.removeObject(%this.getObjectName(%i));


Meshes

getMeshCount(string)

This command is used to get the number of meshes for a given object.

Syntax

getMeshCount(string object_name)

  • object_name: the name of the object to query

Returns
S32

Examples

%count = %this.getMeshCount("SimpleShape");


getMeshname(string, S32)

This command is used to get the name of the indexed mesh within a given object. Mesh names are of the form: <object_name><detail_size>. eg. SimpleShape128 is the mesh with detail size 128 for the SimpleShape object.

Syntax

getMeshName(string object_name, S32 index)

  • object_name: the name of the object to query
  • index: the index of the mesh for which to get the name

Returns
string

Examples

// print the names of all meshes in the shape
%objCount = %this.getObjectCount();
for (%i = 0; %i < %objCount; %i++)
{
   %objName = %this.getObjectName(%i);
   %meshCount = %this.getMeshCount(%objName);
   for (%j = 0; %j < %meshCount; %j++)
      echo(%this.getMeshName(%objName, %j));
}


setMeshSize(string, S32)

This command is used to change the detail size of a mesh.

Syntax

setMeshSize(string mesh_name, S32 new_size)

  • mesh_name: the name of the mesh
  • new_size: new size for the mesh

Returns
boolean flag indicating success

Examples

%this.setMeshSize("SimpleShape128", 64);


getMeshType(string)

This command is used to retrieve the type of a particular mesh. The returned value is one of:

  1. normal: a normal 3D mesh
  2. billboard: a mesh that always faces the camera
  3. billboardzaxis: a mesh that always faces the camera in the Z-axis


Syntax

getMeshType(string mesh_name)

  • mesh_name: the name of the mesh to query

Returns
string

Examples

echo("Mesh type is " @ %this.getMeshType("SimpleShape128"));


setMeshType(string, string)

This command is used to set the type of a particular mesh; the type affects how the mesh will be rendered in-game.

Syntax

setMeshType(string mesh_name, string mesh_type)

  • mesh_name: the name of the mesh to query
  • mesh_type: the new type for the mesh. Must be one of "normal", "billboard" or "billboardzaxis".

Returns
boolean flag indicating success

Examples

// set the mesh to be a billboard
%this.setMeshType("SimpleShape64", "billboard");


getMeshMaterial(string)

This command is used to retrieve the name of the material attached to a mesh. Note that only the first material used by the mesh is returned.

Syntax

getMeshMaterial(string mesh_name)

  • mesh_name: the name of the mesh to query

Returns
string

Examples

echo("Mesh material is " @ %this.sgetMeshMaterial("SimpleShape128"));


setMeshMaterial(string, string)

This command is used to set the name of the material attached to the mesh.

Syntax

setMeshMaterial(string mesh_name, string material_name)

  • mesh_name: the name of the mesh to query
  • material_name: the new material name. This could be the base name of the diffuse texture (eg. "test_mat" for "test_mat.jpg"), or the name of a Material object already defined in script.

Returns
boolean flag indicating success

Examples

// set the mesh material
%this.setMeshMaterial("SimpleShape128", "test_mat");



addMesh(string, string, string)

This command is used to add new geometry to the shape. There are two forms to this command as shown below. The first form copies geometry from an existing DTS or DAE shape file. The second form generates the mesh geometry programmatically using a set of predefined geometry primitives (eg. cube, sphere, cylinder etc).

When a mesh is copied from an existing shape file, any materials required by that mesh are also copied into the constructed shape.

The Torque engine uses fixed names to extract collision and line-of-sight (LOS) collision geometry from the shape. When adding this type of geometry to a shape, it is essential that the mesh_name parameter be set as shown in the table below:

Geometry type mesh_name
Normal (visible) any_nameN (where N is > 0)
Collision ColN (where N is -1 to -8)
LOS Collision LOSColN (where N is -9 to -16)



If no detail size is present at the end of the name, a value of 2 is used. An underscore before the number at the end of the name will be interpreted as a negative sign. eg. "MyMesh_4" will be interpreted as "MyMesh-4".

Syntax

addMesh(string shape_filename, string src_mesh, string mesh_name)

  • shape_filename: the name of a shape file (DTS or DAE) that contains the geometry for the new detail level
  • src_mesh: the name of the mesh to use for this detail level (usually the object name followed by the mesh detail size. eg. SimpleShape128)
  • mesh_name: name of the new mesh, using the form <object_name><detail_size>. eg. SimpleShape128



addMesh(string prim_type, string prim_size, string mesh_name)

  • prim_type: the type of geometric primitive to use. Currently only "cube" is supported.
  • prim_size: the size and center of the geometric primitive - either "bounds" or "size.x size.y size.z center.x center.y center.z" format. The center values are optional (ie. just the size can be specified and center will default to "0 0 0").
  • mesh_name: name of the new mesh, using the form <object_name><detail_size>. eg. SimpleShape128

Returns
boolean flag indicating success

Examples

%this.addMesh("./collision.dts", "ColMesh-1", "Col-1");
%this.addMesh("./testShape.dae", "MyMesh2", "SimpleShape10");
%this.addMesh("cube", "bounds", "Col-1");
%this.addMesh("cube", "bounds", "LOSCol-1");
%this.addMesh("cube", "1 1 1", "SimpleShape2");
%this.addMesh("cube", "4 4 2 0 0 0", "SimpleShape2");


removeMesh(string)

This command is used to remove existing geometry from the shape. If all geometry is removed from an object, the object is also removed from the shape.

Syntax

removeMesh(string mesh_name)

  • mesh_name: the name of the mesh to remove

Returns
boolean flag indicating success

Examples

%this.removeMesh("SimpleShape128");


AutoBillboards

addAutoBillboard(S32, S32, S32, S32, S32, bool, F32)

This command is used to add an auto-billboard detail level to the shape. Auto-billboards are special detail levels that render a series of 'snapshots of an object that always face the camera rather than actual 3D geometry. They are frequently used for the lowest detail level of an object.

Syntax

addAutoBillboard(S32 size, S32 equator_steps, S32 polar_steps, S32 dl, S32 dim, bool include_poles, F32 polar_angle)

  • size: size of the billboard detail level
  • equator_steps: defines the number of snapshots to take around the equator. Imagine the object being rotated around the vertical axis, then a snapshot taken at regularly spaced intervals.
  • polar_steps: defines the number of snapshots taken between the poles, at each equator step. eg. At each equator snapshot, snapshots are taken at regular intervals between the poles.
  • dl: the detail level to use when generating the snapshots. Note that this is an array index rather than a detail size. So if an object has detail sizes of: 200, 150, and 40, then setting dl to 1 will generate the snapshot using detail size 150.
  • dim: defines the size of the billboard images in pixels (must be a power of 2: eg. 2, 4, 8, 16….128). The larger the number, the more detailed the billboard will be.
  • include_poles: flag indicating whether to include the "pole" snapshots. ie. the views from the top and bottom of the object.
  • polar_angle: if pole snapshots are active (include_poles is true), this parameter defines the camera angle within which to render the pole snapshot. eg. if polar_angle is set to 25 degrees, then the snapshot taken at the pole (looking directly down or up at the object) will be rendered when the camera is within 25 degrees of the pole.

Returns
boolean flag indicating success

Examples

%this.addAutoBillboard(2, 4, 0, 0, 64, false, 0);
%this.addAutoBillboard(2, 4, 2, 0, 64, true, 10);


removeAutoBillboard(S32)

This command is used to remove an auto-billboard detail level from the shape.

Syntax

removeAutoBillboard(S32 size)

  • size: the size of the auto-billboard detail level to remove

Returns
boolean flag indicating success

Examples

%this.removeAutoBillboard(2);


Sequences

getSequenceCount()

This command is used to get the total number of animation sequences present in the shape.

Syntax

getSequenceCount()

Returns
S32

Examples

%count = %this.getSequenceCount();


getSequenceIndex(S32)

This command is used to get the index of the named sequence. If the sequence does not exist, the command will return -1.

Syntax

getSequenceIndex(string sequence_name)

  • sequence_name: the name of the sequence to get the index of


Returns
S32

Examples

// Check if a given sequence exists in the shape
if (%this.getSequenceIndex("walk") == -1)
   echo("Could not find 'walk' sequence");


getSequenceName(S32)

This command is used to get the name of the indexed sequence.

Syntax

getSequenceName(S32 index)

  • index: the index of the sequence for which to get the name


Returns
string

Examples

// print the name of all sequences in the shape
%count = %this.getSequenceCount();
for (%i = 0; %i < %count; %i++)
   echo(%i SPC %this.getSequenceName(%i));


getSequenceSource(S32)

This command is used to get information about where the sequence data came from, for example, whether it was loaded from an external DSQ file.

Syntax

getSequenceSource(string sequence_name)

  • sequence_name: the name of the sequence to retrieve information for


Returns
The string returned is of the form:

source_data TAB reserved TAB start_frame TAB end_frame TAB total_frames

Where:

  • source_data: the source of the animation data, such as the path to a DSQ file, or the name of an existing sequence in the shape. Sequences already embedded in DTS or DAE files will use the sequence name itself in this field.
  • reserved: reserved value
  • start_frame: the first frame in the source sequence used to create this sequence
  • end_frame: the last frame in the source sequence used to create this sequence
  • total_frames: the total number of frames in the source sequence


Examples

// print the source for the walk animation
echo("walk source:" SPC getField(%this.getSequenceSource("walk"), 0));


getSequenceFrameCount(string)

This command is used to get the number of keyframes in a sequence.

Syntax

getSequenceFrameCount(string sequence_name)

  • sequence_name: name of the sequence to query


Returns
S32

Examples

echo("Run has " @ %this.getSequenceFrameCount("run") @ " keyframes");


getSequencePriority(string)

This command is used to get the priority of a sequence.

Syntax

getSequencePriority(string sequence_name)

  • sequence_name: name of the sequence to query


Returns
F32

Examples

echo("look priority is" SPC %this.getSequencePriority("look"));


setSequencePriority(string, F32)

This command is used to set the priority of a sequence. Priority is used to establish order of importance when multiple sequences operate on the same skeletal node.

Syntax

setSequencePriority(string sequence_name, F32 priority)

  • sequence_name: name of the sequence to modify
  • priority: new priority value


Returns
boolean flag indicating success

Examples

%this.setSequencePriority("look", 5);


getSequenceCyclic(string)

This command is used to determine if a sequence is cyclic (looping).

Syntax

getSequenceCyclic(string sequence_name)

  • sequence_name: the name of the sequence to query


Returns
boolean flag indicating whether the sequence is cyclic or not

Examples

if (!%this.getSequenceCyclic("ambient"))
   error("ambient sequence is not cyclic!");


setSequenceCyclic(string, bool)

This command is used to set the cyclic flag for a sequence in the shape.

Syntax

setSequenceCyclic(string sequence_name, bool cyclic)

  • sequence_name: the name of an existing sequence on which to set the cyclic flag
  • cyclic: state of the cyclic flag (true or false)


Returns
boolean flag indicating success

Examples

%this.setSequenceCyclic("ambient", true);
%this.setSequenceCyclic("shoot", false);


getSequenceBlend(string)

This command returns information about blended sequences.

Syntax

setSequenceBlend(string sequence_name, bool blend_flag, string blend_seq_name, S32 blend_seq_frame)

  • sequence_name: the name of the sequence to query


Returns
The string returned is of the form:

blend_flag TAB blend_seq_name TAB blend_seq_frame

Where:

  • blend_flag: a boolean flag indicating whether this sequence is a blend
  • blend_seq_name: the name of the sequence that contains the reference frame (empty for blend sequences embedded in DTS files)
  • blend_seq_frame: the blend reference frame (empty for blend sequences embedded in DTS files)

Note that only sequences set to be blends using the setSequenceBlend command will contain the blend_seq_name and blend_seq_frame information.

Examples

%blendData = %this.getSequenceBlend("look");
if (getField(%blendData, 0))
   echo("look is a blend, reference: " @ getField(%blendData, 1));


setSequenceBlend(string, bool, string, S32)

This command is used to make an animation sequence a blend sequence, that is, a sequence that will be added on top of any other playing sequences. This is done by storing the animated node transforms relative to a reference frame, rather than as absolute transforms. Commonly used for things like head turns/nods, or weapon aiming.

Syntax

setSequenceBlend(string sequence_name, bool blend_flag, string blend_seq_name, S32 blend_seq_frame)

  • sequence_name: the name of an existing sequence on which to set the blend flag
  • blend_flag: state of the blend flag (true or false)
  • blend_seq_name: the name of the sequence that contains the blend reference frame.
  • blend_seq_name: the reference frame in the blend_seq_name sequence.


Returns
boolean flag indicating success

Examples

%this.setSequenceBlend("look", true, "root", 0);


getSequenceGroundSpeed(string)

This command returns the ground speed of an animation sequence in the form "tx ty tz rx ry rz". Note that only the first 2 ground frames of the sequence are examined; the speed is assumed to be constant throughout the sequence. Ground speed information is used to switch between walk and run states.

Syntax

getSequenceGroundSpeed(string sequence_name)

  • sequence_name: name of the sequence to query


Returns
string

  • "tx ty tz rx ry rz"


Examples

%speed = VectorLen(getWords(%this.getSequenceGroundSpeed("run"), 0, 2));
echo("Run moves at " @ %speed @ " units per frame");


setSequenceGroundSpeed(string, string, [string])

This command is used to set the ground speed of an animation sequence by generating ground transform keyframes. The ground translational and rotational speed is assumed to be constant for the duration of the sequence. Existing ground frames for the sequence (if any) will be replaced.

Syntax

setSequenceGroundSpeed(string sequence_name, string tx ty tz, [string rx ry rz])

  • sequence_name: name of the sequence to modify
  • tx ty tz: ground translational speed in the X, Y and Z axes
  • rx ry rz: Optional. Ground rotational speed around the X, Y and Z axes.


Returns
boolean flag indicating success

Examples

%this.setSequenceGroundSpeed("run", "5 0 0");
%this.setSequenceGroundSpeed("spin", "0 0 0", "4 0 0");


renameSequence(string, string)

This command is used to rename an animation sequence.

Syntax

renameSequence(string old_name, string new_name)

  • old_name: old sequence name
  • new_name: new name of the sequence


Returns
boolean flag indicating success

Examples

%this.renameSequence("walking", "walk");


addSequence(string, string, [S32], [S32]

This command is used to add a new sequence to the shape. The sequence can be a subset of frames from an existing sequence in the shape, or loaded from a different file altogether.

Syntax

addSequence(string sequence_or_shape_file, string new_name, [S32 start_frame], [S32 end_frame])

  • sequence_or_shape_file: the name of an existing sequence, or the name of a DTS or DAE shape or DSQ sequence file. When the shape file contains more than one sequence, the desired sequence can be specified by appending the name to the end of the shape file. eg. "myShape.dts run" would select the "run" sequence from the "myShape.dts" file.
  • new_name: the name of the new sequence.
  • start_frame: Optional. The first sequence frame to copy. Defaults to 0: the first frame in the sequence.
  • end_frame: Optional. The last sequence frame to copy. Defaults to -1: the last frame in the sequence.

Returns
boolean flag indicating success

Examples

%this.addSequence("./testShape.dts ambient", "ambient");
%this.addSequence("./myPlayer.dae run", "run");
%this.addSequence("./player_look.dsq", "look", 0, -1); // start to end
%this.addSequence("walk", "walk_shortA", 0, 4);        // start to frame 4
%this.addSequence("walk", "walk_shortB", 4, -1);       // frame 4 to end


removeSequence(string)

This command is used to remove an animation sequence from the shape.

Syntax

removeSequence(string sequence_name)

  • sequence_name: the name of the sequence to remove


Returns
boolean flag indicating success

Examples

%this.removeSequence ("Run");


getTriggerCount(string)

This command returns the number of triggers in a sequence.

Syntax

getTriggerCount(string sequence_name)

  • sequence_name: the name of the sequence to query


Returns
S32

Examples

echo("Run has" SPC %this.getTriggerCount("run") SPC "triggers");


getTrigger(string)

This command returns the frame and state of a trigger in the sequence.

Syntax

getTrigger(string sequence_name, S32 index)

  • sequence_name: the name of the sequence to query
  • index: index of the trigger to get


Returns
The string returned is of the form:

trigger_frame SPC trigger_state

Examples

// print all triggers in the sequence
%count = %this.getTriggerCount("back");
for (%i = 0; %i < %count; %i++)
   echo(%i SPC %this.getTrigger("back", %i));


addTrigger(string, S32, S32)

This command is used to add a new trigger to a sequence in the shape. A trigger is used to fire off some kind of code event when the animation reaches the given keyframe. Very useful for footprints or attack animations.

Syntax

addTrigger(string sequence_name, S32 keyframe, S32 state)

  • sequence_name: the name of an existing sequence to add the trigger to
  • keyframe: the frame in the sequence at which to set the trigger (the frames for each sequence start at 0)
  • state: the trigger index (1-30) and on/off state (usually +ve for on and –ve for off)


Returns
boolean flag indicating success

Examples

%this.addTrigger("walk", 3, 1);
%this.addTrigger("walk", 5, -1);


removeTrigger(string, S32, S32)

This command is used to remove a trigger from a sequence in the shape.

Syntax

removeTrigger(string sequence_name, S32 keyframe, S32 state)

  • sequence_name: the name of an existing sequence to add the trigger to
  • keyframe: the frame in the sequence at which to set the trigger (the frames for each sequence start at 0)
  • state: the trigger index (1-30) and on/off state (usually +ve for on and –ve for off)


Returns
boolean flag indicating success

Examples

%this.removeTrigger("walk", 3, 1);


Conclusion

From this document, you should have a better understanding of how to use the new TSShapeConstructor. There are several ways you can take advantage of the module, so be sure to bookmark this article so you can refer back to the examples and command list.