Uberman Posted March 12, 2012 Share Posted March 12, 2012 I have the following simple (LEO-based) LE 2.5 program that compiles and functions properly under both VS2008 and VS2010: // ==================================================================== // This file was generated by LEBuilder // http://leadwerks.com/werkspace // ==================================================================== #include "leo.h" #include <string> #include <iostream> using namespace std; using namespace LEO; const int ScreenWidth = 800; const int ScreenHeight = 600; const char* MediaDir = "D:\\Leadwerks\\SDK"; const char* AppTitle = "TestLEO"; void ErrOut( const string& message ) { cerr << message << endl; } // -------------------------------------------- int main( int argc, char* argv[] ) { // Set graphics mode Engine engine(AppTitle,ScreenWidth,ScreenHeight); if( !engine.IsValid() ) { ErrOut( "Failed to set graphics mode."); return 1; } engine.AddAbstractPath( MediaDir ); // Create framework object and set it to a global object so other scripts can access it Framework fw; if( NULL == fw ) { ErrOut( "Failed to initialize engine." ); return 1; } // Set Lua framework object engine.SetObject( "fw", fw ); // Set Lua framework variable Lua lua; lua.PushObject( fw ); lua.SetGlobal( "fw" ); lua.Pop( 1 ); // Get framework main camera fw.main.GetCamera().SetPosition( Vec3(0,0,-5) ); Material material( "abstract::cobblestones.mat" ); Debug::SetDebugPhysics(); // Create collision cube BodyBox cube1_body(1); cube1_body.SetMass(1.0); cube1_body.SetFriction(1.0, 1.0); cube1_body.SetElasticity(0.2f); Cube cube1; cube1.Paint( material ); cube1.SetParent(cube1_body); cube1_body.SetPosition(Vec3(0, 5, 0)); BodyBox cube2_body(1); cube2_body.SetMass(1.0); cube2_body.SetFriction(1.0, 1.0); cube2_body.SetElasticity(0.2f); Cube cube2; cube2.Paint( material ); cube2.SetParent(cube2_body); cube2_body.SetPosition(Vec3(0.5, 10, 0.5)); // Create collision ground BodyBox ground_body(1); Cube ground; ground.Paint( material ); ground.SetParent(ground_body); ground_body.SetScale(Vec3(10.0f, 0.1f, 10.0f)); ground_body.SetPosition(Vec3(0, -1, 0)); // Turn on physics //TVec3 gravity = Vec3(0,-9.80665f*2,0); TVec3 gravity = Vec3(0,-2.0,0); World& back_world = fw.background.GetWorld(); back_world.SetCollisions(1, 1); back_world.SetGravity(gravity); World& main_world = fw.main.GetWorld(); main_world.SetCollisions(1, 1); main_world.SetGravity(gravity); World& trans_world = fw.transparency.GetWorld(); trans_world.SetCollisions(1, 1); trans_world.SetGravity(gravity); cube1_body.SetType(1); cube2_body.SetType(1); ground_body.SetType(1); // Add some light DirectionalLight light; light.SetRotation( Vec3(45) ); // Spin cube until user hits Escape while( !Keyboard::I****() && !engine.IsTerminated() ) { if( !engine.IsSuspended() ) { fw.Update(); fw.Render(); engine.Flip( 0 ); } } return engine.Free(); } This works as I expect, with both cubes falling and colliding properly. I now would like to detect a collision for each Cube body (to emit a sound, for example). I add the following module-level function to the code: int cubeCollision(TEntity e1, TEntity e2) { return 0; } And then I enable collision callbacks for each Cube toward the bottom of the main() body: cube1_body.SetType(1); cube1_body.SetCallback((BP)cubeCollision, ENTITYCALLBACK_COLLISION); cube2_body.SetType(1); cube2_body.SetCallback((BP)cubeCollision, ENTITYCALLBACK_COLLISION); ground_body.SetType(1); When the first impact occurs, and the callback is triggered, I'm getting a run-time error (the same in both VS2008 and VS2010) that states: Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention. Any ideas about what I might be doing wrong here? Quote Link to comment Share on other sites More sharing options...
Canardia Posted March 12, 2012 Share Posted March 12, 2012 Runtime errors comes from missing parameters in your callback function. It must have also force and userdata parameter. Quote ■ Ryzen 9 ■ RX 6800M ■ 16GB ■ XF8 ■ Windows 11 ■ ■ Ultra ■ LE 2.5 ■ 3DWS 5.6 ■ Reaper ■ C/C++ ■ C# ■ Fortran 2008 ■ Story ■ ■ Homepage: https://canardia.com ■ Link to comment Share on other sites More sharing options...
Uberman Posted March 12, 2012 Author Share Posted March 12, 2012 Thanks for the reply, Metatron. Can you point me to where that is documented? The Collision Callback Example doesn't indicate that additional parameters are required. Quote Link to comment Share on other sites More sharing options...
Uberman Posted March 12, 2012 Author Share Posted March 12, 2012 I found this in leobase.h: typedef void (*CollisionCallback)(TEntity ent0, TEntity ent1, const TVec3& pos, const TVec3& normal, flt speed); But setting the callback to this signature still generates the runtime issue with VS2008 and VS2010. Quote Link to comment Share on other sites More sharing options...
Canardia Posted March 12, 2012 Share Posted March 12, 2012 I had no problems with it so far, but maybe I added also a _stdcall to my callback function. I also use Code::Blocks, and Visual Studio 2012 to make the Microsoft fanatics happy Quote ■ Ryzen 9 ■ RX 6800M ■ 16GB ■ XF8 ■ Windows 11 ■ ■ Ultra ■ LE 2.5 ■ 3DWS 5.6 ■ Reaper ■ C/C++ ■ C# ■ Fortran 2008 ■ Story ■ ■ Homepage: https://canardia.com ■ Link to comment Share on other sites More sharing options...
Uberman Posted March 12, 2012 Author Share Posted March 12, 2012 I have added both __stdcall and __cdecl to the callback function, and no joy. I've also tried the straight C++ code (non-LEO) from the aforementioned tutorial in a fresh VS2008-based project generated by LEBuilder, added the callback function: void __stdcall cubeCollision(TEntity e1, TEntity e2, const TVec3& pos, const TVec3& normal, flt speed) {} or: void __cdecl cubeCollision(TEntity e1, TEntity e2, const TVec3& pos, const TVec3& normal, flt speed) {} set the callback: SetEntityCallback(body, (BP)cubeCollision, ENTITYCALLBACK_COLLISION); And I still get the same runtime error with VS2008 and VS2010. I'm beginning to wonder if there's some compiler setting (like structure alignment) that the LEBuilder program is not adding when it generates the Visual Studio projects. Quote Link to comment Share on other sites More sharing options...
Canardia Posted March 12, 2012 Share Posted March 12, 2012 I will make a short demo. Damn, now I have already 2 promises to fulfill Quote ■ Ryzen 9 ■ RX 6800M ■ 16GB ■ XF8 ■ Windows 11 ■ ■ Ultra ■ LE 2.5 ■ 3DWS 5.6 ■ Reaper ■ C/C++ ■ C# ■ Fortran 2008 ■ Story ■ ■ Homepage: https://canardia.com ■ Link to comment Share on other sites More sharing options...
Uberman Posted March 12, 2012 Author Share Posted March 12, 2012 I appreciate the aid. This is really stumping me. Oh, and I verified: My VS2010 is SP1. I wasn't sure. Quote Link to comment Share on other sites More sharing options...
tjheldna Posted March 13, 2012 Share Posted March 13, 2012 I've just been playing around with this. I had pretty much the same problem with the same error. The callback must be outside of a class I've noticed. I am by no means a master at this, however i have it working with collecting coins in my game. Some of the examples around are a little off, possibly out dated, i dont know. just adjust the names to suit. Here goes... .cpp file //Callbacks int _stdcall CoinCollision( TEntity entity0, TEntity entity1, byte* position, byte* normal, byte* force, flt speed ) { Inventory::CoinItem *item = reinterpret_cast<Inventory::Coinitem*>(GetEntityUserData(entity0)); EntityType(entity0,5); TSound sound = LoadSound("Sound/Item/coinPickup.wav"); EmitSound(entity0, sound, 10.0F, 1.0F, SOURCE_EAX ) ; Controller *controller = reinterpret_cast<controller*>(GetEntityUserData(entity1)); //etc etc.... return 0; } .h file int _stdcall CoinCollision(TEntity entity0, TEntity entity1, byte* position, byte* normal, byte* force, flt speed ); Call from a function...... SetEntityCallback(yourEntity ,(byte*)CoinCollision,ENTITYCALLBACK_COLLISION); Quote Link to comment Share on other sites More sharing options...
Rick Posted March 13, 2012 Share Posted March 13, 2012 What I often do with this is make 1 collision function. All my classes derive from a base class that has a Virtual collision function. Inside each child class the entities that need to worry about collision I call SetEntityUserData() and pass 'this' to the entity data. Then inside my one collision function I call GetEntityUserData() casting to my base class. I then call the virtual collision function. By doing this the one LE collision function serves to just pass control on collisions to the object that should handle them. This way for all my games I do I can just copy and paste this function to my project and as long as I derive all my classes from the common base class, everything just works without having to worry about how to implement this callback. It would be pretty cool if LEO could do something like this automatically but LEO will most likely be dead with LE3D since that's basically what LE3D is now 1 Quote Link to comment Share on other sites More sharing options...
Uberman Posted March 13, 2012 Author Share Posted March 13, 2012 Here goes... Huh?! Your collision callback signature is even different from the prototype in the headers! Yours: int _stdcall CoinCollision( TEntity entity0, TEntity entity1, byte* position, byte* normal, byte* force, flt speed ) LEO/leobase.h Header: typedef void (*CollisionCallback)(TEntity ent0, TEntity ent1, const TVec3& pos, const TVec3& normal, flt speed); The LEO version in leobase.h is 2.5.0, but the C headers are 2.5.1. I'm beginning to get the feeling that the LEO headers are out of date with the current version of the library. The LEO header prototype does not define the "force" parameter found in your callback, but then the C headers don't even define a typedef for the callback prototype at all, so I can't compare. Quote Link to comment Share on other sites More sharing options...
Uberman Posted March 13, 2012 Author Share Posted March 13, 2012 I've just been playing around with this. I had pretty much the same problem with the same error. Your function prototype worked, tj. I don't get the runtime error now (because the correct number of arguments are being processed). Thank you for the concrete example. It let me see that there are definitely some disconnects in the SDK (and the documentation) for which I'll have to be prepared. Quote Link to comment Share on other sites More sharing options...
Canardia Posted March 13, 2012 Share Posted March 13, 2012 I need to send updated LEO to Josh, but first I need to test that it works. Quote ■ Ryzen 9 ■ RX 6800M ■ 16GB ■ XF8 ■ Windows 11 ■ ■ Ultra ■ LE 2.5 ■ 3DWS 5.6 ■ Reaper ■ C/C++ ■ C# ■ Fortran 2008 ■ Story ■ ■ Homepage: https://canardia.com ■ Link to comment Share on other sites More sharing options...
Pixel Perfect Posted March 13, 2012 Share Posted March 13, 2012 That's a really nice solution Rick. Quote Intel Core i5 2.66 GHz, Asus P7P55D, 8Gb DDR3 RAM, GTX460 1Gb DDR5, Windows 7 (x64), LE Editor, GMax, 3DWS, UU3D Pro, Texture Maker Pro, Shader Map Pro. Development language: C/C++ Link to comment Share on other sites More sharing options...
tjheldna Posted March 13, 2012 Share Posted March 13, 2012 Hi Rick, Thanks for that. That would be a good way handling the collision callbacks. As I'm just starting out it's good to know these things early. btw there were a few errors in my code, the editor for some reason some tags were add in when I saved, which i've deleted now. Cheers! Quote Link to comment Share on other sites More sharing options...
xtreampb Posted December 11, 2012 Share Posted December 11, 2012 i know this is rather old, but rick can i see a quick example snippet. I think it would help everyone here to see this as an example. Quote bool Life() { while(death=false) { if(death==true) return death; } } I have found the secret to infinite life Did I help you out? Like my post! Link to comment Share on other sites More sharing options...
Rick Posted December 11, 2012 Share Posted December 11, 2012 I'll throw one off the top of my head here. Below should get you started and show the general idea. Let me know if you have any questions. This is a nice way to keep things object oriented. class Object { private: // you can have a TEntity/TMesh/TModel here also since most every game "object" has a visual representation. then in this ctor you can set entity user data so that you never have to remember to do this public: virtual void OnCollision(Object* obj, byte* position, byte* normal, byte* force, flt speed) {} }; class Coin : public Object { private: TModel _coinModel; public: Coin() { _coinModel = LoadModel("coin.gmf"); // we do this so that when _coinModel entity is passed to the collision function we can call GetEntityUserData() and cast to Object SetEntityUserData(_coinModel, (byte*)this); // register this model to call the one collision method. every model in your game should register to this one function as long as it derives from Object SetEntityCallback(_coinModel, (byte*)Collision, ENTITYCALLBACK_COLLISION); } // override the collision function to handle collisions with the coin virtual void OnCollision(Object* obj, byte* position, byte* normal, byte* force, flt speed) { if(obj->IsPlayer()) { Player* p = (Player)obj; p->GiveCoin(this); HideEntity(_coinModel); } } }; // this is the one collision function for the entire game int _stdcall Collision(TEntity entity0, TEntity entity1, byte* position, byte* normal, byte* force, flt speed) { Object* obj1 = (Object*)GetEntityUserData(entity0); Object* obj2 = (Object*)GetEntityUserData(entity1); // if the objects are valid fire the collision between them. since this method is virtual it'll go to whatever object it is to handle things correctly if(obj1 && obj2) obj1->OnCollision(obj2, position, normal, force, speed); } Quote Link to comment Share on other sites More sharing options...
xtreampb Posted December 12, 2012 Share Posted December 12, 2012 so what is the difference between the virtual polymorphism and just having each model in a class, each with it's own collision function. I don't see the need to use polymorphism here. I also get this error Error 1 error C2440: 'type cast' : cannot convert from 'void (__thiscall Stone::* )(coll *,byte *,byte *,byte *,LE::flt)' to 'byte *' c:\Documents and Settings\Chris\My Documents\Visual Studio 2008\Projects\Castle Defense\Castle Defense\Stone.cpp 40 //the line the error occurs in is here SetEntityCallback(this->Full, (byte*)OnColl, ENTITYCALLBACK_COLLISION); //this->Full is a TModel, OnColl is my collision function name Quote bool Life() { while(death=false) { if(death==true) return death; } } I have found the secret to infinite life Did I help you out? Like my post! Link to comment Share on other sites More sharing options...
Rick Posted December 12, 2012 Share Posted December 12, 2012 so what is the difference between the virtual polymorphism and just having each model in a class, each with it's own collision function. Nothing is wrong with it. That will work. I just try to stick to object oriented style of programming and normal C callbacks don't follow that pattern very well. That and I find this method to be easier to move around from game to game. It's the same 1 C callback between all my games, and the real different logic is put into the custom objects that game has via the collision method. It makes it so I don't have to worry about the clue code details and more just focus on the collision logic. Generally what I do is store a TEntity (which can be a model) in my base class, since pretty much all game objects have a visual, and it automatically registers to the 1 C collision callback function and set's the user data. So all I have to do is derive from this base class and it's all done for me for every object. I don't have to think about it from game to game. Can you show me the class setup where you have the SetEntityCallback() function call you posted? You can take out any parts that aren't relevant. Quote Link to comment Share on other sites More sharing options...
xtreampb Posted December 12, 2012 Share Posted December 12, 2012 i'm not quite sure what your asking for so i'll post everything regarding the collisions //collisions class //header file #ifndef _M_COLL_H__ #define _M_COLL_H__ #include "engine.h" class coll { public: virtual void OnColl(coll *obj, byte* position, byte* normal, byte* force, flt speed) ; }; #endif //cpp file #include "Collisions.h" void coll::OnColl(coll *obj, byte* position, byte* normal, byte* force, flt speed) { } //Stone class //header file #ifndef _M_STONE__H_ #define _M_STONE__H_ #include "engine.h" #include "Collisions.h" class Stone : public coll { private: TModel Full; public: Stone();//constructor ~Stone();//destructor virtual void OnColl(coll* ColObj, byte* position, byte* normal, byte* force, flt speed); }; #endif //#ifndef _M_STONE__H_ //CPP file Stone::Stone() { this->Full=LoadModel("stone//stone_1.gmf"); EntityType(this->Full, 1); SetBodyMass(this->Full, 1); //so that we can pass the model to the collision function. we pass the model to cast it to coll type SetEntityUserData(this->Full, (byte*)this); SetEntityCallback(this->Full, (byte*)this->OnColl, ENTITYCALLBACK_COLLISION); } void Stone::OnColl(coll* ColObj, byte* position, byte* normal, byte* force, flt speed) { this->Destroy();//function call to run my collision information already set aside so that this collision testing can be easily tested } I've had this issue for the longest time, just didn't know where to turn to get help. Thanks ~xtreampb~ Quote bool Life() { while(death=false) { if(death==true) return death; } } I have found the secret to infinite life Did I help you out? Like my post! Link to comment Share on other sites More sharing options...
Rick Posted December 12, 2012 Share Posted December 12, 2012 I see the issue. The collision callback you pass to SetEntityCallback() can't be a member function of a class. It has to be a normal C function defined a very specific way. Look at my example I posted again and you'll see the C function at the bottom of the example. The one declared with _stdcall. That's the way LE requires the callback to be defined. I understand your question now about why polymorphism is required. Now that you know the callback MUST be a normal C function, once you are inside that function you have to ask yourself how can I get the classes that these Entity parameters belong to? That's why we do the SetEntityUserData() in our classes. It's a way for us to associate our object with the LE Entity. So once we are inside the C collision callback, we can get our object with GetEntityUserData() on the entity parameters that were passed to it by LE, cast back to our base class, and call the virtual collision function (which will in turn call the correct objects virtual collision function because of polymorphism). Quote Link to comment Share on other sites More sharing options...
xtreampb Posted December 13, 2012 Share Posted December 13, 2012 ok so now i can compile, but when a collision occurs i get a read/write errors. I moved the collision into my main C++ file and when i create the objects in my game, i 'link' it to the collision function at the top of the code void _stdcall Coll(TEntity *entity0, TEntity *entity1, byte *position, byte *normal, byte *force, flt speed) { coll *Obj1 = (coll*)GetEntityUserData(*entity0); //coll *Obj2 = (coll*)GetEntityUserData(*entity1); Obj1->OnColl(/*Obj1,*/ position, normal, force, speed); //return 0; } //inside my game function Stone mStone;//derived from coll as you saw earlier.\ SetEntityCallback(mStone.GetFullModel(), (byte*)Coll, ENTITYCALLBACK_COLLISION); thank you all for your help ~Xtreampb~ Quote bool Life() { while(death=false) { if(death==true) return death; } } I have found the secret to infinite life Did I help you out? Like my post! Link to comment Share on other sites More sharing options...
Rick Posted December 13, 2012 Share Posted December 13, 2012 entity0 and entity1 should not be pointers. They are just normal values. TEntity isn't really even a C++ class. It's just a typedef that really is just char* type. I also assume 'coll' is your class name? I ask because normally class names start with caps and have more meaningful names. Not sure what coll means. Seems like it would mean collision, but not sure why you would have a class called that. Unless you are more making an interface for collisions. void _stdcall Coll(TEntity entity0, TEntity entity1, byte *position, byte *normal, byte *force, flt speed) { coll *Obj1 = (coll*)GetEntityUserData(entity0); } Quote Link to comment Share on other sites More sharing options...
xtreampb Posted December 13, 2012 Share Posted December 13, 2012 this line throws the error Obj1->OnCall(position, normal, force, speed); Quote bool Life() { while(death=false) { if(death==true) return death; } } I have found the secret to infinite life Did I help you out? Like my post! Link to comment Share on other sites More sharing options...
Rick Posted December 14, 2012 Share Posted December 14, 2012 Yep, it's throwing the error because Obj1 isn't pointing to anything because you are using pointers as the entities when you assign Obj1. So the line where you GetEntityUserData isn't returning a valid object because it's not expecting a pointer like that, because the function definition isn't expect a pointer to those entities. Change it to what I have above and it should work. What IDE are you using? If Visual Studio put a break point in the C callback function and after you call the GetEntityUserData() statement mouse over the Obj1 variable and it will probably be bogus. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.