XSIDestructibleObjects - LuisAntonRebollo/Torque-3D-Wiki-Test GitHub Wiki

<SCRIPT SRC="../../../include/tutorial.js" LANGUAGE="JavaScript"></SCRIPT> <SCRIPT SRC="../../../include/prototype.js" LANGUAGE="JavaScript"></SCRIPT> <SCRIPT SRC="../../../include/scriptaculous.js" LANGUAGE="JavaScript"></SCRIPT> <SCRIPT SRC="../../../include/glossaryLookUp.js" LANGUAGE="JavaScript"></SCRIPT> <SCRIPT SRC="../../../include/referenceLookUp.js" LANGUAGE="JavaScript"></SCRIPT> <SCRIPT SRC="../../../include/component.js" LANGUAGE="JavaScript"></SCRIPT> <SCRIPT SRC="../../../include/componentContainer.js" LANGUAGE="JavaScript"></SCRIPT> <SCRIPT>DocImagePath = "../../../";</SCRIPT> <script> // this script chunk is to update the ToC to the current doc and expand it pageID = 14; parent.leftFrame.expandToItem('tree2', 'doc14'); var element = parent.leftFrame.document.getElementById('doc14'); if((element) && (element.className==parent.leftFrame.nodeClosedClass)) { element.className = parent.leftFrame.nodeOpenClass } ; </script> <style> body { font-family:Verdana, Geneva, sans-serif; font-size:12px; width: 700px; } div.design { background-color: #FFC; } div.hint { margin: 10px; font-style:italic; margin-top: 1em; margin-bottom: 2em; } div.toc { background-color: #EEE; width: 700px; } div.warn { background-color: #F93; font-weight:bold; margin-top: 1em; margin-bottom: 2em; } div.advert { margin: 10px; margin-top: 1em; margin-bottom: 2em; background-color: #B0FFB0 } div.codesample { background-color: #9FF; margin: 10px; margin-top: 1em; margin-bottom: 2em; } p { margin-top: 1em; margin-bottom: 1em; } li { margin-top: 1em; margin-bottom: 1em; } ol { margin-top: 1em; margin-bottom: 2em; } ul { margin-top: 1em; margin-bottom: 2em; } h1 { margin-top: 3em; margin-bottom: 1em; } </style>

Contents

*this was made using Autodesk Softimage 2012

In Torque 3D destructible objects are created by building multiple states of an object (unbroken and broken) and swapping between them when appropriate. Once swapped, the pieces of the broken mesh can become physical simulation entities. The physicsShape.cs file is used to tell the engine how to swap out the undamaged and damaged states.

Unlike previous methods, no special PhysX plug-in is needed for your modeling program. In this much simpler method, everything is set up in physicsShape.cs file. Damaged versions can be made of multiple meshes that become physical when broken. For instance, the crate below has a single mesh made of its undamaged state (left) and four physical meshes that make up its damaged state (right):

(click to enlarge)

The crate is made up of two separate mesh files. Both are set up like traditional Torque 3D files, with an exception for the children of the Start Node. In this example, the meshes only have a single LOD.

The unbroken mesh:

(click to enlarge)

            <b>The broken mesh:</b></p>
          <p>(<i>click to enlarge</i>)</p>
          <p><a href="http://docs.garagegames.com/torque-3d/official/content/documentation/Artist%20Guide/Tutorials/images/BrokenHierarchy.jpg" class="livethumbnail">
		  <img src="http://docs.garagegames.com/torque-3d/official/content/documentation/Artist%20Guide/Tutorials/images/BrokenHierarchy.jpg" border="0" width="350" height="88" largewidth="715" largeheight="191" /></a>

A destructible object can have just a single physical collision volume for the broken mesh. For example, you might want a destructible barrel that does not become multiple pieces after the explosion; just a single dented barrel. The same process would be used to swap between the unbroken and broken version of the barrel.

2 Setting Up Collision

Collision is set up in the traditional Torque fashion. Collision meshes are under the Start Node. Collision marker nodes are under the Base Node, using the standard prefix col- to represent a convex collision mesh.

When creating destructible objects, collision volumes must be children of their mesh counterpart. Complicated collision volumes can be costly, so T3D now allows for some inexpensive primitives to be designated using naming conventions. These should be used whenever possible:

  • colbox- is used for box collision shapes
  • colsphere- is used for spherical collision shapes
  • colcapsule- is used for capsule collision shapes (note that cylindrical is not an option)   *in Autodesk Softimage a �cylinder� can be used in place of a �capsule� since Softimage doesn�t have a �Capsule� primitive. Just name it colcapsule- and it will be treated as a capsule. Note: The radius of the bevel is the same as the radius of the cylinder and then the length is to the tip of the capsule not to the start of the bevel.

When using box, sphere, and capsule primitives in your modeling application, use the above prefixes in their name. Primitives are not always the best option for a shape, so the traditional convex mesh designation of col- can still be used for convex shapes.

Each collision volume, convex mesh or primitive must still have a corresponding Collision Marker.

(click to enlarge)

3 Exporting from Softimage

Export your two meshes (broken and unbroken) to COLLADA format. It may be a good idea to create a folder specifically for destructible Physics shapes. Softimage uses Crosswalk for COLLADA export. It is a series of import/export plugins for transferring data between Softimage and other programs via the .xsi, .dae, and .fbx plugins. However, there is a bug in the way that Softimage exports COLLADA when using the �Export Selected� option where when you don�t select via a middle click �Branch Select� then it basically dumps everything in the root of the scene. To overcome this we will use Softimage�s �model containers� for our branches. In the image above you can see the model containers at the top of the hierarchy.

Here is what it looks like in the Explorer window:

Description: C:\Users\jbarnette\Desktop\DestructibleDocumentation_SI\exportHierarchy.jpg

So, to export the crate broken branch we will �middle-click� the �base-node� on the branch for the broken crate. Then hold CTRL and then �Left-Click� the bounds node. Like so:

Description: C:\Users\jbarnette\Desktop\DestructibleDocumentation_SI\exportHierarchySelected.jpg

Now, with the items we need selected we can export via Crosswalk. File>Crosswalk>Export�

Then on the Settings tab we need to make sure that we have the following items checked:

            <img border=0 width=289 height=570
src="images/image014.jpg"
alt="Description: C:\Users\jbarnette\Desktop\DestructibleDocumentation_SI\CrosswalkOptions.jpg"
v:shapes="Picture_x0020_16"></p>
          <p>
            <a name="Hierarchy_For_Meshes_With_Multiple_LODs"></a>
            <h3>4 Hierarchy For Meshes With
            Multiple LODs </h3>
          <p>Multiple LODs require a slightly
            different setup than the crate example above. To set up your mesh so that
            the broken (aka &quot;debris&quot;) pieces, can LOD properly, a &quot;LOD
            helper&quot; must be a parent of the collision volume and meshes for all
            levels of detail for any given debris piece: </p>
          <p><img border=0 width=325 height=645
src="images/image016.jpg"
alt="Description: Description: Description: C:\Users\jbarnette\Desktop\DestructibleDocumentation_SI\multiLOD_explorer.jpg"
v:shapes="Picture_x0020_17"></p>
          <p>The LOD helpers are dummy
            objects which can be named anything as long as they do not end in a number.
            T3D would read this as a LOD level. None of the node names are critical, as
            long as they end in the proper detail level number and do not contain
            numbers otherwise. </p>

5 Aligning the Pivot Points

The pivot points of any renderable mesh and its associated collision volume must share the same coordinates and orientation. An easy method to align these is setting the pivot points of all objects to 0,0,0 in world-space. The pivot points do not need to be in the center of the object they represent.

6 Freeze Transforms

Shapes often need to have their transforms reset after being modeled so they align properly with their collision volume. To avoid any unwanted weirdness just in case you happened to accidently scale one of your �Base� or �Start� nodes, I suggest activating the �Child Compensation� under Constrain on the �MCP� on the right-hand side of the screen.

Description: Description: Description: C:\Users\jbarnette\Desktop\DestructibleDocumentation_SI\chldComp.jpg

After you have enabled that you are ready to freeze things without fear of child objects freaking out because they were parented to an object that had some kind of SRT transfom on them. to freeze the transforms on the MCP Panel click on the word Transform>Freeze All Transforms.

            <img border=0
width=430 height=370 id="_x0000_i1032"
src="images/freeze_transforms.jpg"

                  alt="Description: Description: C:\Users\RRanft\Documents\T3D\XSI docs\SoftimagePhysicsDocumentation_files\freeze_transforms.jpg"></p>
          <p><b>Freeze
            Modeling</b></p>
          <p>Also, you will want to freeze
            the modeling. This is like the collapse stack function in 3D Studio Max. It
            is located in the �Edit� section of the �MCP�</p>
          <p><img border=0 width=179 height=128
src="images/image020.jpg"
alt="Description: Description: Description: C:\Users\jbarnette\Desktop\DestructibleDocumentation_SI\freeze.jpg"
v:shapes="Picture_x0020_12"></p>

7 Setting up the physicsShape.cs file

The physicsShape.cs file goes in the same directory as the exported unbroken and broken meshes. For each destructible object, two datablocks are needed:

  • datablock PhysicsDebrisData (contains information about the behavior of the broken mesh)
  • datablock PhysicsShapeData (contains information about the behavior of the unbroken mesh)

It is critical that the Debris Data precedes the Shape Data for any given destructible object. Example code for our square crate above:

datablock PhysicsDebrisData( CrateSquareDebris )
{
	lifetime = 60.0;
	lifetimeVariance = 0.0;
	velocity = 0.1;
	velocityVariance = 0;
	shapeFile = "art/shapes/physicsShapes/crate_square_broken.DAE";
	mass = 10;
	dynamicFriction = 0;
	staticFriction = 0.5;
	restitution = 0.0;
	linearDamping = 0.0;
	angularDamping = 0.0;
	linearSleepThreshold = 1.0;
	angularSleepThreshold = 1.0;
	waterDampingScale = 1.0;
	buoyancyDensity = 0.0;
	castShadows = "1";
	friction = "0.4";
};

datablock PhysicsShapeData( PSCrateSquare ) { category = "PhysicsShape"; shapeName = "art/shapes/physicsShapes/crate_square_unbroken.DAE"; emap = 1; mass = 5; massCenter = "0 0 0";      // Center of mass for rigid body massBox = "0 0 0";         // Size of box used for moment of inertia, // if zero it defaults to object bounding box drag = 0.2; // Drag coefficient bodyFriction = 0.2; bodyRestitution = 0.1; minImpactSpeed = 5; // Impacts over this invoke the script callback softImpactSpeed = 5;       // Play SoftImpact Sound hardImpactSpeed = 15;      // Play HardImpact Sound integration = 4;           // Physics integration: TickSec/Rate collisionTol = 0.1;        // Collision distance tolerance contactTol = 0.1;          // Contact velocity tolerance minRollSpeed = 10; maxDrag = 0.5; minDrag = 0.01; triggerDustHeight = 1; dustHeight = 10; dragForce = 0.05; vertFactor = 0.05; normalForce = 0.05; restorativeForce = 0.05; rollForce = 0.05; pitchForce = 0.05; debris = CrateSquareDebris; friction = "0.4"; linearDamping = "0.1"; angularDamping = "0.2"; buoyancyDensity = "0.9"; staticFriction = "0.5"; explosion = WoodMinorExplosion; radiusDamage = 0; damageRadius = 0; areaImpulse = 0; restitution = "0.3"; invulnerable = "0"; waterDampingScale = "10"; };

8 Building a Destructible Object With More Than One Damage State

A destructible object can be built with multiple successive damage states. This way the player can slightly damage an object, then fully damage an object:

(click to enlarge)

          <p>
            In this example we will also use a combination of physical and non-physical
            meshes to make up the three &quot;states&quot; of destruction: </p>
          <ul type=disc>
            <li><b>State One:</b> A single, non-physical
              mesh with a single collision volume. When this is fired upon, the mesh
              swaps out and state two replaces it. </li>
          </ul>
          <ul type=disc>
            <li><b>State Two:</b> This state is made up of a
              non-physical lower portion and a number of physical debris pieces for
              the upper portion. When this state replaces the first state, the
              physical &quot;debris&quot; pieces will go flying, giving the
              impression that the top half of state one has been blown to pieces.
              When the non-physical mesh is fired upon, its mesh swaps out and state
              three replaces it. The physical parts of this mesh (the
              &quot;debris&quot;) will not be swapped out. </li>
          </ul>
          <ul type=disc>
            <li><b>State Three:</b> The final state. It is constructed
              like state two, with a non-physical lower portion and a number of
              physical debris pieces for the upper portion. </li>
          </ul>
          <p>(<i>click to enlarge</i>)</p>
          <p><a href="http://docs.garagegames.com/torque-3d/official/content/documentation/Artist%20Guide/Tutorials/images/Threestates.jpg" class="livethumbnail">
		  <img src="http://docs.garagegames.com/torque-3d/official/content/documentation/Artist%20Guide/Tutorials/images/Threestates.jpg" border="0" width="350" height="149" largewidth="700" largeheight="299" /></a></p>

          <p>
            The three different states are exported separately into five different
            files: </p>
          <ul type=disc>
            <li><b>Unbroken Mesh</b> </li>
          </ul>
          <ul type=disc>
            <li><b>State One base</b> </li>
          </ul>
          <ul type=disc>
            <li><b>State One physical pieces</b> </li>
          </ul>
          <ul type=disc>
            <li><b>State Two base</b> </li>
          </ul>
          <ul type=disc>
            <li><b>State Two physical pieces</b> </li>
          </ul>
          <p>
            As before, in order to export the hierarchies properly we have to separate
            things into �model containers,� like so:</p>
          <p><img border=0 width=256 height=194
src="images/image026.jpg"
alt="Description: Description: Description: C:\Users\jbarnette\Desktop\DestructibleDocumentation_SI\explorerMultiStateFence.jpg"
v:shapes="Picture_x0020_18"></p>
          <p>The individual mesh hierarchies
            end up looking something like this: </p>
          <p>(<i>click to enlarge</i>)</p>
		   <p><a href="http://docs.garagegames.com/torque-3d/official/content/documentation/Artist%20Guide/Tutorials/images/image027.jpg" class="livethumbnail">
		  <img src="http://docs.garagegames.com/torque-3d/official/content/documentation/Artist%20Guide/Tutorials/images/image027.jpg" border="0" width="500" height="864" largewidth="700" largeheight="1215" /></a></p>

          <p>
            The meshes are built either like traditional static Torque meshes or as
            physical pieces set up as described in the Introduction, Collision, and LOD
            sections above. 
            As before, &quot;middle-click&quot; the base node of the branch that you
            are exporting and then &quot;CTRL-click the bounds node, then export.</p>
          <p>For example, I saved them out
            as:</p>
          <table border=0 cellpadding=0 width="66%">
            <tr>
              <td width="50%"><p>fence_1_unbk.DAE</p></td>
              <td width="50%"><p>Unbroken
                  fence mesh</p></td>
            </tr>
            <tr>
              <td><p>fence_1_bkstate_a.DAE</p></td>
              <td><p>State
                  A debries mesh</p></td>
            </tr>
            <tr>
              <td><p>fence_1_bkstate_b.DAE</p></td>
              <td><p>State
                  B debries mesh</p></td>
            </tr>
            <tr>
              <td><p>fence_1_bkbase_a.DAE</p></td>
              <td><p>State
                  A base mesh</p></td>
            </tr>
            <tr>
              <td><p>fence_1_bkbase_b.DAE</p></td>
              <td><p>State
                  B base mesh</p></td>
            </tr>
          </table>
          <p>In this case, the physicsShape.cs file should look something like this: </p>
// fence 1 broken state A
datablock PhysicsDebrisData( PSfence1Adebris )
{
	lifetime = 60.0;
	lifetimeVariance = 0.0;
	velocity = 0.1;
	velocityVariance = 0;
	shapeFile = "art/shapes/physicsShapes/fence_1_bkstate_a.DAE";
	mass = 1.5;
	dynamicFriction = 0;
	staticFriction = 0.3;
	restitution = 0.0
	linearDamping = 0.1;
	angularDamping = 0.1;
	linearSleepThreshold = 1.0;
	angularSleepThreshold = 1.0;
	waterDampingScale = 10;
	buoyancyDensity = 0.8;
	friction = "0.2";
};

datablock PhysicsShapeData( PSfence1A ) { category = "PhysicsShape"; shapeName = "art/shapes/physicsShapes/fence_1_unbk.DAE"; mass = 0; debris =PSfence1Adebris; explosion = "SplinterExplosion"; friction = "0.1"; linearDamping = "0.1"; angularDamping = "0.1"; buoyancyDensity = "0.2"; staticFriction = "0.1"; restitution = "0.3"; invulnerable = "0"; minDamageAmount = "0.5"; destroyedShape = "PSfence1B"; };

// fence 1 broken state B datablock PhysicsDebrisData( PSfence1Bdebris ) { lifetime = 60.0; lifetimeVariance = 0.0; velocity = 0.1; velocityVariance = 0; shapeFile = "art/shapes/physicsShapes/fence_1_bkstate_b.DAE"; mass = 1.5; dynamicFriction = 0; staticFriction = 0.3; restitution = 0.0; linearDamping = 0.1; angularDamping = 0.1; linearSleepThreshold = 1.0; angularSleepThreshold = 1.0; waterDampingScale = 10; buoyancyDensity = 0.8; friction = "0.2"; };

datablock PhysicsShapeData( PSfence1baseB ) { category = "PhysicsShape"; shapeName = "art/shapes/physicsShapes/fence_1_bkbase_b.DAE"; mass = 0; invulnerable = "1"; };

datablock PhysicsShapeData( PSfence1B ) { category = "PhysicsShape"; shapeName = "art/shapes/physicsShapes/fence_1_bkbase_a.DAE"; mass = 0; debris = PSfence1Bdebris; explosion = "SplinterExplosion"; friction = "0.1"; linearDamping = "0.1"; angularDamping = "0.1"; buoyancyDensity = "0.2"; staticFriction = "0.1"; restitution = "0.3"; invulnerable = "0"; minDamageAmount = "1.25"; destroyedShape = "PSfence1baseB"; };

In the Datablock Editor you will need to specify the "destroyedShape" for each state:

(click to enlarge)

To place the shape in the level, choose the first state physics shape in the Inspector and add it to your scene.

9 Null LODs

To minimize performance the overhead of having a large number of physical shapes any hierarchy can be given a "Null LOD" detail marker. The detail number of the next LOD marker will denote when the meshes should disappear.

Description: Description: Description: Image:Nulldetail.jpg

10 Conclusion

This tutorial covered the process of adding destructible objects to your T3D game by building multiple states of an object. This feature greatly enhances interaction and immersion, so plan ahead when creating your art and designing levels.

You can download the sample Softimage files shown in this guide by clicking HERE.

 

Contributed by James Brad Barnette.

Home Back to Top
<script type="text/javascript">

var links = document.getElementsByTagName('a');

for (var i = 0; i < links.length; i++) if (links[i].className == 'livethumbnail') { var img = links[i].getElementsByTagName('img')[0]; img.state = 'small'; img.smallSrc = img.getAttribute('src'); img.smallWidth = parseInt(img.getAttribute('width')); img.smallHeight = parseInt(img.getAttribute('height')); img.largeSrc = links[i].getAttribute('href'); img.largeWidth = parseInt(img.getAttribute('largewidth')); img.largeHeight = parseInt(img.getAttribute('largeheight')); img.ratio = img.smallHeight / img.smallWidth; links[i].onclick = scale; }

function scale() { var img = this.getElementsByTagName('img')[0]; img.src = img.smallSrc;

if (! img.preloaded)
{
	img.preloaded = new Image();
	img.preloaded.src = img.largeSrc;
}

var interval = window.setInterval(scaleStep, 10);
return false;

function scaleStep()
{
	var step = 45;
	var width = parseInt(img.getAttribute('width'));
	var height = parseInt(img.getAttribute('height'));
	
	if (img.state == 'small')
	{
		width += step;
		height += Math.floor(step * img.ratio);
		
		img.setAttribute('width', width);
		img.setAttribute('height', height);
		
		if (width > img.largeWidth - step)
		{
			img.setAttribute('width', img.largeWidth);
			img.setAttribute('height', img.largeHeight);
			img.setAttribute('src', img.largeSrc);
			window.clearInterval(interval);
			img.state = 'large';
		}
	}
	else
	{
		width -= step;
		height -= Math.floor(step * img.ratio);

		img.setAttribute('width', width);
		img.setAttribute('height', height);
		
		if (width < img.smallWidth + step)
		{
			img.setAttribute('width', img.smallWidth);
			img.setAttribute('height', img.smallHeight);
			img.src = img.smallSrc;
			window.clearInterval(interval);
			img.state = 'small';
		}
	}
}			

}

</script>
⚠️ **GitHub.com Fallback** ⚠️