Jump to content


  • Posts

  • Joined

  • Last visited

Everything posted by irei1as

  1. If the object has no internal animations it's very easy, like a static object moving with a tween. Add the matrix transform of the parent object (the object receiving the decal) as a variable of the decal. Then if the object position changes (you have the id of the object to check) you make a transform of the decal according to that. The problem is if the object has animations, like the soldier moving his feet or arms. That's because it's not easy to know where in the geometry the hit will be in any other frame. Checking how the nodes moves may be a starting point but that needs a C++ change for the nodes to show the transform in real time. This is what I have related to this for the 3.8 version. Sadly I can't use the new code for the newest version. Inside source/T3D/shapeBase.h add before the end of the definition of class ShapeBase: public: MatrixF getNodeTransformN(const String &nodeName); MatrixF getNodeTransform(const S32 nodeIdx); Inside source/T3D/shapeBase.cpp just at the end: MatrixF ShapeBase::getNodeTransformN(const String &nodeName) { S32 nodeIdx = mShapeInstance->getShape()->findNode(nodeName); return getNodeTransform(nodeIdx); } MatrixF ShapeBase::getNodeTransform(const S32 nodeIdx) { MatrixF returnValue = MatrixF::Identity; if(nodeIdx != -1) { if (isServerObject() && mShapeInstance) mShapeInstance->animateNodeSubtrees(true); returnValue = mShapeInstance->mNodeTransforms[nodeIdx]; } MatrixF objectTransform = getTransform(); Point3F nodeObjPos = returnValue.getPosition(); nodeObjPos.convolve( getScale() ); returnValue.setPosition(nodeObjPos); returnValue.mulL(objectTransform); return returnValue; } DefineEngineMethod( ShapeBase, getNodeTransform, TransformF, (const char* nodeName),, "Get the transform of a named node in the shape.\n" "@param nodeName Name of the node to check.\n" "@return The current transform of the node.\n" ) { return TransformF(object->getNodeTransformN(nodeName)); } --------------------------------------------------------------------------------------------------------------------- In source\T3D\shapeImage.cpp look inside of void ShapeBase::getMountTransform( S32 index, const MatrixF &xfm, MatrixF *outMat ) for MatrixF mountTransform = mShapeInstance->mNodeTransforms[ni]; And add these lines before: if (isServerObject() && mShapeInstance) mShapeInstance->animateNodeSubtrees(true);
  2. Very interesting. Thanks for sharing.
  3. Using the id is better if you're creating the objects on the run and saving their id as variables. Because id changes every time you run the game you can't just use the number. If you have already created objects I think it's better to give them a global name (you can see that field just above the id box that Johxz pictured) and use that name (global name and number id are usually interchangeable in TorqueScript). For example it would be like: LocalClientConnection.controlOutVehicle( VehicleSteve , CameraJim );
  4. If the vehicle is a default one and you're ok with the default controls, the easiest way is to just set the vehicle as a control object (like you would like to control it from inside) but then change the camera to that outside one. Something like: function GameConnection::controlOutVehicle(%this, %vehicle, %camera) { %camera.scopeToClient(%this); %this.setControlObject(%vehicle); %this.setFirstPerson(false); %this.setCameraObject(%camera); } And then call if it's a single player game: LocalClientConnection.controlOutVehicle(Vehicle_ID, Camera_ID); (Vehicle_ID and Camera_ID are the id or name of the vehicle and the camera you created before.) If it's multiplayer you can't use LocalClientConnection. You'll have to use the command to server and client and all that and use: %client.controlOutVehicle(Vehicle_ID, Camera_ID); (Note: tecnically, I should do it for single player, too...)
  5. The fog problem is related to a postFX doing its work. In my case it's "GammaPostFX". That false in sceneRenderState what does is disable all the postFX in the rendering including that problematic one. Now, it seems that in 3.9 you may need a postFX to render the scene so disabling all of the postFX doesn't do the job. Get that line to "true" again, then. With that variable as true, I can think of two ways of fixing it: 1) The easy one. Well, if GammaPostFX is the one being problematic just disable that particular one. Look for "core\scripts\client\postFx\GammaPostFX.cs" inside locate "singleton PostEffect( GammaPostFX )". And make isEnabled = false; instead of true. Also just in case, in "core\scripts\client\postFx\hdr.cs" locate GammaPostFX.enable(); And just comment it so it isn't enabled later. HDRPostFX may give issues, too but I'm not sure as I have it disabled. If you run into it just look for HDRPostFX.enable(); in "core\scripts\client\postFx\hdr.cs" and "core\scripts\client\postFx\postFxManager.gui.settings.cs" and comment it out. The problem with this method is that you lose the use of "GammaPostFX" for the normal scene. 2) Hard way. But I don't know if it works for 3.9. If "GammaPostFX" in the normal RenderPassManager is problematic then... we can use a completely different RenderPassManager without "GammaPostFX". I'm not sure if this can work in 3.9 but you can try. First we need our new RenderPassManager. In "core\scripts\client\renderManager.cs" add just before the closing } of "function initRenderManager()": new RenderPassManager( GuiObjectViewRenderPassManager ); GuiObjectViewRenderPassManager.addManager( new RenderPassStateBin() { renderOrder = 0.001; stateToken = AL_FormatToken; } ); GuiObjectViewRenderPassManager.addManager( new RenderObjectMgr() { bintype = "Sky"; renderOrder = 0.1; processAddOrder = 0.1; } ); GuiObjectViewRenderPassManager.addManager( new RenderObjectMgr() { bintype = "Begin"; renderOrder = 0.2; processAddOrder = 0.2; } ); GuiObjectViewRenderPassManager.addManager( new RenderTerrainMgr() { renderOrder = 0.4; processAddOrder = 0.4; basicOnly = true; } ); GuiObjectViewRenderPassManager.addManager( new RenderMeshMgr() { bintype = "Mesh"; renderOrder = 0.5; processAddOrder = 0.5; basicOnly = true; } ); GuiObjectViewRenderPassManager.addManager( new RenderImposterMgr() { renderOrder = 0.56; processAddOrder = 0.56; } ); GuiObjectViewRenderPassManager.addManager( new RenderObjectMgr() { bintype = "Object"; renderOrder = 0.6; processAddOrder = 0.6; } ); GuiObjectViewRenderPassManager.addManager( new RenderObjectMgr() { bintype = "Shadow"; renderOrder = 0.7; processAddOrder = 0.7; } ); GuiObjectViewRenderPassManager.addManager( new RenderMeshMgr() { bintype = "DecalRoad"; renderOrder = 0.8; processAddOrder = 0.8; } ); GuiObjectViewRenderPassManager.addManager( new RenderMeshMgr() { bintype = "Decal"; renderOrder = 0.81; processAddOrder = 0.81; } ); GuiObjectViewRenderPassManager.addManager( new RenderOcclusionMgr(){ bintype = "Occluder"; renderOrder = 0.9; processAddOrder = 0.9; } ); GuiObjectViewRenderPassManager.addManager( new RenderObjectMgr() { bintype = "ObjectTranslucent"; renderOrder = 1.0; processAddOrder = 1.0; } ); GuiObjectViewRenderPassManager.addManager( new RenderObjectMgr() { bintype = "Water"; renderOrder = 1.2; processAddOrder = 1.2; } ); GuiObjectViewRenderPassManager.addManager( new RenderObjectMgr() { bintype = "Foliage"; renderOrder = 1.3; processAddOrder = 1.3; } ); GuiObjectViewRenderPassManager.addManager( new RenderParticleMgr() { renderOrder = 1.35; processAddOrder = 1.35; } ); GuiObjectViewRenderPassManager.addManager( new RenderTranslucentMgr(){ renderOrder = 1.4; processAddOrder = 1.4; } ); GuiObjectViewRenderPassManager.addManager(new RenderObjectMgr(){ bintype = "ObjectVolumetricFog"; renderOrder = 1.45; processAddOrder = 1.45; } ); GuiObjectViewRenderPassManager.addManager( new RenderGlowMgr() { renderOrder = 1.5; processAddOrder = 1.5; } ); GuiObjectViewRenderPassManager.addManager( new RenderPassStateBin() { renderOrder = 1.7; stateToken = AL_FormatToken; } ); The name being "GuiObjectViewRenderPassManager" will be used in the c++ code. In "source\T3D\guiObjectView.cpp" inside "void GuiObjectView::renderWorld( const RectI& updateRect )" add just before (our original with true) SceneRenderState state ( gClientSceneGraph, SPT_Diffuse, SceneCameraState( GFX->getViewport(), frust, MatrixF::Identity, GFX->getProjectionMatrix() ), renderPass, true ); This: renderPass = dynamic_cast<RenderPassManager*>(Sim::findObject("GuiObjectViewRenderPassManager")); Note the looking for that particular RenderPassManager we created. You may want to add a sanity check so renderPass isn't modified if "GuiObjectViewRenderPassManager" doesn't exist. What this method does is to replace "DiffuseRenderPassManager" of the rendering (the default one for Torque3D rendering) with a new one created with all its renderbin without name. The PostFX activates after and before renderbin with particular names so they will not activate for this RenderPassManager. You could even add different names to the renderbin and new PostFX for only the guiObjectView scene.
  6. I'm still having problems using #prepass to get any results in my old computer. But it just may be that it doesn't work in the old code I'm using and it's fixed in the new versions. An example of how to use it will be helpful so I can be sure where I'm wrong. Thanks. Changing colours is a bit complex. Let me share a few experiments of how I modified colour_exit: Plain black and white with a colour multiplication: (...) float bn_base = (colour_exit.x + colour_exit.y + colour_exit.z) / 3.0; //red example, change rgb values as needed float3 mult_colour = float3(1.0, 0.0, 0.0); colour_exit = float4(mult_colour.x * bn_base, mult_colour.y * bn_base, mult_colour.y * bn_base, colour_exit.a); return colour_exit; } The same but with luminance black and white: (...) float bn_base = (0.2126 * colour_exit.x + 0.7152 * colour_exit.y + 0.0722 * colour_exit.z); //red example, change rgb values as needed float3 mult_colour = float3(1.0, 0.0, 0.0); colour_exit = float4(mult_colour.x * bn_base, mult_colour.y * bn_base, mult_colour.y * bn_base, colour_exit.a); return colour_exit; } Sepia tone (copy/paste from somewhere): (...) float3 sepia; sepia.r = dot(colour_exit.rgb, float3(0.393, 0.769, 0.189)); sepia.g = dot(colour_exit.rgb, float3(0.349, 0.686, 0.168)); sepia.b = dot(colour_exit.rgb, float3(0.272, 0.534, 0.131)); colour_exit = float4(sepia.xyz, colour_exit.a); return colour_exit; } Direct light multiply: (...) //[0,1] range for darker and over 1 to make them more white. //Note: return clamps the colour float light_value = 0.5; colour_exit = float4(colour_exit.xyz * light_value, colour_exit.a); return colour_exit; }
  7. I think it's related to: http://forums.torque3d.org/viewtopic.php?f=12&t=597
  8. Those textures are looking really amazing. I'm not sure you'll experience this but let me share some issues I had while playing with these kind of transparencies: a) Using #prepass works only for Advance Lightning. But that's fine as very few users have the kind of ancient computers* that can't handle it. b) Be careful with the limits of the screen. For me the texture wraps around so in these limits a wobble can show the texture of the other side of the screen. It's more noticeable if there is a huge contrast of colours between both sides. http://i.imgur.com/yzXE5lm.png *just me, probably.
  9. I recommend to get the .chm files of the script reference that you can get from http://forums.torque3d.org/viewtopic.php?f=25&t=751 There's a lot of info about classes, too. It's my main help guide for scripting. For example you can get info about echo() that is the console print for torquescript.
  10. Thanks a lot for both your insight. I was able to locate the issue from the hints. It's related to the backbuffer not being cleared (or something like that) and GammaPostFX being picky with it. Let me share the kind of hack for the fix. In postEffect.cpp locate: if ( mTargetTex || mTargetDepthStencil ) { mTarget->resolve(); GFX->popActiveRenderTarget(); } else { // We wrote to the active back buffer, so release // the current texture copy held by the manager. // // This ensures a new copy is made. PFXMGR->releaseBackBufferTex(); } And make it: if ( mTargetTex || mTargetDepthStencil ) { mTarget->resolve(); GFX->popActiveRenderTarget(); PFXMGR->releaseBackBufferTex(); } else { // We wrote to the active back buffer, so release // the current texture copy held by the manager. // // This ensures a new copy is made. PFXMGR->releaseBackBufferTex(); } But I don't think this change is very efficient. But, well, it works for what I want to test.
  11. I'm having an issue with a particular kind of PostEffect, first person view and some particular equipments of weapons. I have absolutely no idea why this is happening so I would want to ask for a confirmation that this is common or if it's just happening with my old computer. Note: Using DirectX. First create a .cs with this (and then exec it somewhere, of course): function createRend() { if(!isObject(RendViewUpdater)) { %tickObj = new ScriptTickObject(RendViewUpdater); %tickObj.setProcessTicks(true); %tickObj.tNT = "PreBin1"; PreBin1.enable(); MissionCleanup.add(%tickObj); %playGuiRendWindow = new GuiBitmapCtrl() { position = "100 100"; extent = "150 150"; }; PlayGui.add(%playGuiRendWindow); %tickObj.miniWindow = %playGuiRendWindow; } } function RendViewUpdater::onInterpolateTick(%this, %delta) { (%this.miniWindow).setNamedTexture(%this.tNT); } function RendViewUpdater::onRemove(%this) { (%this.miniWindow).delete(); } singleton ShaderData( TestCopy_Shader ) { DXVertexShaderFile = "./xintexV.hlsl"; DXPixelShaderFile = "./xintexP.hlsl"; samplerNames[0] = "$inputTex"; pixVersion = 2.0; }; singleton GFXStateBlockData( TestCopy_stateblock ) { zDefined = true; zEnable = false; zWriteEnable = false; samplersDefined = true; samplerStates[0] = SamplerClampPoint; }; singleton PostEffect( PreBin1 ) { isEnabled = false; renderTime = "PFXAfterBin"; renderBin = "SkyBin"; shader = TestCopy_Shader; stateBlock = TestCopy_stateblock; texture[0] = "$backbuffer"; target = "#PreBin1"; }; In the same folder as the .cs add these two files: xintexV.hlsl struct VertToPix { float4 hpos : POSITION; float2 uv : TEXCOORD0; }; VertToPix main( VertToPix In ) { return In; } xintexP.hlsl struct ConnectData { float2 texCoord : TEXCOORD0; }; uniform sampler2D inputTex : register(S0); //-------------------------------------------------------------- //Main //-------------------------------------------------------------- float4 main( ConnectData IN ) :COLOR { float4 color1 = tex2D(inputTex, IN.texCoord); return color1; } Run the desert level of a full project and in the console type: createRend() Then the scene isn't correctly rendered in first person (render first person must be disabled in the Player datablock for the error to show) and with a particular weaponImage equipment. It will render incorrectly if: -You have the Ryder Pistol equipped. -You have no image equipped ( in the console: LocalClientConnection.player.unmountImage(0); ) -You have the vehicle turret equpped ( in the console: LocalClientConnection.player.mountImage(TurretImage,0); Note: funny in third person.) http://i.imgur.com/MIcw5FH.png It will be alright otherwise (another weapon equipped, like the Lurker Rifle): http://i.imgur.com/x3YGHKD.png I really need to know if it's just my version of Torque 3d (not up to date) or if the issue it's still around. And, um, do you know why it may be happening? Why it happens for some weapons but not for others? Thanks a lot for your help.
  12. Sure, I was planning to submit a PR on the texture fix. By the way, another interesting use is to mount a camera in another soldier to split the camera locally like old times multiplayer (1st person rendering must be enabled for their dtablocks). But the guicamera view kind of fails for PostFX when $backbuffer is involved (underwater shader, for example) so it needs more work.
  13. Edit: The problem was solved. Let me add the fix I found at the end. --- I've tried making a new class that shows a GameTSCtrl but using a particular camera instead of the control object: guiCameraView.h: http://pastebin.com/eChZDTGG guiCameraView.cpp: http://pastebin.com/M8yp2bLD And It works correctly -I ignore all the compile warnings- if that gui is inside playGui.gui. (It may be one frame late but I don't care about that. I'm rendering the scene twice, after all.) Now, I want that gui inside a GuiOffscreenCanvas (that canvas is not childed to anything) with something like: //offsidescreen("OffName","400 400") function offsidescreen(%name, %size) { if(%name $= "") return; //-------------------- %guiContent = new GuiOffscreenCanvas() { targetSize = %size; targetFormat = "GFXFormatR8G8B8A8"; targetName = %name @ "Canvas"; dynamicTarget = "1"; numFences = "0"; displayWindow = "1"; position = "0 0"; extent = %size; visible = "1"; active = "1"; }; %guiContentCam = new GuiCameraView() { Camera = "TheOtherCamera"; orthoMode = "0"; nearClip = "0"; visibleDistance = "0"; enablePostFX = "0"; forceFOV = "45"; position = "0 0"; extent = %size; visible = "1"; active = "1"; noCursor = "1"; }; %guiContent.add(%guiContentCam); %guiContent.insideCam = %guiContentCam; %guiContent.setName(%name); } And then use the targetName of the GuiOffscreenCanvas as part of a CustomMaterial so I can show a camera view inside a portal or monitors. The problem is the zdepth is wrong in that particular texture. Objects are rendered over the terrain when they should be part hidden or the player vanishes after the object if they shot (but the gun keeps with the previous depth). http://i.imgur.com/qYqvkUG.png At the top right you can see a correct use of GuiCameraView as it's child of playgui. The texture of the door shows incorrect z-depth and it comes from the named texture of the guioffscreencanvas. Any idea where the issue could be that it doesn't work for one and it does for the other one? Or, how could I render the GuiCameraView directly to a named texture without using guioffscreencanvas? I could just put the GuiCameraView out of screen bounds if needed and use the texture directly. --- Fix: It seems GuiOffscreenCanvas targetName texture couldn't save zdepth because it's not defined to do that. What I needed is to add GFXTexHandle depthTex( mTargetSize.x, mTargetSize.y, GFXFormatD24S8, &GFXDefaultZTargetProfile, "guioffscreencanvas::depth thing" ); mTarget->attachTexture( GFXTextureTarget::RenderSlot(GFXTextureTarget::DepthStencil), depthTex ); before mTarget->attachTexture( GFXTextureTarget::RenderSlot(GFXTextureTarget::Color0), mTargetTexture ); in guiOffscreenCanvas.cpp With just that the texture gets zdepth information and works as I wanted. http://i.imgur.com/OgbkjYb.png
  14. Let me share my try at this subject. What I did is to use a PostFX to copy the backbuffer before the TransparentObj RenderBin into a NamedTexture and use that NamedTexture as a texture sampler for a Custom Material. That Custom material just distorted a bit so it's not really refraction. If you have a custom RenderBin for those cloacking objects you should change the renderBin for the PostFX to that one. Note that the use of $backbuffer can cause issues if you use various viewports like with VR. ---------------------------------------------- First we create the PostFX (and exec it somewhere): singleton ShaderData( PreTranspCopy_Shader ) { DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/passthruP.hlsl"; OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; OGLPixelShaderFile = "shaders/common/postFx/gl/passthruP.glsl"; samplerNames[0] = "$inputTex"; pixVersion = 2.0; }; singleton GFXStateBlockData( PreTranspCopy_stateblock ) { zDefined = true; zEnable = false; zWriteEnable = false; samplersDefined = true; samplerStates[0] = SamplerClampPoint; }; singleton PostEffect( PreTranspCopy ) { // This PostEffect is used by 'AL_FormatToken' directly. It is never added to // the PostEffectManager. Do not call enable() on it. isEnabled = true; renderTime = "PFXBeforeBin"; renderBin = "ObjTranslucentBin"; shader = PreTranspCopy_Shader; stateBlock = PreTranspCopy_stateblock; texture[0] = "$backbuffer"; target = "#PreRenderTranslucent"; }; Then the material. For this example I'll just create a new material in the material.cs of the soldier so I can skin it directly. I don't have access to a computer with glsl compability so those are up to you. Add this at the end of that material.cs: singleton ShaderData( predatorTextureShader ) { DXVertexShaderFile = "shaders/common/z_predatorV.hlsl"; DXPixelShaderFile = "shaders/common/z_predatorP.hlsl"; samplerNames[0] = "$diffuseMap"; pixVersion =2.0; }; singleton CustomMaterial(Mat_Predator_Soldier_Main : Mat_Soldier_Main) { mapTo = "Predator_Soldier_Main"; translucent = true; shader = predatorTextureShader; sampler["diffuseMap"] = "#PreRenderTranslucent"; }; You may want to use a different CustomMaterial for each object so the woobly effect is not sincronized: singleton CustomMaterial(Mat_Predator2_Soldier_Main : Mat_Predator_Soldier_Main) {}; singleton CustomMaterial(Mat_Predator3_Soldier_Main : Mat_Predator_Soldier_Main) {}; ... Finally I added the two shader (hlsl) files at shaders/common. (Remember they must be plain ANSI files.) ----------z_predatorV.hlsl--------- struct VertToPixOut { float4 hpos : POSITION; float4 screen : TEXCOORD1; }; struct VertToPixIn { float4 hpos : POSITION; }; uniform float4x4 modelview; VertToPixOut main( VertToPixIn In) { VertToPixOut Out; Out.hpos = mul( modelview, In.hpos); Out.screen = Out.hpos; return Out; } --------------z_predatorP.hlsl----------- The distortion effect is in here. You should, of course, use another function for the distortion. This one is just a basic example. struct datainput { float4 screenCoord : TEXCOORD1; }; //diffuseMap is defined in the CustomMaterial and its ShaderData uniform sampler2D diffuseMap : register(S0); //accumTime is an engine variable. It's kind of the time of the material. //Use it to move things in time uniform float accumTime; float4 main( datainput IN ) : COLOR0 { //texture to screen float2 screenpos = 0.5+IN.screenCoord.xy/2*float2(1,-1)/IN.screenCoord.w; //wooble effect example float2 repeat = (1 - 1/200) - abs(sin(accumTime*3)/200); screenpos = screenpos * repeat; float4 colour_exit = tex2D( diffuseMap, screenpos); //here you can modify colourexit if needed return colour_exit; } After these changes just do %player.setSkinName("Predator") (%player.setSkinName("Predator2"), etc) Change %player with the ID of the soldier to re-skin like (LocalClientConnection.player).setSkinName("Predator")
  15. In less than 18 days I was able to summit a (very) small 3rd person single player beat em' up game for a game jam. The engine is quite versatile and flexible once you're used to it. But I only had experience doing games with RPGMaker before. I don't have enough knowledge of other engines to compare them. The server/client stuff is not easy when heavily modifying the C++ code but once I kind of understood the pack/unpack thing it became doable. As for scripting for single player I think there are good enough tutorials for the few parts where it's needed (change camera, mainly). I ignore the duality in the rest of scripts and they work fine.
  16. For how I've used them, in the datablocks you define the constants. If something changes all the time in the client then that is a variable of the object instance (for example: position, rotation, health...) and if something is (should be) always the same for that object then it's better in the datablock (like the shape .dae or what weapons are available from the start). It's this way to save client-server data movement because the data inside the datablock doesn't need to be re-sent constantly as it's defined at the start of the game. Well, you could create objects in c++ that use no datablock at all (like TSStatic) if you don't mind wasting pack/unpack updates. In your examples you would use one datablock for each (iron sword, wooden sword, etc...) because constants like max damage and shape are different for each. But as @Duion said, you would first make a base datablock (sword) and then derive the subsequent children datablocks and define inside them only the minor differences.
  17. It's maybe not enough to be in an official list, but I did this little freeware game thing for a game jam: https://irei1as.itch.io/1t
  18. I've checked and it seems how you keyframe doesn't matter but I think I've found your issue. You need for the threds to be in different slots, that's: robot.playThread(0, "rotation"); robot.playThread(1, "movearm"); If you use the same slot you destroy the previous animation (rotation in your case) so you don't have a base to blend and just plays the animation.
  19. First be sure for the animation ("blendable" animation) to be tagged as a blend animation. You can change that in the shape editor. http://docs.garagegames.com/torque-3d/official/content/documentation/World%20Editor/Editors/ShapeEditor.html#Properties_Window If I remember correctly you need a "root" animation for the Blend sequence. I did that sequence just a pair of frames of all the bones "keyframed" from the starting pose. The "blendable" animation has the not moving bones the same as how appear for the "root". Hmm, I don't remember in you have to keyframe all the bones or just the moving bones for the "blendable" animation. I'll check later.
  20. Stock collision uses the root position. That is that only the base collision without animation is the one being used. Using physX or bullet ~may~ avoid that but that uses a lot of c++ changes, I think. What I use: 1. The door has no collision at all. 2. Create invisible TSStatic objects but with the collision the size of each door. 3. In script, use setTransform(...) with the TSStatic objects to position them where you need the collision.
  21. Ah, there is a small difference on how both schedule work. They're not the same. EDIT: Hmm, I think that reference is wrong. In short: ···schedule(250,object,"aaa") calls function aaa() as long as object exists. If object is deleted then the schedule stops. It's used just for that condition. ···object.schedule(250,"aaa") calls function object.aaa(%this) if object exists. The function has access to object using %this. They call different functions. If you want to test, try to create both functions and use the different types of schedules... Copy-paste this in the console: function Player::hello(%this) { echo("This is with the Player.schedule(250,\"hello\")"); } function hello() { echo("This is with the alone schedule(250,Player,\"hello\")"); } And then use: (LocalClientConnection.player).schedule(250,"hello"); and schedule(250,LocalClientConnection.player,"hello")
  22. The first doesn't work because the function is defined as Player::scheduledLook(%this). That means it's a method (thing) of the Player class (namespace thing). So in order to be called you need a player (%this is the player in your working code so %player.scheduledLook() is called after the 250 ms using that %this.schedule(...)). For it to work in the first way (plain schedule without player) it should be defined as function scheduledLook() { ... } as a global function.
  23. I think that's related to one of the changes of player.cpp since that resource and not directly to the part of code you have. Inside updateMove(const Move* move) look for: if(doStandardMove) Inside that comes the code for the normal rotation code. (I just deleted everything related to "TORQUE_EXTENDED_MOVE" in my code so if you're using that then I can't help you, sorry.) Now, try to replace the doStandardMove branch with: if(doStandardMove) { F32 p = move->pitch * (mPose == SprintPose ? mDataBlock->sprintPitchScale : 1.0f); if (p > M_PI_F) p -= M_2PI_F; mHead.x = mClampF(mHead.x + p,mDataBlock->minLookAngle, mDataBlock->maxLookAngle); F32 y = move->yaw * (mPose == SprintPose ? mDataBlock->sprintYawScale : 1.0f); if (y > M_PI_F) y -= M_2PI_F; if (move->freeLook && ((isMounted() && getMountNode() == 0) || (con && !con->isFirstPerson()))) { mHead.z = mClampF(mHead.z + y, -mDataBlock->maxFreelookAngle, mDataBlock->maxFreelookAngle); } else { mRot.z += y; //quat rot {...} { //Get a quaternion of our desired rotation //Apply it to our current orientation //First let's try getting an up vector Point3F up(0.0f,0.0f,1.0f); Point3F rotatedUp(0,0,0); mOrient.mulP(up,&rotatedUp); //rotatedUp should now contain our local up vector //We want a rotation around rotatedUp by what was added to mRot.z radians QuatF rot(rotatedUp,y); mOrient *= rot; } // Rotate the head back to the front, center horizontal // as well if we're controlling another object. mHead.z *= 0.5f; if (mControlObject) mHead.x *= 0.5f; } // constrain the range of mRot.z while (mRot.z < 0.0f) { mRot.z += M_2PI_F; //quat rot {...} { //Get a quaternion of our desired rotation //Apply it to our current orientation //First let's try getting an up vector Point3F up(0.0f,0.0f,1.0f); Point3F rotatedUp(0,0,0); mOrient.mulP(up,&rotatedUp); //rotatedUp should now contain our local up vector //We want a rotation around rotatedUp by what was added to mRot.z radians QuatF rot(rotatedUp,M_2PI_F); mOrient *= rot; } } while (mRot.z > M_2PI_F) { mRot.z -= M_2PI_F; //quat rot {...} { //Get a quaternion of our desired rotation //Apply it to our current orientation //First let's try getting an up vector Point3F up(0.0f,0.0f,1.0f); Point3F rotatedUp(0,0,0); mOrient.mulP(up,&rotatedUp); //rotatedUp should now contain our local up vector //We want a rotation around rotatedUp by what was added to mRot.z radians QuatF rot(rotatedUp,(-1)*M_2PI_F); mOrient *= rot; } } } After that you should have these lines of code: delta.rot = mRot; delta.orient[1] = mOrient; //quat rot delta.rotVec.x = delta.rotVec.y = 0.0f; delta.rotVec.z = prevZRot - mRot.z; if (delta.rotVec.z > M_PI_F) delta.rotVec.z -= M_2PI_F; else if (delta.rotVec.z < -M_PI_F) delta.rotVec.z += M_2PI_F; So be careful and don't replace too many lines. Maybe you should make a safety copy of the file before doing the changes. But... I'm not sure if that change will be enough, sorry. In my code I use doQuatOrientation before that "if(doStandardMove)" (it's the first line in the update move as my gravity is a variable of player class and not just normals) so it's kind of different. I guess you could copy (with different variable names) the part of finding the normal at the start of the function if needed. By the way, the camera problem I mentioned is related to motion sickness. If the geometry of the terrain is irregular the camera jumps wildly around when moving with contact surface. Edit: Also, check for the pack unpack functions. It seems player has two pairs of each and you need to do the mOrient thing in both unlike once as the resouce points. It must be that before there wasn't that many.
  24. Thanks a lot for the updates. I use a lot the script reference and having the new functions and the "new" classes (my old chm hadn't ScriptTickObject) in hand is very useful.
  25. Quaternion math is not the easiest thing. You shouldn't think of them as a fancy vector as quaternions "kinda" have 4 dimensions (direction and a rotation in that direction). They're a transformation so the up vector is defined by something like "Quat.mul(0,0,1)". Well, just let me share the code I used when I was playing with the "mOrient-resource". Feel free to ask anything if the comments are not enough. In the .h file inside the class (I used it in "public:") add: void doLocalRotateX(const F32& rotX , QuatF* qchanged ); void doLocalRotateZ(const F32& rotZ , QuatF* qchanged ); void doQuatOrientation( const Point3F &neworient ); In the .cpp file add at the end (for example): void Player::doLocalRotateX(const F32& rotX, QuatF* qchanged ) { QuatF tempq(qchanged->x,qchanged->y,qchanged->z,qchanged->w); if(!mIsZero(rotX)) { Point3F localX; tempq.mulP(Point3F(1.0,0.0,0.0),&localX); QuatF qx(localX,rotX); tempq *= qx; } tempq.normalize(); qchanged->set(tempq.x,tempq.y,tempq.z,tempq.w); } void Player::doLocalRotateZ(const F32& rotZ, QuatF* qchanged ) { QuatF tempq(qchanged->x,qchanged->y,qchanged->z,qchanged->w); if(!mIsZero(rotZ)) { Point3F localZ; tempq.mulP(Point3F(0.0,0.0,1.0),&localZ); QuatF qz(localZ,rotZ); tempq *= qz; } tempq.normalize(); qchanged->set(tempq.x,tempq.y,tempq.z,tempq.w); } void Player::doQuatOrientation( const Point3F &neworient ) { //we will not modify mOrient untill the end QuatF tempmOrient = mOrient; tempmOrient.normalize(); tempmOrient.inverse(); //we're going global->local so we need the inverse of mOrient Point3F vect=neworient; //we need to normalize neworient vect.normalize(); Point3F localvect; tempmOrient.mulP(vect,&localvect); localvect.normalize(); //this normalize may not be needed... but just in case tempmOrient = mOrient; //we need back normalized mOrient to do the transformations tempmOrient.normalize(); //localvect is our wanted neworient in local coordinates //x and y values are the projections of localvect in the z=0 plane //to know that angle (that is the rotation on the z axis) we can use then the arctangent F32 rotZ = mAtan2(localvect.x,localvect.y); doLocalRotateZ(rotZ,&tempmOrient); //we rotate around our local z axis so we face our destination (y axis aligned) //so we only need a final rotation with our local x axis //rotation on x axis is similar to z but the plane is x=0 F32 rotX = mAtan2(mSqrt(localvect.x*localvect.x + localvect.y*localvect.y),localvect.z); doLocalRotateX(rotX,&tempmOrient); //now we undo the initial rotZ //This way the initial local-z rotation is similar... I think doLocalRotateZ((-1)*rotZ,&tempmOrient); //finally we transform mOrient mOrient.set(tempmOrient.x,tempmOrient.y,tempmOrient.z,tempmOrient.w); } (Change "Player::" with the name of your particular class, of course.) Then in order to align the mOrient to a "vectorUp" you just do in the c++ code: doQuatOrientation(vectorUp); where vectorUp is the new orientation. You can see I normalize() a lot... maybe too much. Feel free to remove all of the normalize() you think are not needed. Note I use up as vector. If you're using gravity(going down) then remember to use gravity.neg() or you'll "walk" on your head. As final note let me share that using the normal as up vector is not a very good idea if you use the default camera of player. Well, it's all right if the terrain is a plane but if it's kind of bumpy like the terrain of the full template then the camera movements are unpleasant when you run.
  • Create New...