Game Development Community

dev|Pro Game Development Curriculum

Mission Editor Snapping Tool Update

by Daniel Hopkins · 09/24/2008 (6:49 am) · 7 comments

Download Code File

Hello again!! I would first like to emphasize that this is an update to the previous snapping tool. If you haven't already downloaded and installed it, do so now: you can find it here. Alright, so I received some encouraging replys to the tool, so I decided to improve on it a bit. As before, the ".zip" file includes the installation guide. In short, the improvements are:

1) There are some objects, namely those listed under the "Mission Objects" header, which will crash the program if you use the tool to add them. As a result, it now checks to see if it can "safely" add the object, and if not, it will notify you via a dialog box, revert to "Drop to Camera" mode, add the object, and then revert back into "Drop to cursor(Snap)" mode.

2) You will have also noticed that interiors are pitch black when you add them, and remain so until a scene relight. That has been fixed: the interiors are now updated as you move them in the same way as when they are moved using the axis gizmos in the world editor.

3) You can now rotate the object along its relative x-axis by holding down "ctrl" and "alt" and moving your mouse left and right. This adds a little more object placement control.

4) A new option has been added under the "Snap uses surface normals": "Snap uses random Z-rotation." This option aids in the process of adding things like rocks, trees, crates, etc. which you generally do not want to be oriented in the same way. It randomly rotates the object along its relative z-axis every time you add a new one. Activate the option, select a tree to add, [click,click, click], and you will now have a grove of three trees which are not uniformaly aligned.

5) A few smaller tweaks and such not really worth enough to mention.


In all, there you have it. Just download and enjoy!!

#1
09/24/2008 (8:42 am)
Daniel, I haven't looked yet, but can you tell more about this:

2) You will have also noticed that interiors are pitch black when you add them, and remain so until a scene relight. That has been fixed: the interiors are now updated as you move them in the same way as when they are moved using the axis gizmos in the world editor.

Does this mean you've found a way around needing a full relight?
#2
09/24/2008 (10:02 am)
Yes, that would be very nice not having to do full relights every time you move 1 little object. This resource looks sweet, will try it for the next proj.
Good Work.
#3
09/24/2008 (5:48 pm)
Sorry guys, perhaps I wasn't clear enough. This resource only pertains to my previously released snapping tool resource found here: Snapping Tool. It makes no changes to how Torque operates, so "yes" you will need to do a full relight. The issues addressed are problems exhibited while using the tool only.
#4
09/24/2008 (10:27 pm)
Hi Daniel, awesome resource, would love to talk more about a few more mission ideas if you dont mind. Contact me on the email in my profile :D
#5
11/30/2008 (4:03 pm)
I got this working with TGEA 1.71. Changes are in my next post. There are 2 major bugs outstanding still, maybe someone can help.

Bug 1: Deleting objects placed with the snap tool results in a crash.
EDIT: Fixed in snappingTool.cs below

Bug 2: The lighting fix does not compile with TGEA. The code borrowed in engine for this fix is also commented out in TGEA, so a more in depth solution is needed.
#6
11/30/2008 (4:03 pm)
---------------------------------------------------------
TGEA 1.71 ENGINE CHANGES:


sceneGraph\sceneObject.h
In the Class definition, right under 'Public:' at line 284:
//===========================================
//SNAP TOOL============================
///Rotates object around relative axis
void RotateObj(const EulerF & snapRot);
//===========================================



sceneGraph\sceneObject.cpp
Under Console Callbacks section, around line 50:
//================================
//SNAP TOOL=======================
ConsoleMethod( SceneObject, getWorldTransform, const char*, 2, 2, "Get world transform of object.")
{	
   char *returnBuffer = Con::getReturnBuffer(256);
   const MatrixF& mat = object->getWorldTransform();
   Point3F pos;
   mat.getColumn(3,&pos);
   AngAxisF aa(mat);
   dSprintf(returnBuffer,256,"%g %g %g %g %g %g %g",
            pos.x,pos.y,pos.z,aa.axis.x,aa.axis.y,aa.axis.z,aa.angle);
   return returnBuffer;  
}

ConsoleMethod( SceneObject, RotateObj, void, 3, 3, "(Point3F rot)")
{
   EulerF rot(0.f,0.f,0.f);
   dSscanf(argv[2], "%g %g %g", &rot.x, &rot.y, &rot.z);
   object->RotateObj(rot);
}
//================================

Around line 615:
//===========================
//SNAP TOOL==================
void SceneObject::RotateObj(const EulerF & snapRot)
{	
	Point3F snapCenter = this->getBoxCenter();
	MatrixF snapMat = this->getTransform();
    Point3F snapPos;
    snapMat.getColumn(3, &snapPos);

    // get offset in obj space
    Point3F snapOffset = snapPos - snapCenter;
    MatrixF wSnapMat = this->getWorldTransform();
    wSnapMat.mulV(snapOffset);

	MatrixF snapTransform(EulerF(0,0,0), -snapOffset);
    snapTransform.mul(MatrixF(snapRot));
    snapTransform.mul(MatrixF(EulerF(0,0,0), snapOffset));
    snapMat.mul(snapTransform);

    this->setTransform(snapMat);
}
//===========================



gui\missionEditor\editTSCtrl.h
Around line 138, in the 'public:' section:
//=======================
//SNAP TOOL==============
void send3DMouseEvent(const char * name, const Gui3DMouseEvent & );
//=======================



gui\missionEditor\editTSCtrl.cpp
Anywhere after includes, I put it around line 40:
//==============================
//SNAP TOOL=====================
void EditTSCtrl::send3DMouseEvent(const char * name, const Gui3DMouseEvent & event)
{
   char buf[4][32];
   dSprintf(buf[0], sizeof(buf[0]), "%d", event.modifier);
   dSprintf(buf[1], sizeof(buf[1]), "%f %f %f", event.vec.x, event.vec.y,event.vec.z);
   dSprintf(buf[2], sizeof(buf[2]), "%f %f %f", event.pos.x, event.pos.y,event.pos.z);
   dSprintf(buf[3], sizeof(buf[3]), "%d %d",event.mousePoint.x,event.mousePoint.y);
   Con::executef(this, name, buf[0], buf[1], buf[2],buf[3]);
}
//==============================

Add the bold below to "void EditTSCtrl::onMouseDown(const GuiEvent & event)" method:
void EditTSCtrl::onMouseDown(const GuiEvent & event)
{
   mLeftMouseDown = true;
   mLastBorderMoveTime = 0;
   make3DMouseEvent(mLastEvent, event);
   on3DMouseDown(mLastEvent);
   
   [b]//=======================
   //SNAP TOOL==============
   send3DMouseEvent("on3DMouseDown",mLastEvent);
   //=======================[/b]
}


Add the bold below to "void EditTSCtrl::onMouseMove(const GuiEvent & event)" method:
void EditTSCtrl::onMouseMove(const GuiEvent & event)
{
   make3DMouseEvent(mLastEvent, event);
   on3DMouseMove(mLastEvent);

   [b]//=======================
   //SNAP TOOL==============
   send3DMouseEvent("on3DMouseMove",mLastEvent);
   //=======================[/b]

   mLastMousePos = event.mousePoint;
}


Add the bold below to "void EditTSCtrl::onRightMouseDown(const GuiEvent & event)" method:
void EditTSCtrl::onRightMouseDown(const GuiEvent & event)
{
   // always process the right mouse event first...

   mRightMouseDown = true;
   mLastBorderMoveTime = 0;

   make3DMouseEvent(mLastEvent, event);
   on3DRightMouseDown(mLastEvent);

   [b]//======================
   //SNAP TOOL=======
   send3DMouseEvent("on3DRightMouseDown",mLastEvent);
   //======================[/b]
...



T3D\tsStatic.cpp
Add the bold below to "void TSStatic::setTransform(const MatrixF & mat)" method:
void TSStatic::setTransform(const MatrixF & mat)
{
   Parent::setTransform(mat);
   
   [b]//==================
   //SNAP TOOL=========
   setMaskBits(advancedStaticOptionsMask);
   //==================[/b]
   
   // Since the interior is a static object, it's render transform changes 1 to 1
   //  with it's collision transform
   setRenderTransform(mat);
}



gui\missionEditor\worldEditor.cpp
Insert below where console methods are defined:
//====================================
//SNAP TOOL===========================
ConsoleMethod( WorldEditor, updateClientTransforms, void, 2, 2, "")
{
	for(U32 i = 0; i < object->mSelected.size(); i++)
   {
      SceneObject * clientObj = object->getClientObj(object->mSelected[i]);
      if(!clientObj)
         continue;
	
	  //INTERIOR RELIGHTS
      //This next part does not compile. 
	  //This code was borrowed from elsewhere in the engine, 
	  //and that is commented out too in TGEA. 
	  //Please help to get moving DIFs to work with lighting.
	  
      /*InteriorInstance*	ii = dynamic_cast<InteriorInstance*>(clientObj);
      if(ii != NULL && ii->mSmoothLighting == false)
      {
         // Set it to be dynamically lit as well.
         ii->mDoSimpleDynamicRender = true;
         Con::executef( 1, "onNeedRelight" );
      }*/

      clientObj->setTransform(object->mSelected[i]->getTransform());
      clientObj->setScale(object->mSelected[i]->getScale());
   }
}
//====================================


---------------------------------------------------------
---------------------------------------------------------
---------------------------------------------------------
TGEA 1.71 SCRIPT CHANGES:


Tools\missionEditor\scripts\menu.ed.cs
Right under this "item[18] = "Drop Location" TAB %this.dropTypeMenu;" around line 201:
//===================
//SNAP TOOL====
item[19] = "-";
item[20] = "Snap Tool: Drop to Cursor" TAB "" TAB "snapEditUseSnapToggle();";
item[21] = "Snap Tool: Use Surface Normals" TAB "" TAB "snapEditFollowNormToggle();";
//===================



Tools\missionEditor\gui\EditorGui.ed.cs
At the bottom, right before the last '};', insert the below code:
new EditTSCtrl(mouseCtrl) {
	canSaveDynamicFields = "0";
	Profile = "GuiDefaultProfile";
	HorizSizing = "width";
    VertSizing = "height";
	position = "0 0";
	Extent = "800 600";
	MinExtent = "8 2";
	canSave = "1";
	Visible = "1";
	hovertime = "1000";
	lockMouse = "0";
};



Tools\missionEditor\scripts\editors\creator.ed.cs
Change the "Creator::onInspect" method to look like the following (note bold):
function Creator::onInspect(%this,%obj)
{
   if(!$missionRunning)
      return;

   %objId = eval(%this.getItemValue(%obj));

   [b]//=============================================
   //SNAP TOOL====================================
   /*
   // drop it from the editor - only SceneObjects can be selected...
   Creator.removeSelection(%obj);

   EditorTree.clearSelection();
   EWorldEditor.clearSelection();
   EWorldEditor.selectObject(%objId);
   EWorldEditor.dropSelection();
   */
   
   if($UseSnap)
   {
		%cName = %objId.getClassName();
		
		if( (%cName !$= "TSStatic") && (%cName !$= "InteriorInstance")  && (%cName !$= "Item") && (%cName !$= "StaticShape") && (%cName !$= "WayPoint") && (%cName !$= "SpawnSphere"))
		{
			messageboxok("Snap Tool Error!", "Cannot use tool for this type of object!", "");
			
			//EditorMenuBar.onWorldMenuItemSelect(0, "Drop at Camera");
			
			Creator.removeSelection(%obj);
			   
			EditorTree.clearSelection();
			EWorldEditor.clearSelection();
			EWorldEditor.selectObject(%objId);
			EWorldEditor.dropSelection();
			
			//EditorMenuBar.onWorldMenuItemSelect(0, "Drop to Cursor(Snap)");
		}
		else
		{
			Creator.removeSelection(%obj);
			   
			EditorTree.clearSelection();
			EWorldEditor.clearSelection();
			EWorldEditor.selectObject(%objId);
			EWorldEditor.dropSelection();
			
			useSnap($UseSnap);
			mouseCtrl.setDropObj(%obj,%objId);
		}
   }
	else
   {	    
		// drop it from the editor - only SceneObjects can be selected...   
		Creator.removeSelection(%obj);
		   
		EditorTree.clearSelection();
		EWorldEditor.clearSelection();
		EWorldEditor.selectObject(%objId);
		EWorldEditor.dropSelection();
   }
   //===================================================[/b]   
}

Tools\missionEditor\scripts\snappingTool.cs
Insert the below 2 functions into the snappingTool.cs script given in the resource:
function snapEditUseSnapToggle()
{
	if ($UseSnap == true)
	{
		$UseSnap = false;
		EditorGui.worldMenu.checkItem(20, false);
	}
	else
	{
		$UseSnap = true;
		EditorGui.worldMenu.checkItem(20, true);
	}
}

function snapEditFollowNormToggle()
{
	if ($FollowNorm == true)
	{
		$FollowNorm = false;
		//EditorGui.worldMenu.checkRadioItem(21, 22, 21);
		EditorGui.worldMenu.checkItem(21, false);
	}
	else
	{
		$FollowNorm = true;
		//EditorGui.worldMenu.checkRadioItem(21, 22, 22);
		EditorGui.worldMenu.checkItem(21, true);
	}
}

Insert the bold line below into the "mouseCtrl::on3DRightMouseDown" function:
function mouseCtrl::on3DRightMouseDown(%this,%mods,%projVec,%cPos,%mPos)
{
	%this.dropObj.delete();		
	%this.setVisible(false);
	[b]EWorldEditor.clearSelection(); //Clears selection to prevent crash on next delete[/b]
}
#7
12/02/2008 (9:42 pm)
@Dante: Hey and thanks for your interest and support!! I had actually ported it over a while back, but forgot to make notes as I was doing it. As a result, I was sort of dreading having to go through it again and figure out how I did it. You were just it time!!

As far as the lighting fix not working, all you need to do is open up worldEditor.cpp, scroll down to the updateClientTransforms console method and change it to this:

ConsoleMethod( WorldEditor, updateClientTransforms, void, 2, 2, "")
{
	object->updateClientTransforms(object->mSelected);
}

As far as I know, that should work. This command is called at the bottom of the function mouseCtrl::on3DMouseMove(%this,%mods,%projVec,%cPos,%mPos) function in snappingTool.cs. So, if nothing happens, make sure that line is uncommented.

By the way, I did a bit more work, and added a "Use random rotation" option. It randomly rotates the object you're placing along the relative z-axis. This means you no longer have to worry about objects, such as trees, looking uniform. I'll try posting the update code here sometime.

Thanks again,
Daniel