NewBuilding.getTransform giving bad values
by Justin Tolchin · in RTS Starter Kit · 02/11/2005 (6:14 pm) · 18 replies
Hi all,
I've been modifying the GuiRTSTSCtrl code to allow the player to rotate a building as it's being placed on the terrain. The problem is that when they place the building the building transform is getting sent to the server with invalid values. From what I can tell (I'm new to the RTS kit) the logic flow goes like this:
1) player clicks on "build" icon
2) startPlaceBuilding is called in client/scripts/buildings.cs. This creates the "$NewBuilding" object.
3) $NewBuilding is passed to PlayGui.startBuildingPlacement
4) player clicks on terrain to place/rotate building. After releasing the button, I call the GuiRTSTSCtrl::placeBuilding (in C++) which calls:
Con::executef(this, 1, "placeBuilding");
which calls the GuiRTSTSCtrl::placeBuilding function in client/scripts/buildings.cs.
5) That code calls:
echo("Client sending building notify!" SPC $NewBuilding.getTransform() SPC $NewBuilding.thedatablock);
commandToServer('PlaceBuilding', "LOCAL", $NewBuilding.getTransform(),
$NewBuilding.thedatablock);
This is what I see in the console.log:
startPlaceBuilding--building name is (barracks) building index is (1)
setting building rotation to true
placing building
Client sending building notify! -11.6442 -62.9389 401.5 0 0 0 1.5708 barracksBlock
I don't know why the axis of rotation is 0 0 0 and the amount of rotation is 1.5708 (90 degrees). Those are definitely not values I'm setting and when I look at the transform matrix in the debugger just before the Con::execute call in step 4 I see what appear to be valid rotation values.
Can anyone tell me why this info is getting lost?
Thanks!
I've been modifying the GuiRTSTSCtrl code to allow the player to rotate a building as it's being placed on the terrain. The problem is that when they place the building the building transform is getting sent to the server with invalid values. From what I can tell (I'm new to the RTS kit) the logic flow goes like this:
1) player clicks on "build" icon
2) startPlaceBuilding is called in client/scripts/buildings.cs. This creates the "$NewBuilding" object.
3) $NewBuilding is passed to PlayGui.startBuildingPlacement
4) player clicks on terrain to place/rotate building. After releasing the button, I call the GuiRTSTSCtrl::placeBuilding (in C++) which calls:
Con::executef(this, 1, "placeBuilding");
which calls the GuiRTSTSCtrl::placeBuilding function in client/scripts/buildings.cs.
5) That code calls:
echo("Client sending building notify!" SPC $NewBuilding.getTransform() SPC $NewBuilding.thedatablock);
commandToServer('PlaceBuilding', "LOCAL", $NewBuilding.getTransform(),
$NewBuilding.thedatablock);
This is what I see in the console.log:
startPlaceBuilding--building name is (barracks) building index is (1)
setting building rotation to true
placing building
Client sending building notify! -11.6442 -62.9389 401.5 0 0 0 1.5708 barracksBlock
I don't know why the axis of rotation is 0 0 0 and the amount of rotation is 1.5708 (90 degrees). Those are definitely not values I'm setting and when I look at the transform matrix in the debugger just before the Con::execute call in step 4 I see what appear to be valid rotation values.
Can anyone tell me why this info is getting lost?
Thanks!
#2
Seriously, I just wanted to say how great this community is. I would probably still be trying to figure out how to change the startup screen if it weren't for all the info in GG forums. Ha.
Thanks for this tid-bit, I was just getting ready to work on this.
02/12/2005 (1:38 am)
Ha, I must be getting better at this. I actually comprehended what you just said. Seriously, I just wanted to say how great this community is. I would probably still be trying to figure out how to change the startup screen if it weren't for all the info in GG forums. Ha.
Thanks for this tid-bit, I was just getting ready to work on this.
#3
Thanks for the info but (to my limited understanding) the problem is on the *client*, not the server. If you look at step 5 above, that's where the client is sending data over to the server. At that point, the client script output (from the "placeBuilding" method) is:
Client sending building notify! -11.6442 -62.9389 401.5 0 0 0 1.5708 barracksBlock
So the problem isn't that the server is overriding the rotation values, it's that the client isn't passing them, and I don't understand why. All the client script does is pass "$NewBuilding.getTransform()". Is there actually some intermediate step between the call:
Con::executef(this, 1, "placeBuilding");
(which is the last place I can see the values in the C++ debugger) and the actual "placeBuilding" method in "/client/scripts/buildings.cs"? That's the only way I could see the values getting munged, unless I'm really missing something obvious here.
This brings up a larger issue, by the way. What do people around here normally use to debug their apps? By that I mean, I can use Visual C++ to debug the C++ end of things, which is what I'm doing, but as soon as the script gets called I have no way of knowing what's going on. I tried using TIDE at one point but never really got it working properly, and that seems to preclude using Visual C++ to debug the C++ side. Is there a good document on debugging the entire running process somewhere? Or do people just pepper their script code with lots of "echo" calls? :-)
Thanks!
02/12/2005 (1:33 pm)
Hi Stephen,Thanks for the info but (to my limited understanding) the problem is on the *client*, not the server. If you look at step 5 above, that's where the client is sending data over to the server. At that point, the client script output (from the "placeBuilding" method) is:
Client sending building notify! -11.6442 -62.9389 401.5 0 0 0 1.5708 barracksBlock
So the problem isn't that the server is overriding the rotation values, it's that the client isn't passing them, and I don't understand why. All the client script does is pass "$NewBuilding.getTransform()". Is there actually some intermediate step between the call:
Con::executef(this, 1, "placeBuilding");
(which is the last place I can see the values in the C++ debugger) and the actual "placeBuilding" method in "/client/scripts/buildings.cs"? That's the only way I could see the values getting munged, unless I'm really missing something obvious here.
This brings up a larger issue, by the way. What do people around here normally use to debug their apps? By that I mean, I can use Visual C++ to debug the C++ end of things, which is what I'm doing, but as soon as the script gets called I have no way of knowing what's going on. I tried using TIDE at one point but never really got it working properly, and that seems to preclude using Visual C++ to debug the C++ side. Is there a good document on debugging the entire running process somewhere? Or do people just pepper their script code with lots of "echo" calls? :-)
Thanks!
#4
Regarding your second question, it would be most useful if you show us the script around the placeBuilding call, and you'll want to check throughout the guiRTSTSCtrl.cc for any time the rotation itself is being modified--it's most likely being zero'ed out (or at least initialized) somewhere, and that's why your rotations aren't being tracked.
Of course, you haven't mentioned it, but you do need to write a couple of methods in the source to handle the user input and set the rotation.
02/12/2005 (1:45 pm)
To answer your second question first, I personally just use echo() extensively, and place them in every function/script in a particular sequence when I'm trying to debug things.Regarding your second question, it would be most useful if you show us the script around the placeBuilding call, and you'll want to check throughout the guiRTSTSCtrl.cc for any time the rotation itself is being modified--it's most likely being zero'ed out (or at least initialized) somewhere, and that's why your rotations aren't being tracked.
Of course, you haven't mentioned it, but you do need to write a couple of methods in the source to handle the user input and set the rotation.
#5
Ok, I'll try to describe the code changes I made (they're fairly minor), starting with the C++ side. As I'm sure you know, there's an "mPlacingBuilding" boolean in guiRTSTSCtrl.cc which is used to determine when the client's cursor is the "ghost" version of the building that they are trying to place, and is basically used to indicate when the client is in building placement "mode". I added a boolean called "mRotatingBuilding" which is set when the initial left-button click occurs, which tells me that at this point I don't want to move the building cursor around when the mouse moves, I just want it to rotate. So the last section of the GuiRTSTSCtrl::on3DMouseDown method looks like this:
// If we are placing buildings, this is where it happens.
if (mPlacingBuilding)
{
Con::warnf("setting building rotation to true");
// after the initial mouse-click to place the building,
// we go into "rotating" mode.
mRotatingBuilding = true;
}
At that point, both booleans are set to true. Now if the user moves the mouse around with the button still clicked, I update the rotation value like this (in GuiRTSTSCtrl::on3DMouseDragged):
if(mRotatingBuilding)
{
// We need to make the building "face" the cursor (even though the cursor
// is invisible at this point) like a billboard.
// First we get the "look" vector (the vector from the cursor to the build position).
Point3F bldPos = mNewBuilding->getPosition();
bldPos.z = 0.0f; // ignore the Z position
CollisionInfo info;
collide(event, info);
Point3F mousePos(info.pos.x, info.pos.y, 0.0f);
Point3F look = bldPos - mousePos;
look.normalizeSafe();
// Next take the crossproduct of the up and look vector to get the right vector.
Point3F right = mCross(Point3F(0.0f, 0.0f, 1.0f), look);
// Now get the "real" up vector from the crossproduct of look and right.
Point3F up = mCross(look, right);
// create the new transform
MatrixF newRot = mNewBuilding->getTransform();
newRot.setColumn(0, right);
newRot.setColumn(1, look);
newRot.setColumn(2, up);
// set the building position into the transform
newRot.setColumn(3, mNewBuilding->getPosition());
// update the transform
((ShapeBase*)mNewBuilding)->setTransform(newRot);
return;
}
Side note: I hope that code makes sense. I stink at 3D math and I got that code from a site that described how to do billboarding. It seems to work but one thing I was really confused about is whether I should be using setColumn or setRow to set the transform matrix. Strangely it seemed to work correctly either way I did it(!?!?).
Now when the left-button is released, we need to invoke the placeBuilding method. Here is the code from the end of the GuiRTSTSCtrl::on3DMouseUp method.
if(mRotatingBuilding)
{
Con::warnf("placing building");
mRotatingBuilding = false;
placeBuilding();
}
That "placeBuilding" method looks like this:
void GuiRTSTSCtrl::placeBuilding()
{
Con::warnf("in GuiRTSTSCtrl::placeBuilding");
if (!mPlacingBuilding || !mNewBuilding)
return;
Con::executef(this, 1, "placeBuilding");
mNewBuilding = NULL;
mPlacingBuilding = false;
}
continued next post...
02/14/2005 (10:09 am)
Hi Stephen,Ok, I'll try to describe the code changes I made (they're fairly minor), starting with the C++ side. As I'm sure you know, there's an "mPlacingBuilding" boolean in guiRTSTSCtrl.cc which is used to determine when the client's cursor is the "ghost" version of the building that they are trying to place, and is basically used to indicate when the client is in building placement "mode". I added a boolean called "mRotatingBuilding" which is set when the initial left-button click occurs, which tells me that at this point I don't want to move the building cursor around when the mouse moves, I just want it to rotate. So the last section of the GuiRTSTSCtrl::on3DMouseDown method looks like this:
// If we are placing buildings, this is where it happens.
if (mPlacingBuilding)
{
Con::warnf("setting building rotation to true");
// after the initial mouse-click to place the building,
// we go into "rotating" mode.
mRotatingBuilding = true;
}
At that point, both booleans are set to true. Now if the user moves the mouse around with the button still clicked, I update the rotation value like this (in GuiRTSTSCtrl::on3DMouseDragged):
if(mRotatingBuilding)
{
// We need to make the building "face" the cursor (even though the cursor
// is invisible at this point) like a billboard.
// First we get the "look" vector (the vector from the cursor to the build position).
Point3F bldPos = mNewBuilding->getPosition();
bldPos.z = 0.0f; // ignore the Z position
CollisionInfo info;
collide(event, info);
Point3F mousePos(info.pos.x, info.pos.y, 0.0f);
Point3F look = bldPos - mousePos;
look.normalizeSafe();
// Next take the crossproduct of the up and look vector to get the right vector.
Point3F right = mCross(Point3F(0.0f, 0.0f, 1.0f), look);
// Now get the "real" up vector from the crossproduct of look and right.
Point3F up = mCross(look, right);
// create the new transform
MatrixF newRot = mNewBuilding->getTransform();
newRot.setColumn(0, right);
newRot.setColumn(1, look);
newRot.setColumn(2, up);
// set the building position into the transform
newRot.setColumn(3, mNewBuilding->getPosition());
// update the transform
((ShapeBase*)mNewBuilding)->setTransform(newRot);
return;
}
Side note: I hope that code makes sense. I stink at 3D math and I got that code from a site that described how to do billboarding. It seems to work but one thing I was really confused about is whether I should be using setColumn or setRow to set the transform matrix. Strangely it seemed to work correctly either way I did it(!?!?).
Now when the left-button is released, we need to invoke the placeBuilding method. Here is the code from the end of the GuiRTSTSCtrl::on3DMouseUp method.
if(mRotatingBuilding)
{
Con::warnf("placing building");
mRotatingBuilding = false;
placeBuilding();
}
That "placeBuilding" method looks like this:
void GuiRTSTSCtrl::placeBuilding()
{
Con::warnf("in GuiRTSTSCtrl::placeBuilding");
if (!mPlacingBuilding || !mNewBuilding)
return;
Con::executef(this, 1, "placeBuilding");
mNewBuilding = NULL;
mPlacingBuilding = false;
}
continued next post...
#6
function GuiRTSTSCtrl::placeBuilding(%this)
{
// right now, buildings can be placed by peons basically anywhere, regardless of
// where the peon is. A good task here would be to track where the selected building position
// is, and make sure that the peon assigned to do the build moves to that spot before
// we actually cause the building to be placed. Not currently implemented
echo("Client sending building notify!" SPC $NewBuilding.getTransform() SPC $NewBuilding.thedatablock);
commandToServer('PlaceBuilding', "LOCAL", $NewBuilding.getTransform(),
$NewBuilding.thedatablock);
$NewBuilding.delete();
}
The output from the above "echo" command shows the wrong rotation value, but I don't know of any place where it would have been reset. Is there anything going on "behind the scenes" in all of this where the values might get reset, or some step that I'm missing?
I guess I'm also assuming that the "mNewBuilding" object in the C++ code is the *exact same object* being referred to as $NewBuilding in the script code. Is that true? Or is it being proxied in some way and I need to update the "real" object?
Thanks!
02/14/2005 (10:10 am)
My understanding is (and please correct me if I'm wrong), that the above Con::executef call is directly calling the "placeBuilding" script function in client/scripts/buildings.cs. That script looks like this (again, this is from the Villager RTS mod - I don't think I modified it at all):function GuiRTSTSCtrl::placeBuilding(%this)
{
// right now, buildings can be placed by peons basically anywhere, regardless of
// where the peon is. A good task here would be to track where the selected building position
// is, and make sure that the peon assigned to do the build moves to that spot before
// we actually cause the building to be placed. Not currently implemented
echo("Client sending building notify!" SPC $NewBuilding.getTransform() SPC $NewBuilding.thedatablock);
commandToServer('PlaceBuilding', "LOCAL", $NewBuilding.getTransform(),
$NewBuilding.thedatablock);
$NewBuilding.delete();
}
The output from the above "echo" command shows the wrong rotation value, but I don't know of any place where it would have been reset. Is there anything going on "behind the scenes" in all of this where the values might get reset, or some step that I'm missing?
I guess I'm also assuming that the "mNewBuilding" object in the C++ code is the *exact same object* being referred to as $NewBuilding in the script code. Is that true? Or is it being proxied in some way and I need to update the "real" object?
Thanks!
#7
However, if the building marker itself isn't rotatating visually, then the issue is in your input primitive (how the mouse events are captured), or the rotation calculation and setTransform() in the source code.
I'm not an expert myself regarding 3-D math, so I can't really provide any clues to the way you implemented it (I did it pretty differently, but your overall structure does look ok).
EDIT: I did see in my copy of the client building.cs script in startPlacingBuilding() a line that I commented out:
// $NewBuilding.setTransform( "0 0 0 0 0 1 3.14159" ); we rotate buildings now, this isn't needed
I don't think this is your issue, but it won't hurt anything to comment it out and check--I honestly don't remember why I had to comment it out, or if it was simply done as a part of cleanup.
02/14/2005 (10:23 am)
Tell me this: while the mouse is held down, do you see the building marker rotating on screen while you move the mouse about? If so, then the issue is handing the transform back to the script (somehow, that doesn't make much sense right now).However, if the building marker itself isn't rotatating visually, then the issue is in your input primitive (how the mouse events are captured), or the rotation calculation and setTransform() in the source code.
I'm not an expert myself regarding 3-D math, so I can't really provide any clues to the way you implemented it (I did it pretty differently, but your overall structure does look ok).
EDIT: I did see in my copy of the client building.cs script in startPlacingBuilding() a line that I commented out:
// $NewBuilding.setTransform( "0 0 0 0 0 1 3.14159" ); we rotate buildings now, this isn't needed
I don't think this is your issue, but it won't hurt anything to comment it out and check--I honestly don't remember why I had to comment it out, or if it was simply done as a part of cleanup.
#8
I commented out the line you mentioned but it didn't have any effect.
Can you describe generally how you accomplished your building rotation? Maybe it will give me a clue where to look.
Thanks!
02/14/2005 (12:25 pm)
Yes, the building marker rotates fine.I commented out the line you mentioned but it didn't have any effect.
Can you describe generally how you accomplished your building rotation? Maybe it will give me a clue where to look.
Thanks!
#9
Since you are showing visual rotation on your building marker, you know that the math is at least operational, as is the setTransform() on the marker, so you want to track down the reason why the rotation portion of the transform is going away. It's possible there is a setTransform() somewhere in code or script that ignores rotation (due to the design assumption), which is causing your rotation info to go away.
Could you show me your script and code versions of startBuildingPlacement()?
02/14/2005 (6:19 pm)
The only thing I can suggest right now is for you to trace through the entire callback chain from rotating the building marker to sending the transform values to the server, and figure out where it changes to your constant value that way. Since you are showing visual rotation on your building marker, you know that the math is at least operational, as is the setTransform() on the marker, so you want to track down the reason why the rotation portion of the transform is going away. It's possible there is a setTransform() somewhere in code or script that ignores rotation (due to the design assumption), which is causing your rotation info to go away.
Could you show me your script and code versions of startBuildingPlacement()?
#10
Here's the startPlaceBuilding function where it all begins:
function startPlaceBuilding(%buildingName)
{
// Building is already being placed
if( isObject( $NewBuilding ) )
return;
%buildingIndex = getBuildingTypeIDFromUnitName(%buildingName);
echo("startPlaceBuilding--building name is (" @ %buildingName @ ") building index is (" @ %buildingIndex @ ")");
$NewBuilding = new RTSBuildingMarker()
{
scale = $BD_Marker_scale[0,%buildingIndex];
thedatablock = $BD_Marker_thedatablock[0,%buildingIndex];
shapeName = $BD_Marker_shapeName[0,%buildingIndex];
};
// Starting position = (0,0,0)
// $NewBuilding.setTransform( "0 0 0 0 0 1 3.14159" );
$NewBuilding.setOverrideTexture("test.RTS/data/shapes/barracks/building_green");
PlayGui.startBuildingPlacement($NewBuilding);
}
And here's the startBuildingPlacement code:
void GuiRTSTSCtrl::startBuildingPlacement(RTSBuildingMarker* building)
{
//can only build one building at a time...
if (mPlacingBuilding)
return;
mPlacingBuilding = true;
mRotatingBuilding = false;
mNewBuilding = building;
mNewBuilding->setTransVal(0.5f);
}
Other than the addition of the mRotatingBuilding flag it's unchanged.
Phil
02/15/2005 (10:35 am)
Stephen,Here's the startPlaceBuilding function where it all begins:
function startPlaceBuilding(%buildingName)
{
// Building is already being placed
if( isObject( $NewBuilding ) )
return;
%buildingIndex = getBuildingTypeIDFromUnitName(%buildingName);
echo("startPlaceBuilding--building name is (" @ %buildingName @ ") building index is (" @ %buildingIndex @ ")");
$NewBuilding = new RTSBuildingMarker()
{
scale = $BD_Marker_scale[0,%buildingIndex];
thedatablock = $BD_Marker_thedatablock[0,%buildingIndex];
shapeName = $BD_Marker_shapeName[0,%buildingIndex];
};
// Starting position = (0,0,0)
// $NewBuilding.setTransform( "0 0 0 0 0 1 3.14159" );
$NewBuilding.setOverrideTexture("test.RTS/data/shapes/barracks/building_green");
PlayGui.startBuildingPlacement($NewBuilding);
}
And here's the startBuildingPlacement code:
void GuiRTSTSCtrl::startBuildingPlacement(RTSBuildingMarker* building)
{
//can only build one building at a time...
if (mPlacingBuilding)
return;
mPlacingBuilding = true;
mRotatingBuilding = false;
mNewBuilding = building;
mNewBuilding->setTransVal(0.5f);
}
Other than the addition of the mRotatingBuilding flag it's unchanged.
Phil
#11
There has to be something, somewhere, that is negating your modifications to the transform--and it should almost certainly be in the "up click" handoff back to script.
Can you send me an email so we can figure out what's going on off the forums, and then come back with a solution once we figure it out? My email is in my profile (left click on my name).
If you don't have a problem with it, attach your building.cs and your guiRTSTSCtrl.cc/.h files and I'll be able to take a more accurate look.
02/15/2005 (4:23 pm)
I have to be honest, I'm kind of stumped--I can't see any glaring errors, and a couple of desk check run-throughs (in my head) look pretty ok.There has to be something, somewhere, that is negating your modifications to the transform--and it should almost certainly be in the "up click" handoff back to script.
Can you send me an email so we can figure out what's going on off the forums, and then come back with a solution once we figure it out? My email is in my profile (left click on my name).
If you don't have a problem with it, attach your building.cs and your guiRTSTSCtrl.cc/.h files and I'll be able to take a more accurate look.
#12
02/15/2005 (5:34 pm)
I'm sure its my unfamaliarity with the RTS SK, but its unclear to me reading through the code above what takes place on the server and what takes place on the client. It almost sounds like an issue where the ghost's aren't getting the right information though. Is this an object that split server and client? Perhaps the pack/unpack is missing some info.
#13
The mPlacingBuilding boolean has the guiRTSTSCtrl.cc code interpret the mouse events in the "placing building" mode, which basically means that you don't get drag-selection box rendering, and the RTSBuildingMarker's position is tied to the mouse point. Once the left mouse button is clicked, the RTSBuildingMarker is dropped down to the terrain height map, and then the event is passed back to the script. The script then grabs the current transformation of the building marker, destroys the marker, and transmits the appropriate information (building datablock name, final building position and rotation via the transform, and any other data needed) to the server script via cmdToServer.
The server then creates a "real" server side ghosted RTSBuilding object, sets the transform based on the information the placing client gave, and sets it to be scoped appropriately (which is done via the visManager, since the RTSBuilding is a sub-class of RTSUnit).
Now that a real server building object exists, it is propagated to the "has in scope" clients via standard TGE networking.
02/15/2005 (5:44 pm)
@John: For this particular example, everything is done client side. The way buildings are placed is that you create a client only object called an RTSBuildingMarker, which is a .dts using the shape of your building from the .dts, but overrides the texture with a light green image to indicate it's not yet a "real" building.The mPlacingBuilding boolean has the guiRTSTSCtrl.cc code interpret the mouse events in the "placing building" mode, which basically means that you don't get drag-selection box rendering, and the RTSBuildingMarker's position is tied to the mouse point. Once the left mouse button is clicked, the RTSBuildingMarker is dropped down to the terrain height map, and then the event is passed back to the script. The script then grabs the current transformation of the building marker, destroys the marker, and transmits the appropriate information (building datablock name, final building position and rotation via the transform, and any other data needed) to the server script via cmdToServer.
The server then creates a "real" server side ghosted RTSBuilding object, sets the transform based on the information the placing client gave, and sets it to be scoped appropriately (which is done via the visManager, since the RTSBuilding is a sub-class of RTSUnit).
Now that a real server building object exists, it is propagated to the "has in scope" clients via standard TGE networking.
#14
02/15/2005 (6:47 pm)
Maybe i'm missing it somewhere in here.. but just to clarify.. Is there some point in the flow where you can verify that the proper matrix/transform exists for $NewBuilding? At what point is the last point you can verify this and it shows correct?
#15
All he seems to see right now is the transform right before the cmdToServer(), which is showing at the script level a transform that is missing the rotation values.
02/15/2005 (7:12 pm)
@John: that was basically the last question I had asked--I think that he needs to sprinkle debug lines (echo in script, Con::printf() in code) displaying the transform values in all his called methods so we can find out exactly where the break is. If/when he sends me the code he's using, it's what I plan on doing!All he seems to see right now is the transform right before the cmdToServer(), which is showing at the script level a transform that is missing the rotation values.
#16
@John: yes, all of this is happening client-side. As near as I can tell in the debugger there are valid values for the rotation transform before the call to the client-side script, and pretty much all the script does is turn around and fire the transform off to the server, but by that point it's been reset. :-(
02/16/2005 (10:05 am)
@Stephen: thanks for the offer. I'll package up the files and send them asap. Let me know if I leave out anything you need. I'm still struggling with figuring out all the interrelations between files in Torque/RTS. :-)@John: yes, all of this is happening client-side. As near as I can tell in the debugger there are valid values for the rotation transform before the call to the client-side script, and pretty much all the script does is turn around and fire the transform off to the server, but by that point it's been reset. :-(
#17
Managed to get this (mostly) working. The problem was that the matrix I was generating always created a "bad" angle-axis object (represented by the AngAxisF class in C++), which is what the client script was using the send the transform over to the server. I don't understand the math well enough to explain it, but the way it worked out I always got an axis of 0 0 0 and an angle of 1.5708.
To solve the problem I needed to change around the order of the variables in the cross-product calls that generated the matrix columns.
The original code looked like this:
Point3F look = bldPos - mousePos;
look.normalizeSafe();
Point3F right = mCross(Point3F(0.0f, 0.0f, 1.0f), look);
Point3F up = mCross(look, right);
MatrixF newRot = mNewBuilding->getTransform();
newRot.setColumn(0, right);
newRot.setColumn(1, look);
newRot.setColumn(2, up);
The updated code looks like this:
Point3F look = bldPos - mousePos;
look.normalizeSafe();
Point3F right = mCross(look, Point3F(0.0f, 0.0f, 1.0f));
Point3F up = mCross(right, look);
MatrixF newRot = mNewBuilding->getTransform();
newRot.setColumn(0, right);
newRot.setColumn(1, look);
newRot.setColumn(2, up);
Stephen had also sent me some code to set the building rotation based on the mouse input. Essentially he created an AngAxisF object based on the mouse input, and then used that AngAxisF object to create a matrix (using the AngAxisF::setMatrix method). That also works fine.
There is still one strange problem. When you place the building, it fires the transform off to the server and the server validates the building "purchase" and then tells the client where to put the actual building. At that point I usually see the building placed slightly rotated from where I told it to be. Usually it rotates 5-10 degrees or so, but sometimes I've seen it rotated as much as 90 degrees from where I placed it. Stephen verified that he sees the same kind of thing with his code. It also seems to me there's a slight translation of the object as well.
So my question now is... how does the server actually tell the client where the new building is supposed to be? The "serverCmdPlaceBuilding" method in the server-side script does the following (among other things):
%b = new RTSBuilding()
{
datablock = %data;
scale = "1 1 1";
};
%b.setTransform( %transform );
%b.client = %conn;
%b.setTeam(%conn.getTeam());
%b.setControllingConnection(%conn);
%conn.buildings.add(%b);
So that "%b" object exists on the server, but what actually tells the client to create the "real" building that gets rendered? Or maybe the question is "how does %b get over to the client?". Is it one of the above lines, or does it happen somewhere else? I'd like to try and track down where the transform is getting out of sync.
Thanks!
02/22/2005 (12:24 pm)
Hi all,Managed to get this (mostly) working. The problem was that the matrix I was generating always created a "bad" angle-axis object (represented by the AngAxisF class in C++), which is what the client script was using the send the transform over to the server. I don't understand the math well enough to explain it, but the way it worked out I always got an axis of 0 0 0 and an angle of 1.5708.
To solve the problem I needed to change around the order of the variables in the cross-product calls that generated the matrix columns.
The original code looked like this:
Point3F look = bldPos - mousePos;
look.normalizeSafe();
Point3F right = mCross(Point3F(0.0f, 0.0f, 1.0f), look);
Point3F up = mCross(look, right);
MatrixF newRot = mNewBuilding->getTransform();
newRot.setColumn(0, right);
newRot.setColumn(1, look);
newRot.setColumn(2, up);
The updated code looks like this:
Point3F look = bldPos - mousePos;
look.normalizeSafe();
Point3F right = mCross(look, Point3F(0.0f, 0.0f, 1.0f));
Point3F up = mCross(right, look);
MatrixF newRot = mNewBuilding->getTransform();
newRot.setColumn(0, right);
newRot.setColumn(1, look);
newRot.setColumn(2, up);
Stephen had also sent me some code to set the building rotation based on the mouse input. Essentially he created an AngAxisF object based on the mouse input, and then used that AngAxisF object to create a matrix (using the AngAxisF::setMatrix method). That also works fine.
There is still one strange problem. When you place the building, it fires the transform off to the server and the server validates the building "purchase" and then tells the client where to put the actual building. At that point I usually see the building placed slightly rotated from where I told it to be. Usually it rotates 5-10 degrees or so, but sometimes I've seen it rotated as much as 90 degrees from where I placed it. Stephen verified that he sees the same kind of thing with his code. It also seems to me there's a slight translation of the object as well.
So my question now is... how does the server actually tell the client where the new building is supposed to be? The "serverCmdPlaceBuilding" method in the server-side script does the following (among other things):
%b = new RTSBuilding()
{
datablock = %data;
scale = "1 1 1";
};
%b.setTransform( %transform );
%b.client = %conn;
%b.setTeam(%conn.getTeam());
%b.setControllingConnection(%conn);
%conn.buildings.add(%b);
So that "%b" object exists on the server, but what actually tells the client to create the "real" building that gets rendered? Or maybe the question is "how does %b get over to the client?". Is it one of the above lines, or does it happen somewhere else? I'd like to try and track down where the transform is getting out of sync.
Thanks!
#18
Unfortunately, the answer to the main question (why the building doesn't seem to ever get the last user input given by the player) is one I've been trying to trace for months now, and never have figured out what the problem is!
02/22/2005 (3:33 pm)
The answer to your specific question about how the data gets back to the client is because the object is created on the server as a ghostable object, and then the net code transmits it to the appropriate clients that have the new building in scope. This is done via the pack/unpack methods in RTSUnit.cc.Unfortunately, the answer to the main question (why the building doesn't seem to ever get the last user input given by the player) is one I've been trying to trace for months now, and never have figured out what the problem is!
Torque 3D Owner Stephen Zepp
There is one important assumption/understanding that is critical to implementing it properly:
RTSBuildingMarker is client side only. Read that again--it's very important! It does appear from my "peers into your brain" estimation that you have this part down, but what is important is that you need to modify guiRTSTSCtrl.cc to handle all of your user inputs that control the rotation prior to actually sending the information to the server via the commandToServer('PlaceBuilding'...) call.
From memory, you'll also need to properly handle the full transform of your RTSBuildingMarker on the server side--I do remember the server side script assuming that there will be no rotation, and only paying attention to the positional portion of the transform that is sent.
If you keep in mind that up until the point where the server scripts actually "catch" the NewBuilding command, and instantiate the new building object it only exists on the client's exe that is placing the building, you should be able to keep things straight. The real building doesn't actually exist in the game world until the server scripts have captured the data from the building placement event, and more importantly, processed the entire transform--not just the x/y coordinates.