Game Development Community

TorqueX 3D Hiding Meshes Part 2

by David Everhart · 09/03/2008 (3:23 am) · 0 comments

Update on Hiding Meshes

I have finally completed my testing on my art asset creation. In my last blog, I had a barebones static mesh , that was able to hide and show meshes using my MeshManager class. I have since updated that class, and added some error handling, refactored some of the functions, replaced one of the loops with a find call, added xml definitions , and changed the return value to a MeshManagerResult object , which contains the status and any error messages. Here it is in its full glory:

using System;
using System.Collections.Generic;
using System.Text;
using GarageGames.Torque.Core;
using GarageGames.Torque.T3D;
namespace StarterGame3D
{
    /// <summary>
    /// This class manages meshes and their visibility. It uses static methods that can be called from anywhere. 
    /// </summary>
    public class MeshManager
    {
        /// <summary>
        /// This Method allows you to turn a meshs visibility on
        /// </summary>
        /// <param name="modelName">This is the name of the model in the TorqueObjectDatabase. </param>
        /// <param name="meshName">This is the name of the mesh in the model you want to show</param>
        public static MeshManagerResult ShowMesh(string modelName, string meshName)
        {
            return SetMeshVisibility(modelName,meshName, true);  
        }

        /// <summary>
        /// This Method allows you to turn a meshs visibility off
        /// </summary>
        /// <param name="modelName">This is the name of the model in the TorqueObjectDatabase. </param>
        /// <param name="meshName">This is the name of the mesh in the model you want to hide</param>
        public static MeshManagerResult HideMesh(string modelName, string meshName)
        {
            return SetMeshVisibility(modelName, meshName, false);
        }

        /// <summary>
        /// This Method allows you to turn off the visibility for all meshs in a model
        /// </summary>
        /// <param name="modelName">This is the name of the model in the TorqueObjectDatabase. </param>
        public static MeshManagerResult HideAllMeshes(string modelName)
        {
            return SetMeshVisibility(modelName, false);  
        }
        /// <summary>
        /// This Method allows you to turn on the visibility for all meshs in a model
        /// </summary>
        /// <param name="modelName">This is the name of the model in the TorqueObjectDatabase. </param>
        public static MeshManagerResult ShowAllMeshes(string modelName)
        {
            return SetMeshVisibility(modelName, true);  
        }

        /// <summary>
        /// This Method is the first overloaded method that allows you to turn
        /// off or on all the mesh's visibility in a model. 
        /// </summary>
        /// <param name="modelName">This is the name of the model in the TorqueObjectDatabase. </param>
        /// <param name="visible">This is a flag to indicate whether to turn all the model's meshes visibility on or off. true = turn on, false = turn off</param>
        private static MeshManagerResult SetMeshVisibility(string modelName, bool visible)
        {
            MeshManagerResult result = new MeshManagerResult();
            TorqueObject model = TorqueObjectDatabase.Instance.FindObject<TorqueObject>(modelName);
            if (model != null)
            {
                T3DTSRenderComponent componentRender = model.Components.FindComponent<T3DTSRenderComponent>();
                for (int meshIndex = 0; meshIndex < componentRender.Shape.Objects.Length; meshIndex++)
                {
                    if (visible)
                    {
                        componentRender.ShapeInstance.MeshObjects[meshIndex].Visibility = 1.0f;
                    }
                    else
                    {
                        componentRender.ShapeInstance.MeshObjects[meshIndex].Visibility = 0.0f;
                    }
                }
                result.Status = MeshManagerResult.StatusType.Success;
            }
            else
            {
                result.Status = MeshManagerResult.StatusType.Failed;
                result.ErrorMessage = "Could not find a model in TorqueObjectDatabase named " + modelName;
            }
            return result;
        }
        /// <summary>
        /// This Method is the second overloaded method that allows you to turn
        /// off or on all the mesh's visibility in a model. 
        /// </summary>
        /// <param name="modelName">This is the name of the model in the TorqueObjectDatabase. </param>
        /// <param name="meshName">This is the name of the mesh in the model</param>
        /// <param name="visible">This is a flag to indiciate whether to turn the mesh's visibility on or off. true = turn on, false = turn off</param>
        private static MeshManagerResult SetMeshVisibility(string modelName, string meshName, bool visible)
        {
            MeshManagerResult result = new MeshManagerResult();
            TorqueObject model = TorqueObjectDatabase.Instance.FindObject<TorqueObject>(modelName);
            if (model != null)
            {
                T3DTSRenderComponent componentRender = model.Components.FindComponent<T3DTSRenderComponent>();
                int meshIndex = componentRender.Shape.FindObject(meshName);
                if(meshIndex > -1)
                {
                    if (visible)
                    {
                        componentRender.ShapeInstance.MeshObjects[meshIndex].Visibility = 1.0f;
                    }
                    else
                    {
                        componentRender.ShapeInstance.MeshObjects[meshIndex].Visibility = 0.0f;
                    }
                    result.Status = MeshManagerResult.StatusType.Success;
                }
                else
                {
                    result.Status = MeshManagerResult.StatusType.Failed;
                    result.ErrorMessage = "Could not find a mesh in the shape named " + meshName;
                }
            }
            else
            {
                result.Status = MeshManagerResult.StatusType.Failed;
                result.ErrorMessage = "Could not find a model in TorqueObjectDatabase named " + modelName;
            }

            return result;

        }
        /// <summary>
        /// This method will get all meshes and the state of their visibility. It returns a List<string> 
        /// where each string is in the format of MeshName: [nameofmesh] ||Visibility: [visible or not]
        /// </summary>
        /// <param name="modelName">This is the name of the model in the TorqueObjectDatabase.</param>
        /// <param name="showHidden">Flag to indicate to show hidden or not</param>
        /// <param name="showVisible">Flag to indicate to show visible or not</param>
        /// <returns></returns>
         public static List<string> ListMeshes(string modelName,bool showHidden, bool showVisible)
        {
            List<string> meshes = new List<string>();
            TorqueObject model = TorqueObjectDatabase.Instance.FindObject<TorqueObject>(modelName);
            if (model != null)
            {
                T3DTSRenderComponent componentRender = model.Components.FindComponent<T3DTSRenderComponent>();
               
                string mesh = "";
                for (int objectIndex = 0; objectIndex < componentRender.Shape.Objects.Length; objectIndex++)
                {
                    float meshVisibility = componentRender.ShapeInstance.MeshObjects[objectIndex].Visibility;

                    if (showVisible && meshVisibility == 1.0f)
                    {
                        mesh = "MeshName:" + componentRender.Shape.Names[componentRender.Shape.Objects[objectIndex].NameIndex] + "||Visibility:Visible";
                        meshes.Add(mesh);
                    }
                    if (showHidden && meshVisibility == 0.0f)
                    {
                        mesh = "MeshName:" + componentRender.Shape.Names[componentRender.Shape.Objects[objectIndex].NameIndex] + "||Visibility:Hidden";
                        meshes.Add(mesh);
                    }
                }
            }
            return meshes;
        }

    }
    /// <summary>
    /// This class represents the result of a MeshManager call
    /// </summary>
    public class MeshManagerResult
    {
        /// <summary>
        /// This enumeration represents the status of the call
        /// </summary>
        public enum StatusType
        {
            /// <summary>
            /// This means the function call was successful
            /// </summary>
            Success,
            /// <summary>
            /// This means the function call was not successful
            /// </summary>
            Failed
        }
        private string _errorMessage="";
        private StatusType _status;

        /// <summary>
        /// This is populated if the Status = Failed with the proper error message
        /// </summary>
        public string ErrorMessage
        {
            get { return _errorMessage;}
            set { _errorMessage = value; }
        }
        /// <summary>
        /// This Status of the call
        /// </summary>
        public StatusType Status
        {
            get { return _status; }
            set { _status = value; }
        }
    }
}

This can be used in conjunction with the "Extending the Torque X Console" resource found here:

Extending the Torque Console

I put in all my console methods into one class called ConsoleMethods. For instance, to be able to type ShowMesh(modelname,meshname) from the Torque Console (accessed by hitting the ' key), I just added this:

#define TORQUE_CONSOLE
using System;
using System.Collections.Generic;
using System.Text;
using GarageGames.Torque.Core;
using GarageGames.Torque.SceneGraph;
using GarageGames.Torque.Sim;
using GarageGames.Torque.GameUtil;
using GarageGames.Torque.T2D;
using GarageGames.Torque.Platform;
using GarageGames.Torque.GUI;
using GarageGames.Torque.T3D;
using GarageGames.Torque.TS;
using GarageGames.Torque.GFX;
using GarageGames.Torque.Util;

namespace StarterGame3D
{
    /// <summary>
    /// This class encapsulates all the custom methods that can be called from the Torque Console
    /// </summary>
    public class ConsoleMethods
    {

        /// <summary>
        /// This method allows you to turn a meshs visibility on in a model. 
        /// From the command line, you can access it via :  ShowMesh(modelname,meshname)
        /// You do not need quotes to surround the modelname or meshname.
        /// example: ShowMesh(Player,Boots)
        /// </summary>
        /// <param name="error"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public static bool ShowMesh(out string error, string[] parameters)
        {
            error = null;

#if !TORQUE_CONSOLE
            Assert.Warn(false, "Warning: TORQUE_CONSOLE not defined, output won't be displayed!");
#endif
            // Print out all the parameters
            if (parameters.Length == 0)
                TorqueConsole.Echo("No parameters passed into ShowMesh()");
            else
            {
                for (int x = 0; x < parameters.Length; x++)
                {
                    TorqueConsole.Echo("Parameter #" + x + " = " + parameters[x]);
                }
                MeshManagerResult result = MeshManager.ShowMesh(parameters[0], parameters[1]);
                if (result.Status == MeshManagerResult.StatusType.Success)
                {
                    TorqueConsole.Echo(parameters[1] + " is Visible");
                }
                else
                {
                    TorqueConsole.Echo(result.ErrorMessage);
                }
            }
            
            return true;
        }
  }
}

I have a lot more methods defined than that, and some that deal with anmations, such as:

/// <summary>
        /// This method allows you to play an animation. 
        /// From the command line, you can access it via :  PlayAnimation(modelname,animationName)
        /// You do not need quotes to surround the modelname.
        /// example: PlayAnimation(Player)
        /// </summary>
        /// <param name="error"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public static bool PlayAnimation(out string error, string[] parameters)
        {
            error = null;

#if !TORQUE_CONSOLE
            Assert.Warn(false, "Warning: TORQUE_CONSOLE not defined, output won't be displayed!");
#endif
            // Print out all the parameters
            if (parameters.Length == 0)
                TorqueConsole.Echo("No parameters passed into ShowMesh()");
            else
            {
                for (int x = 0; x < parameters.Length; x++)
                {
                    TorqueConsole.Echo("Parameter #" + x + " = " + parameters[x]);
                }
                TorqueObject model = TorqueObjectDatabase.Instance.FindObject<TorqueObject>(parameters[0]);
                T3DAnimationComponent animationComponent = model.Components.FindComponent<T3DAnimationComponent>();
                Animation animation = animationComponent.GetAnimation(parameters[1]);
                if (animation != null)
                {
                    animation.Play();
                    TorqueConsole.Echo("Playing Animation");
                }
                else
                {
                    TorqueConsole.Echo("Could not find " + parameters[1]);
                }
            }

            return true;
        }

Once you have your ConsoleMethods class, all it takes to be able to call it then is this in your begin run:

CustomConsoleMethodPool customPool = new CustomConsoleMethodPool();
            customPool.RegisterMethod(ConsoleMethods.ShowMesh);
            customPool.RegisterMethod(ConsoleMethods.HideMesh);
            customPool.RegisterMethod(ConsoleMethods.HideAllMeshes);
            customPool.RegisterMethod(ConsoleMethods.ShowAllMeshes);
            customPool.RegisterMethod(ConsoleMethods.ListMeshes);
            customPool.RegisterMethod(ConsoleMethods.PlayAnimation);
            customPool.RegisterMethod(ConsoleMethods.StopAnimation);
            TorqueConsole.MethodPool = customPool ;

You can then do any type of initialization such as:

string modelName = "Player";
            MeshManager.HideAllMeshes(modelName);
            MeshManager.ShowMesh(modelName, "Default");

Animations

This took me a while to get going. I originally had every peice , per my last blog, of the player , on seperate layers, aka the default model was spread across 5 layers, and the gear he could use was spread across 5 layers. After doing some research on the various approaches, I think I have found a more common method of doing it. I now have one layer containing a default mesh which is the full body with all parts (head, chest, arms, feet, legs). Then, for the gear he can wear, it still resides on the different layers. Here is the new default testguy:

farm4.static.flickr.com/3066/2823010635_c16da1d666_t.jpg

The other 5 layers house individual peices of equipment, and are colored red. I took this into layout, and added one bone in the chest, as seen here:

farm4.static.flickr.com/3089/2823852316_c58f1df317_t.jpg

Here is the mesh setup in Layout:

farm4.static.flickr.com/3143/2823046195_6a7d3dfb2d_t.jpg

With this bone, I created 3 keyframes. At keyframe 0, it was in its rest position. At Keyframe 15, it was rotated horizontally 45 degrees. At KeyFrame 30, it went back to its rest position. This was a very basic animation, and I just wanted to see if I could get it into TorqueX, and then call it. Once I had my basic animation setup, I exported it as a DSQ using our good friend the LW2DTS plugin. I had to use the animation tabs to export the DSQ. One gotcha, is to make sure all your nodes are exported, , as seen below:

farm4.static.flickr.com/3256/2823878794_1e8cce5a39_t.jpg

And you also have to make sure your objects are used in the sequence, as below:

farm4.static.flickr.com/3171/2823046165_5d5ce63abc_t.jpg

Once I had the DTS and DSQ, and verified it worked in Torque Showtool Pro, I put it into the game. Here is a shot where the animation is playing, and I show the head mesh (which would realistically be more like a helm or hair) and the model is in its rest pose:

farm4.static.flickr.com/3098/2823924174_562376092f_t.jpg

Here you can see it rotating per the animation:

farm4.static.flickr.com/3177/2823924212_558b32d8ac_t.jpg

Here you can see the new console method I put in to stop the animation:

farm4.static.flickr.com/3141/2823924274_4291b3865c_t.jpg

Originally, it was only playing one frame, then stopping. After several weeks, and even some correspondence with Andy Hawkins on model setup (his lightwave information is priceless), I figured out that the T3DTSRenderComponent requires an updateanimation call. So on my Player Component, I added a call to updateanimation in the process tick function:

public void ProcessTick(Move move, float dt)
        {
             if (move != null)
            {
                _renderComponent.UpdateAnimation(dt);
            }
        }

In the starter fps demo, they use an animationFSM, but I was just testing a basic animation. Here is the xml for my animation component:

<AnimationComponent type="GarageGames.Torque.T3D.T3DAnimationComponent">
          <SceneGroupName>PlayerMesh</SceneGroupName>
          <Animations>
            <Animation type="GarageGames.Torque.T3D.TSAnimation">
              <ThreadName>ActionThread</ThreadName>
              <SequenceFilename>data\Skeleton\test.dsq</SequenceFilename>
              <Name>Rotate</Name>
            </Animation>
          </Animations>
        </AnimationComponent>

There are some who are having issues having animations run through code only, but that does not surprise me. There seems to be a very heavy focus on having things load through the Torque Deserializer (Like the physic states), which seems like a really odd design choice. I would have thought that you could programmtically setup everything you need without the XML. Then again, I am still new to game engines, so maybe this is how it is supposed to be done.

Summary

I now have my asset creation pipeline functional, and can do basic animations as well as show and hide meshes on demand. I also added some code to handle the toggling of a camera, which was no walk in the park hehe. One thing I noticed was when I switched cameras, I had to make sure it continued to update the animatin, otherwise it would stop. Nonetheless, I have got a lot done, and now with my pipeline defined, and Hiding Mesh class working solid, I am on the right track. I am currently testing John Kanalkis excellent 3d Builder, and will probably attempt to create a generic room, with some artifacts in it as my next step. Until then!