Problem on converting Object Selection code to pure C
by Kuju Manila · in Torque Game Engine · 10/21/2006 (12:01 am) · 9 replies
Some background
Hello, I've been trying to make Dave Myer's Object Selection code to be a new different control instead of totally transforming the default GameTSCtrl. To do this, I made a new class in the engine code named ArcadeTSCtrl (I'm planning to make the control behaved like it's a Time Crisis light gun shooting using mouse) and copied over GameTSCtrl and just renamed lots of things in the source file. The rest, I just followed the resource.
All was great!!! I now have a working ArcadeTSCtrl! I have confirmed it works by using it. But, I want to get rid of the TorqueScript-based console methods and console functions that I have to copy over my different mods to make it work. So I decided, to reimplement the TorqueScript methods to pure C++.
Here's what I've done so far:
Convert Dave Myer's playGui.cs onMouseDown code -> My C++
Hello, I've been trying to make Dave Myer's Object Selection code to be a new different control instead of totally transforming the default GameTSCtrl. To do this, I made a new class in the engine code named ArcadeTSCtrl (I'm planning to make the control behaved like it's a Time Crisis light gun shooting using mouse) and copied over GameTSCtrl and just renamed lots of things in the source file. The rest, I just followed the resource.
All was great!!! I now have a working ArcadeTSCtrl! I have confirmed it works by using it. But, I want to get rid of the TorqueScript-based console methods and console functions that I have to copy over my different mods to make it work. So I decided, to reimplement the TorqueScript methods to pure C++.
Here's what I've done so far:
Convert Dave Myer's playGui.cs onMouseDown code -> My C++
ConsoleMethod( ArcadeTSCtrl, onMouseDown, void, 2, 2, "()" ){
char* retBufferPos = Con::getReturnBuffer(256);
char* retBufferVec = Con::getReturnBuffer(256);
const Point3F &pos = object->getMouseCam3DPos();
const Point3F &vec = object->getMouseCam3DVec();
dSprintf(retBufferPos, 256, "\"%g %g %g\"", pos.x, pos.y, pos.z);
dSprintf(retBufferVec, 256, "\"%g %g %g\"", vec.x, vec.y, vec.z);
Con::printf("Doing select object...");
Con::evaluatef("commandToServer('SelectObject', %s, %s);", retBufferVec, retBufferPos);
}
#2
10/21/2006 (2:25 am)
.
#3
Just for the heck of it, I rewrote it even with your suggestion even I know its redundant. It's the same behavior. It can successfully call serverCmdSelectObject and even run through inside the contents (you've seen already in the code that I've been printf-ing like crazy), but the raycasting or computation doesn't happen as it used to. So, I'm pretty sure it's not the evaluatef anymore.
10/21/2006 (2:44 am)
Berserk, that issue is already resolved. It does go into the console function correctly. It displays "serverCmdSelectObject called" in the console. I already tried this with the \ or not. Both works. To be exact, adding \ to a single quote inside a double quote is already overkill according to C/C++ rules. Doing an escape sequence on a single quote is only necessary when you use enclosing ' ' for simple char data types (i.e. '\'' to capture the single quote as a char).Just for the heck of it, I rewrote it even with your suggestion even I know its redundant. It's the same behavior. It can successfully call serverCmdSelectObject and even run through inside the contents (you've seen already in the code that I've been printf-ing like crazy), but the raycasting or computation doesn't happen as it used to. So, I'm pretty sure it's not the evaluatef anymore.
#4
10/21/2006 (11:54 am)
.
#5
An update:
It think it might be a Torque Networking issue (I dont know the governing rules yet on networking since that's the last thing I would like to learn in Torque). I tried another experiment.
First, I updated the raycasting code above, that's the complete and correct one AFAIK (I've tested it and it works, more about it below).
Then, I changed back from using the C++ version I made to the TorqueScript of playGui.cs. So, onMouseDown will now be the TorqueScript version. Then, I just used the C++ version of my serverCmdSelectObject command.
With that setup, it works! The raycasting of my serverCmdSelectObject works. It outputs the same passed parameters and raycasts says true when it is supposed to be.
However, when I use the C++ version of my onMouseDown, using the same parameters, vector math works just fine but when arrive to the raycasting code, it ALWAYS says false when it isn't supposed to be. So, Im thinking is raycasting checking for something else that is networked?
I don't know now ;-P
10/21/2006 (8:24 pm)
Berserk: getForwardVector? No, I havent. What for? That's ok. Thank you for continued interest on trying to help me though.An update:
It think it might be a Torque Networking issue (I dont know the governing rules yet on networking since that's the last thing I would like to learn in Torque). I tried another experiment.
First, I updated the raycasting code above, that's the complete and correct one AFAIK (I've tested it and it works, more about it below).
Then, I changed back from using the C++ version I made to the TorqueScript of playGui.cs. So, onMouseDown will now be the TorqueScript version. Then, I just used the C++ version of my serverCmdSelectObject command.
With that setup, it works! The raycasting of my serverCmdSelectObject works. It outputs the same passed parameters and raycasts says true when it is supposed to be.
However, when I use the C++ version of my onMouseDown, using the same parameters, vector math works just fine but when arrive to the raycasting code, it ALWAYS says false when it isn't supposed to be. So, Im thinking is raycasting checking for something else that is networked?
I don't know now ;-P
#6
10/22/2006 (10:14 am)
.
#7
before submitting.
matahari -
first, try hardcoding all the parameters to the ray cast to make sure that it's getting the args you think.
i recommend casting from like 0,0,0 to 100,0,0, and making sure there's an object at 50,0,0.
second, are your search masks what you want ? those will only hit players, corpses, and items. - won't hit buildings, terrain, or i think general DTSs.
third, getLocalClientConnection() is only useful here in a single-player scenario. for an actual networked game you need to get the client from the params passed to the ConsoleFunction. ie, cast argv[1] (probably) to a GameConnection. something like gc = static_cast (dAtoi(argv[1])); i'm not sure it's argv[1], but it probably is.
fourth, are you sure you want to be doing these raycasts on the server ? that's how the resource is written, but really the raycast only needs to be done server-side if it's critical that it be authoritative. ie, if it's for something like shooting a gun, then yeah, it needs to be authoritative. if it's just object selection, it doesn't really need to be authoritative and you'd be better off doing it all client-side. .. oh, i see you write it's for a gun. n/m then.
good luck,
ooo
edit: ps, escaping single-quotes inside double-quotes in C++ will probably work, but is technically incorrect. ie, "he said \'foo\' and vanished" will probably work, but it's more correct to just use "he said 'foo' and vanished".
more edit: fifth, == true is doing nothing for you except making it more difficult to read the code.
sixth, your logic around the whole pExempt thing is broken - pExempt will *always* be NULL, even if connection->isFirstPerson() returns true. (which is itself also going awry; see "third" above)
seventh, it's probably not critical, but you should really make selectRange be a float instead of an int, since you're using it with floats. eg F32 selectRange = 200.0f.
eighth, i'd recommend getting rid of the whole scantarg mechanism and just using bRay. ie, replace this: if( dStrcmp(scanTarg,"0") != 0 ) with just if (bRay), and erase all the uses of scanTarg.
10/22/2006 (10:41 am)
.. crap, just wrote abunch of stuff and hit matahari -
first, try hardcoding all the parameters to the ray cast to make sure that it's getting the args you think.
i recommend casting from like 0,0,0 to 100,0,0, and making sure there's an object at 50,0,0.
second, are your search masks what you want ? those will only hit players, corpses, and items. - won't hit buildings, terrain, or i think general DTSs.
third, getLocalClientConnection() is only useful here in a single-player scenario. for an actual networked game you need to get the client from the params passed to the ConsoleFunction. ie, cast argv[1] (probably) to a GameConnection. something like gc = static_cast
fourth, are you sure you want to be doing these raycasts on the server ? that's how the resource is written, but really the raycast only needs to be done server-side if it's critical that it be authoritative. ie, if it's for something like shooting a gun, then yeah, it needs to be authoritative. if it's just object selection, it doesn't really need to be authoritative and you'd be better off doing it all client-side. .. oh, i see you write it's for a gun. n/m then.
good luck,
ooo
edit: ps, escaping single-quotes inside double-quotes in C++ will probably work, but is technically incorrect. ie, "he said \'foo\' and vanished" will probably work, but it's more correct to just use "he said 'foo' and vanished".
more edit: fifth, == true is doing nothing for you except making it more difficult to read the code.
sixth, your logic around the whole pExempt thing is broken - pExempt will *always* be NULL, even if connection->isFirstPerson() returns true. (which is itself also going awry; see "third" above)
seventh, it's probably not critical, but you should really make selectRange be a float instead of an int, since you're using it with floats. eg F32 selectRange = 200.0f.
eighth, i'd recommend getting rid of the whole scantarg mechanism and just using bRay. ie, replace this: if( dStrcmp(scanTarg,"0") != 0 ) with just if (bRay), and erase all the uses of scanTarg.
#8
Nice tip. Will do.
Yep, my search masks are correct.
I tried to do this, there's some still a data type conversion needed to be done. It made things more complicated. I did not do this.
Yep, I am aware of this. I just copied this portion of code from the original ContainerRayCast console method anyway.
No, it will not always be NULL. Sim::findobject will eventually write something to this.
I value good practice. Thanks for the tip.
Nice tip. Again, this is just copied over from the original console function. I still did your suggestion though, it IS much cleaner ;)
10/23/2006 (10:23 am)
Quote:
first, try hardcoding all the parameters to the ray cast to make sure that it's getting the args you think.
i recommend casting from like 0,0,0 to 100,0,0, and making sure there's an object at 50,0,0.
Nice tip. Will do.
Quote:
second, are your search masks what you want ? those will only hit players, corpses, and items. - won't hit buildings, terrain, or i think general DTSs.
Yep, my search masks are correct.
Quote:
third, getLocalClientConnection() is only useful here in a single-player scenario. for an actual networked game you need to get the client from the params passed to the ConsoleFunction. ie, cast argv[1] (probably) to a GameConnection. something like gc = static_cast(dAtoi(argv[1])); i'm not sure it's argv[1], but it probably is.
I tried to do this, there's some still a data type conversion needed to be done. It made things more complicated. I did not do this.
Quote:
fifth, == true is doing nothing for you except making it more difficult to read the code.
Yep, I am aware of this. I just copied this portion of code from the original ContainerRayCast console method anyway.
Quote:
sixth, your logic around the whole pExempt thing is broken - pExempt will *always* be NULL, even if connection->isFirstPerson() returns true. (which is itself also going awry; see "third" above)
No, it will not always be NULL. Sim::findobject will eventually write something to this.
Quote:
seventh, it's probably not critical, but you should really make selectRange be a float instead of an int, since you're using it with floats. eg F32 selectRange = 200.0f.
I value good practice. Thanks for the tip.
Quote:
eighth, i'd recommend getting rid of the whole scantarg mechanism and just using bRay. ie, replace this: if( dStrcmp(scanTarg,"0") != 0 ) with just if (bRay), and erase all the uses of scanTarg.
Nice tip. Again, this is just copied over from the original console function. I still did your suggestion though, it IS much cleaner ;)
#9
Boy, was I looking at the wrong places!!!
The use of returnBuffer is wrong!
Ok, from this:
to this:
You see, in the previous code the returnBufferVec == returnBufferPos. So, whatever was written last on the returnBuffer, will be the value of the returnBuffer. I am not aware of this design! Didn't find it written in the documentation either. Guess, I learned it the hard way. So, the fix was to combine everything into one single use of the returnBuffer.
Thank you everyone!
^_^ So happy it's finally fixed, yet so frustrated that it took me four days. d-_-b
10/23/2006 (10:32 am)
I FINALLY FIXED IT!Boy, was I looking at the wrong places!!!
The use of returnBuffer is wrong!
Ok, from this:
ConsoleMethod( ArcadeTSCtrl, onMouseDown, void, 2, 2, "()" ){
char* retBufferPos = Con::getReturnBuffer(256);
char* retBufferVec = Con::getReturnBuffer(256);
const Point3F &pos = object->getMouseCam3DPos();
const Point3F &vec = object->getMouseCam3DVec();
dSprintf(retBufferPos, 256, "\"%g %g %g\"", pos.x, pos.y, pos.z);
dSprintf(retBufferVec, 256, "\"%g %g %g\"", vec.x, vec.y, vec.z);
Con::printf("Doing select object...");
Con::evaluatef("commandToServer('SelectObject', %s, %s);", retBufferVec, retBufferPos);
}to this:
ConsoleMethod( ArcadeTSCtrl, onMouseDown, void, 2, 2, "()" ){
const Point3F vec = object->getMouseCam3DVec();
const Point3F pos = object->getMouseCam3DPos();
char* retBuffer = Con::getReturnBuffer(256);
dSprintf(retBuffer, 256, "\"%g %g %g\", \"%g %g %g\"", vec.x, vec.y, vec.z, pos.x, pos.y, pos.z);
Con::printf("Doing select object...");
Con::printf("Vec, Pos = %s", retBuffer);
Con::evaluatef("commandToServer('SelectObject', %s);", retBuffer);
}You see, in the previous code the returnBufferVec == returnBufferPos. So, whatever was written last on the returnBuffer, will be the value of the returnBuffer. I am not aware of this design! Didn't find it written in the documentation either. Guess, I learned it the hard way. So, the fix was to combine everything into one single use of the returnBuffer.
Thank you everyone!
^_^ So happy it's finally fixed, yet so frustrated that it took me four days. d-_-b
Kuju Manila
Default Studio Name
ConsoleFunction(serverCmdSelectObject, void, 4, 4, "()") { // Determine how far should the picking ray extend into the world? int selectRange = 200; Con::printf("serverCmdSelectObject called"); Con::printf("SelectObject: Passed mouseVec = %s cameraPoint = %s", argv[2], argv[3]); // scale mouseVec to the range the player is able to select with mouse VectorF mouseScaled(0,0,0); dSscanf(argv[2],"%g %g %g",&mouseScaled.x,&mouseScaled.y,&mouseScaled.z); mouseScaled *= selectRange; Con::printf("SelectObject: mouseScaled = %g %g %g", mouseScaled.x, mouseScaled.y, mouseScaled.z); // cameraPoint = the world position of the camera // rangeEnd = camera point + length of selected range VectorF cameraPoint(0,0,0); dSscanf(argv[3],"%g %g %g",&cameraPoint.x,&cameraPoint.y,&cameraPoint.z); VectorF rangeEnd = cameraPoint + mouseScaled; Con::printf("SelectObject: rangeEnd = %g %g %g", rangeEnd.x, rangeEnd.y, rangeEnd.z); // Search for anything that is selectable U32 searchMasks = PlayerObjectType | CorpseObjectType | ItemObjectType; Con::printf("SelectObject: searchMasks = %d", searchMasks); // Search for objects within the range that fit the masks above. If we are // in first person mode, we make sure player is not selectable by setting // fourth parameter (exempt from collisions) when calling ContainerRayCast RayInfo rinfo; S32 ret = 0; char * scanTarg; GameConnection * connection = GameConnection::getLocalClientConnection(); SceneObject* pExempt = NULL; if (connection->isFirstPerson()) { Con::printf("SelectObject: CASE 1."); U32 exemptId = dAtoi(connection->getControlObject()->getIdString()); Sim::findObject(exemptId, pExempt); } else Con::printf("SelectObject: CASE 2."); Con::printf("SelectObject: pExempt = %d", pExempt); if(pExempt) pExempt->disableCollision(); bool bRay = gServerContainer.castRay(cameraPoint, rangeEnd, searchMasks, &rinfo) == true; Con::printf("SelectObject: RayCast = %d", bRay); if (bRay) ret = rinfo.object->getId(); Con::printf("SelectObject: ID returned = %d", ret); if (pExempt) pExempt->enableCollision(); // add the hit position and normal? char * returnBuffer = Con::getReturnBuffer(256); if(ret) { dSprintf(returnBuffer, 256, "%d %g %g %g %g %g %g", ret, rinfo.point.x, rinfo.point.y, rinfo.point.z, rinfo.normal.x, rinfo.normal.y, rinfo.normal.z); } else { returnBuffer[0] = '0'; returnBuffer[1] = '[[60c234475cbfe]]'; } scanTarg = returnBuffer; Con::printf("SelectObject: returnBuffer = %d", returnBuffer); // a target in range was found so select it Con::printf("SelectObject: strcmp = %d", (dStrcmp(scanTarg,"0") != 0)); if( dStrcmp(scanTarg,"0") != 0 ) { Con::printf("SelectObject: Preparing to set selected object."); connection->setSelectedObject(static_cast<ShapeBase *>(rinfo.object)); } Con::printf("SelectObject: END"); }**I am aware I didn't reimplement CASE2 yet, since I'm still dealing with CASE1 anyway.
The Problem:
Surprisingly, right after I made my onMouseDown conversion to C++, when I tested it, serverCmdSelectObject doesn't work anymore whether it is declared via command.cs or using my C++ code. The raycast doesn't somehow work. I can confirm that serverCmdSelectObject is called because it does printf on the console. What is wrong?