Jump to content

Charrua

Developers
  • Posts

    230
  • Joined

  • Last visited

Blog Entries posted by Charrua

  1. Charrua
    Thnigs i will cover:
     
    1) How to get objects created on the editor from Cpp
    2) How to send objects created by Cpp code to LUA
    3) How to call an entity's script LUA function easely (without parameters)
    4) How to call an entity's script LUA function passing parameters
    5) How to receive returned parameters from LUA functions (yes one or more)
    6) How to expose a Cpp Class to LUA using tolua++
     
     
    I will split this post acordingly to the items listed above for easy referencing/reading
     
    Here a complete Leadwerks app: cpp source, map and scripts of a working example of the all above.
    //no longer valid!
    https://dl.dropboxusercontent.com/u/78894295/leadwerks/cppLuaBindings.zip
    //2021/12/14 update
    https://drive.google.com/file/d/1JdewNls8joRJygZ5zkNnuHrnCqNqDl_S/view?usp=sharing
    (see description of what executable does and how to operate it on a note at the end of this post)
     
    I'm not English native, so apologies if i do linguistic mistakes, and i'm not a so experienced cpp programmer (ok i'm not a programmer, I'm a technician in electronics, so for any inconsistency or semantic error/mistake pm me and i'll edit/modify the post, thank's)
     
     
    This site has many topics about this subject but for someone like me many of them has almost all, but normally there are some or too much details which are not exposed. Normally it takes a while to get to the point:
     
    do
    try, pray, try again, reread
    until ok OR tooLate
     
    My intention is to give "allincluded" for new Leadwerks cpp users which is asumed that if they are
    cpp programmers normally are advanced ones, but being new to Leadwerks normally the learning
    curve is shorten with material like i'll expose.
     
    Here some interesting links on the subject (not all of them)
     
    http://www.leadwerks.com/werkspace/topic/11007-calling-lua-script-function-from-c-with-arguments/page__hl__callfunction
    http://www.leadwerks.com/werkspace/topic/7548-access-cpp-global-via-lua/page__hl__tolua#entry60531
    http://www.leadwerks.com/werkspace/topic/8802-tolua-exposure-problems/
    http://www.leadwerks.com/werkspace/topic/7866-lua-version-in-30/
    http://www.leadwerks.com/werkspace/blog/138/entry-1288-pushing-complex-lua-tables-from-c/
     
    there are many references to:
    http://www.leadwerks.com/werkspace/files/file/216-tolua/
    but this just display: sorry we couldn't find that!
     
    so, here is the tolua++ source
    https://github.com/LuaDist/toluapp
     
    documentation
    https://www8.cs.umu.se/kurser/TDBD12/VT04/lab/lua/tolua%2B%2B.html
     
    precompiled
    http://www.codereactor.net/tolua-windows-precompiled-binary-exe-file-download-and-more-instructions-on-tolua/
     
    (note, the link addressed by autors to download:
    tolua++ can be downloaded from the sites below:
    http://www.codenix.com/~tolua/ is broken! )
     
    How hard is to get all the links up to date! isn't it?
     
     
    Items coming soon...
     
    jio
     
    ---------------------------------------------------------------
    Notes about the executable:
     
    In the terminal window, each Lua function and cpp method throws some info, so
    move the main window a little to the right, so you can see the terminal window.
     
    You'll se something like that, without my comments in italic:
     
    app.lua start //things that happens LUA side App.lua Start
    c2luaTest not nil //instance of object form c created and not nil
    lua is calling me! //calling an object's method from lua OK
    3.3561e+008 //calling average: (10+35+15)/3 :(WRONG! see bellow)
     
    start scanning.... //this is cpp side on App.cpp, App::Start
    //list of objets from the map (world)
    Directional Light 4
    EditorCamera
    findMe //objet searched
    found entity findMe !!! // find OK, set a pointer to it for future use.
    Directional Light 4
     
    EditorCamera //now searching only for cameras
    found editor camera //found editor camera, got camera from editor!
     
    now, put manin window on focus.
    Press a, s, d, f
     
    a: calls average(10, 15, 25) and print the results on the terminal window
    s: calls average( 5, 10, 4) and print the results on the terminal window
    d: calls turnMe(vec3) with a default value of: turn.x=10; turn.y = 20; turn.z = 30;
    if ok, you see the red cube turn this amount per axis on each keyhit.
    f: calls noParams() and print "executing noParams"
     
    As you press each letter, new messages appear on the terminal window confirming functions of lua
    correctly called from c++ code.
     
    if you press a, s, d, f in this sequence, you should see
     
     
    entity has script
    average defined
    executing average
    16.6667
    entity has script
    average defined
    executing average
    6.33333
    entity has script
    turnMe defined
    calling me from C
    executin noParams
     
     
    WRONG display in calling average cpp method from LUA, guess what? a bug.
     
    in Start function inside App.lua i do:
     
     
    c2luaTest.intA = 10
    c2luaTest.intA = 35
    c2luaTest.intA = 15
    System:Print(c2luaTest:average())
     
    so I only initialize intA, leaving intB and intC with garbage, so, expect to see any number
    on each run!
    Please edit the App.lua for me!
  2. Charrua
    Now, I wrote a single script that creates suspension, steer and traction
    You have to create the chassis, 4 wheels, set this script to each one of the wheels and then paly with the configurable parameters.
    Enjoy:
    --[[ Autor Juan Ignacio Odriozola (charrua) Purpose: A script that facilitates the making of a simple car all you need is a chassis and 4 wheels assing this scrip to each wheel and set the object chassis then adjust some of the configurable parameters Parent: chassis entity : wheel 3 joints and 2 auxiliary entities are created the chain is: Parent Slider Pivot Hinge Pivot Hinge chassis -suspensionJoint- suspensionPivot -steerJoint- steerPivot -wheelJoint- wheel suspension uses pin 010 (Y axis) steer uses pin 010 (Y axis) wheel pin (must be set depends of wheel orientation) up/down keys are defaults for forward and backward left/right keys are defaults for steer left/right space key is default for brakes steer velocity and start/end angle must be set suspension lenght must be set wheel friction must be set steerAngle set both limits to +/- steerAngle/2 if no key (left/right) is pressed then, target angle is 0 : straight suspensionLenght set both limits to +/- suspensionLength/2 and target distance is set to 0 suspension strength defaults to 1000 which is too much strenght for a light weight car (20 of mass) and not to much for a 200 car of mass each joint is created with a mass of 1, which should be taking into accoung (so for a 4 wheels car, you have a mass of 8 on the 8 joints). there are so many other parameters that may be adjusted: Spring, Strength, Stiffness ... not too much documented :) ]]-- Script.currspeed = 0 Script.chassis = nil--Entity "chassis" Script.pin = Vec3(0,0,1) --Vec3 "wheel Pin" Script.motorspeed=500--float "max motor speed" Script.velcontrolled=false--bool "velControl" Script.suspensionLength=0.2--float "suspension" Script.steerAngle=90--float "steer angle" Script.steerSpeed=100--float "steer velocity" Script.friction=1--float "wheel friction" Script.steerPivot=nil Script.suspensionPivot=nil Script.steerJoint=nil Script.suspensionJoint=nil Script.wheelJoint=nil function Script:Start() local pos = self.entity:GetPosition(false) --true for global if self.chassis ~= nil then self.suspensionPivot = Pivot:Create() self.suspensionPivot:SetPosition(pos) self.suspensionPivot:SetMass(1) self.suspensionPivot:SetCollisionType(Collision.None) self.steerPivot = Pivot:Create() self.steerPivot:SetPosition(pos) self.steerPivot:SetMass(1) self.steerPivot:SetCollisionType(Collision.None) --joints creation self.suspensionJoint = Joint:Slider(pos.x, pos.y, pos.z, 0, 1, 0, self.chassis, self.suspensionPivot) self.steerJoint = Joint:Hinge(pos.x, pos.y, pos.z, 0, -1, 0, self.suspensionPivot, self.steerPivot) self.wheelJoint = Joint:Hinge(pos.x, pos.y, pos.z, self.pin.x, self.pin.y, self.pin.z, self.steerPivot, self.entity) --suspension self.suspensionJoint:EnableLimits() self.suspensionJoint:SetLimits(-self.suspensionLength/2,self.suspensionLength/2) --steerAngle=0 means no steer self.suspensionJoint:SetTargetAngle(0) --at the middle self.suspensionJoint:SetMotorSpeed(1) -- 1 m/s self.suspensionJoint:SetStrength(100) --defatul is 1000 self.suspensionJoint:EnableMotor() --steer self.steerJoint:EnableLimits() self.steerJoint:SetLimits(-self.steerAngle/2,self.steerAngle/2) --steerAngle=0 means no steer self.steerJoint:SetMotorSpeed(self.steerSpeed) self.steerJoint:EnableMotor() --wheel self.entity:SetFriction(self.friction, self.friction) else Debug:Error("no chassis assigned") end end function Script:setMotorSpeed(speed) if self.velcontrolled then --System:Print("setMotorSpeed: "..speed) self.currspeed = speed if speed~=0 then self.wheelJoint:EnableMotor() end self.wheelJoint:SetMotorSpeed(self.currspeed) end end function Script:UpdateWorld() if self.motorspeed>0 then self.wheelJoint:SetAngle(self.wheelJoint:GetAngle()+100) else self.wheelJoint:SetAngle(self.wheelJoint:GetAngle()-100) end if App.window:KeyDown(Key.Space) then self:setMotorSpeed(0) end if self.velcontrolled then if App.window:KeyDown(Key.Up) then self.currspeed = self.currspeed + 10 if self.currspeed>self.motorspeed then self.currspeed=self.motorspeed end if self.currspeed == 10 then self.wheelJoint:EnableMotor() end self.wheelJoint:SetMotorSpeed(self.currspeed) end if App.window:KeyDown(Key.Down) then self.currspeed = self.currspeed - 10 if self.currspeed<-self.motorspeed then self.currspeed=-self.motorspeed end self.wheelJoint:SetMotorSpeed(self.currspeed) end end if self.steerAngle>0 then local direction=0 if App.window:KeyDown(Key.Left) then direction=-self.steerAngle/2 end if App.window:KeyDown(Key.Right) then direction=self.steerAngle/2 end self.steerJoint:SetAngle(direction) else self.steerJoint:SetAngle(0) end end  
    In the other maps i was using a box as a floor to which I set the desired friction, testing this new script i use a terrain and have to figure it out how to set the friction to the terrain...
    Did some searches and ended with:
    local n for n=0,self.world:CountEntities()-1 do local entity = self.world:GetEntity(n) if entity:GetClassName()=="Terrain" then terrain = entity System:Print("terrain found!") terrain:SetFriction(10,10) break end end insert this in the app.lua (after load map) and then you can play with terrain friction, the video shows how the car behaves with the defaul terrain friction and then whit a friction of 10,10
    Always learning something new 
     
    A word about some parameters:
    If you are using a hinge, when you specity speed (SetMotorSpeed) the number means degrees per second. So if you use 3600 as max speed you get 10 revoluions per second.
    If your tire has, 64cm then d*pi aprox 2 meters per revolution, 10 revolutions per secon aprox 20 meters per second... and if you are lucky 72Km/h
    If you are using a slider, then speed is un meters per second. 
    Other parameter which is very important is the hinge/slider "pin" which is a vector that tells the direction of movement of the slider or over which plane de hinges open/close
    For a common door, we need to use the Y axis, so the pin is 0,1,0
    I use this pin for the suspension and for the steer but for this script, you must tell the traction pin, which sould be 1,0,0 or 0,0,1 depending on the orientation of your tires
    If your tires are not facing X nor Z axis, then you have to do some math to get the proper x,z component of the pin
    In the script I use a Strenght of 100, instead of the 1000 which is default, my car is light weight : 5 + 4*3 = 17 of Mass
    chassis has 5, each wheel has 1 and the 2 auxiliary pivots has 1 each one
    whith a friction of 10 on each tire and with a friction of 10 on the terrain looks ok for me (better than I spected at first). 
    Juan
  3. Charrua
    Recently i posted a simple car made with joints without too much explanations... so
    What is a joint?
    This video shows joints in action. At the end, the stand alone executable and the complete project.
     
    The following text and figure is from the "ode-latest-userguide", figures are also form the JV-ODE documentation:
    In real life a joint is something like a hinge, that is used to connect two objects. It is a relationship that is enforced between two bodies so that they can only have certain positions and orientations relative to each other. This relationship is called a constraint,  the words joint and constraint are often used interchangeably. The figure shows three different constraint types. The first is a ball and socket joint that constraints the “ball” of one body to be in the same location as the “socket” of another body.  The second is a hinge joint that constraints the two parts of the hinge to be in the same location and to line up along the hinge axle.  The third is a slider joint that constraints the “piston” and “socket” to line up, and additionally constraints the two bodies to have the same orientation. Each time the integrator takes a step all the joints are allowed to apply constraint forces to the bodies they affect. These forces are calculated such that the bodies move in such a way to preserve all the joint relationships. Each joint has a number of parameters controlling its geometry. An example is the position of the balland- socket point for a ball-and-socket joint. The functions to set joint parameters all take global coordinates, not body-relative coordinates. A consequence of this is that the rigid bodies that a joint connects must be positioned correctly before the joint is attached. Figure: types of joints

     
    The "Integrator step" is the part of the physics engine that does all the calculations.
    Based on the current state, force applied, constrain parameters, collisions etc... recalculates the next position and rotation of every affected physical obejct.
    Leadwerks then, does the render of the objects acordingly to it's new positions, or that is what is supposed to do... (Josh should correct me if I'm wrong).
    A joint normally is placed between two objects, one is called Parent and the other Child. The child should be NULL, and in this case
    the joint is between one body and the world or scene.
    In the case of a door, you need a Door and a Frame, the Frame should be the Parent body, the Door the child body.
    In real life you have to decide where the Hinge (or more than one) will be placed and use some screws to attach both parts of the hinge to the frame and the door.
    In software, things are practically the same
    In other words, we have to have 3 things clear:
        Who is the parent and where it should be positioned and oriented.
        Who is the child and where it should be poitioned and oriented.
        Which type of joint you need, where it should be positioned and oriented.
    Normally we create and place the two bodies first and then, at joint creation we say where the joint will be and how it should be oriented, who the parent is and who child is, if any.
    After that, joints normally has some other properties that should be adjusted, used, controlled, limited, enabled... etc, depending on the joint type.
    Joints have two "limits":
    If the joint is a Hinge, the limits are the Start Angle and End Angle "the door should turn".
    If the joint is a Slider, the limits are the Start Position and End Position the piston should extend.
    If the joint is a Ball, the limits are the cone angle and twist or torsion... keep reading
    Joints (hinge and slider) should have a "motor" if not, as a normal door, we need external forces to "open the door", if we place a door on a frame attached with hinges and the door has no lock at all, then
    the wind should open/close it, or some body that collides with the door should move it. As we use a hinge, the door do not fall, do not take an upward motion, the motion is well known and 
    constrainded tho the clasic movement we all know.
    But if, for instance we have a "motorized door" then the open/close operation is started in some way (press of a button?) and a motor does the job. Normally it stops automatically, because there are end-run switches or the like installed and working for us.
    A sofware motorized joint is exactly that, we set where we want the joint go (setTargetAngle), and start a motor (enableMotor).
    Ball Joint
    A ball is used when we need a "cone" movement freedom, think that you have a rope attached at the roof of your room and you hang a box on it.
    If the rope is made of a rigid material, the box should made a pendulum movement not so large and probably the box orientations should not change too much.
    How much we let the rope to move is dictated by the first limit and how much the object attached should change orientation (twist/torsion) is dictated by the second limit.

    A rope is a nice looking example, all you have to do is place N cubes (shape is not important) and place ball joints in the middle of each pair.
    You may have the first box joined to the world (attached to a fixed 3d position) and then a link of bodies and ball joints:
        Joint N has body N as parent and body N+1 as child
    Leadwerks came with a simple ball.lua script:
    function Script:Start()     local pos = self.entity:GetPosition(true)     self.joint = Joint:Ball(pos.x, pos.y, pos.z, self.entity, self.entity:GetParent())     self.joint:SetFriction(10)     self.entity:SetDamping(0,0) end If you have a chain of parented objetcts, then you  may set this script and you have a rope build with a few clicks of your mouse on the editor.
    Procedure:
        Create some boxes, set prop collision and a mass not zero (1 perhaps), place then in a vertical row somewhat separated.
        In the editor drag the second box to the first, the third to the second building a hierachy (parent/child relationship)
        Select all (one by one with ctrl click on each box) and then apply the ball script. Voila! You have a rope with no limits: cone angle is the maximum the engine lets you to be and the same for torsion.
        Collide the rope with some object or, place the boxes horizontally (instead of vetically) and let gravity do it's job.
        
    I made another ball script: ballEnhaced:
    Script.parent = nil--Entity "Parent" Script.child = nil--Entity "Child" Script.useLimits=false--bool "Use limits" Script.coneAngle = 45--Float "Cone Angle" Script.torsion = 45--Float "Torsion" Script.hide = true--bool "Hide entity" function Script:Start()     local pos = self.entity:GetPosition(true)     if self.parent~=nil then         self.joint = Joint:Ball(pos.x, pos.y, pos.z, self.parent, self.child)         self.joint:SetLimits(self.coneAngle, self.torsion)         if self.useLimits then             self.joint:EnableLimits()             System:Print("limits:"..self.coneAngle..", "..self.torsion)         end         if self.hide then             self.entity:Hide()         end     end end if using this script, you have to create a pivot or box (collision type: none, and no mass) and tell the script ho the parent is and ho the child is, if no child, then the parent body
    will be attached to the environement, fixed at this 3d point.
    In the map included in this blog there are two ropes, one made with the leadwerks ball.lua and one with the ballEnhaced.lua script, in this script you may tell how to constrain 
    the ball cone anlgle and trosion. Look the video, and you will see the difference on how the cubes behave when hitted by the car.
    A rope suspension bridge should be made with both ends attached to the world and a double chain of joints... doing two unions between each part of the brige.
    It's nice so see the car passing over it!
    The attached video shows at first the car passing over the bridge... the mass of the car is very little, if not, the joints get broken.. as always there are some other things to consider.
    Hinje Joint
    As I used a hinge for some explanation, i guess there is not to much to say here, I use some hinges for doors in the map attached and also used them for the steer and wheels of the car.
    One door have a motor and the other not, so the second one moves as the car collides it. 

    I wrote two scripts: hingeEnhaced.lua
    Script.parent = nil --entity "parent" Script.child = nil --entity "hild" Script.hide = true--bool "Hide entity" Script.pin = Vec3(0,0,1) --Vec3 "Hinge Pin" Script.limitsenabled=false--bool "Enable limits" Script.limits = Vec2(-45,45) --Vec2 "Limits" Script.friction = 0--float "Friction" function Script:Start()     local pos = self.entity:GetPosition(true)     if self.parent~=nil then         self.joint = Joint:Hinge(pos.x, pos.y, pos.z, self.pin.x, self.pin.y, self.pin.z, self.parent, self.child)         if self.limitsenabled then self.joint:EnableLimits() end         self.joint:SetLimits(self.limits.x,self.limits.y)         self.joint:SetFriction(self.friction)         self.entity:SetDamping(0,0)         if self.hide then             self.entity:Hide()         end     end end As in the case of a ball joint, with this script you may set parent, child and limits
    hingeMotorized.lua
    Script.parent = nil --entity "parent" Script.child = nil --entity "hild" Script.hide = true--bool "Hide entity" Script.pin = Vec3(0,0,1) --Vec3 "Hinge Pin" Script.limitsenabled=false--bool "Enable limits" Script.limits = Vec2(-45,45) --Vec2 "Limits" Script.tOpenClose = 15 --Int "Open/Close time" Script.movespeed = 60 --Int "Speed" Script.startTime = 0 Script.action = 1    --1 open, 0 close function Script:Start()     local pos = self.entity:GetPosition(true)     if self.parent~=nil then         self.joint = Joint:Hinge(pos.x, pos.y, pos.z, self.pin.x, self.pin.y, self.pin.z, self.parent, self.child)         if self.limitsenabled then self.joint:EnableLimits() end         self.joint:SetLimits(self.limits.x,self.limits.y)         self.joint:SetTargetAngle(self.limits.x)         self.joint:EnableMotor()         self.startTime = Time:GetCurrent()         self.joint:SetMotorSpeed(self.movespeed)         if self.hide then             self.entity:Hide()         end     end end function Script:UpdatePhysics()     local time_ = Time:GetCurrent()     if time_ - self.startTime > self.tOpenClose*1000 then                  self.action = 1-self.action    --toggle action         if self.action == 1 then             self.joint:SetTargetAngle(self.limits.x)         else             self.joint:SetTargetAngle(self.limits.y)         end         self.startTime = Time:GetCurrent()     end end This scripts creates the joint, then set a target angle, enables the motor and apply a velocity, so the "door" starts going to the first limit.
    After the tOpenClose time, the variable action is toggled and based on it the TargetAngle will be first limit or second limit, in this way, the door continously open and then close
    after tOpenClose time

    Slider Joint


    For a slider joint, in this map i wrote one script: sliderMotorized.lua
    Script.child = nil --entity "hild" Script.hide = true--bool "Hide entity" Script.pin = Vec3(0,0,1) --Vec3 "Hinge Pin" ScrScript.parent = nil --entity "parent" ipt.limitsenabled=false--bool "Enable limits" Script.limits = Vec2(-45,45) --Vec2 "Limits" Script.tOpenClose = 15 --Int "Open/Close time" Script.movespeed = 60 --Int "Speed" Script.startTime = 0 Script.action = 1    --1 open, 0 close function Script:Start()     local pos = self.entity:GetPosition(true)     if self.parent~=nil then         self.joint = Joint:Slider(pos.x, pos.y, pos.z, self.pin.x, self.pin.y, self.pin.z, self.parent, self.child)         if self.limitsenabled then self.joint:EnableLimits() end         self.joint:SetLimits(self.limits.x,self.limits.y)         self.joint:SetTargetAngle(self.limits.x)         self.joint:EnableMotor()         self.startTime = Time:GetCurrent()         self.joint:SetMotorSpeed(self.movespeed)         if self.hide then             self.entity:Hide()         end     end end function Script:UpdatePhysics()     local time_ = Time:GetCurrent()     if time_ - self.startTime > self.tOpenClose*1000 then                  self.action = 1-self.action    --toggle action         if self.action == 1 then             self.joint:SetTargetAngle(self.limits.x)         else             self.joint:SetTargetAngle(self.limits.y)         end         self.startTime = Time:GetCurrent()     end end This script is 99% the same as the hingeMotorized.lua, the only difference is that a slider joint is created instead of a hinge joint.
    Note that the limis are not angles, they are offsets from the initial position of the plataform.
    Stand alone executable:
    jointsworldDistro.zip
    Project:
    jointsworldProject.zip
    Enjoy
    Juan
  4. Charrua
    Here there is a way of making a simple car based on hinges
    This is not a tutorial, is just simply a stating point for the ones that want/like to play arround physics and hinges...
    I included the entire project and the distribution executable to test the scripts so, you have as I say a starting point and just that, hpe it helps someone
    This is the editor view and the initial placement of the parts needed

    Basically I made 3 scripts
    physicsProperties.lua
    wheel.lua
    steer.lua
    First you have to create and place the car body or chassis, 4 wheels and 6 auxiliary pivots/ or any brush you like (a cube is fine) for the hinges
    4 of the auxiliary entities are for the wheels hinges and 2 for the wheels steer.
    Place the wheel hinge and steer centered with the wheel.
    After that you may set some script parameters:
    Wheel scritp:

    Basically, the position of the entity script holder is used to create a hinge between the "parent" and the "child" you choose
    (in the picture above: between the auxiliary entity SteerFL and the WheelFL)
    If vel control is checked then, a motor is enabled for that hinge and keys Up/Down are used to increase/decrease speed
    If vel control is not cheched, no motor is enabled and the wheel is free to run
    Be carefull whit the Hinge Pin, which dictates the axis over which the wheel will rotate, in this case I used X axis, but if you use other pieces direction/alignement you should adjust this values.
     
    Steer script:

    The steer hinge is used to turn the wheel to handle car heading, so the pin is the Y axis
    Limits and Motor are needed to control the steer
    Limits is for how much the steer will turn right/left using the default keys left/right arrow
    When you press left or ritght key, the right limit will be set as the hinge angle and the hinge will try to reach this angle at the "steer speed", the same, but whit the left limit happen if you press the left key.
     
    physicsProperties just let you adjust the friction of the wheels and or the floor
    Script.sfrict=0--float "static friction" Script.kfrict=0--float "kinetic friction" function Script:Start() System:Print("phy properties start") self.entity:SetFriction(self.sfrict, self.kfrict) end so simple, and in the editor it looks:

    Here is a hand drawing of how scripts, objects, parent/child are connected
     
     

     
    Here is the wheel script
    Script.currspeed = 0 Script.parent = nil--Entity "Parent" Script.child = nil--Entity "Child" Script.pin = Vec3(0,0,1) --Vec3 "Hinge Pin" Script.motorspeed=500--float "Motor speed" Script.velcontrolled=false--bool "velControl" function Script:Start() System:Print("wheel start") self.entity:Hide() local pos = self.entity:GetPosition(false) --true for global if self.child ~= nil then if self.parent ~= nil then self.joint = Joint:Hinge(pos.x, pos.y, pos.z, self.pin.x, self.pin.y, self.pin.z, self.child, self.parent) else Debug:Error("no parent assigned") end else Debug:Error("no child assigned") end end function Script:setMotorSpeed(speed) if self.velcontrolled then System:Print("setMotorSpeed: "..speed) self.currspeed = speed if speed~=0 then self.joint:EnableMotor() end self.joint:SetMotorSpeed(self.currspeed) end end function Script:UpdateWorld() if self.motorspeed>0 then self.joint:SetAngle(self.joint:GetAngle()+100) else self.joint:SetAngle(self.joint:GetAngle()-100) end if App.window:KeyDown(Key.Space) then self:setMotorSpeed(0) end if self.velcontrolled then if App.window:KeyDown(Key.Up) then self.currspeed = self.currspeed + 10 if self.currspeed>self.motorspeed then self.currspeed=self.motorspeed end if self.currspeed == 10 then self.joint:EnableMotor() end self.joint:SetMotorSpeed(self.currspeed) end if App.window:KeyDown(Key.Down) then self.currspeed = self.currspeed - 10 if self.currspeed<-self.motorspeed then self.currspeed=-self.motorspeed end self.joint:SetMotorSpeed(self.currspeed) end end end Here is the steer scritp
    Script.parent = nil--Entity "Parent" Script.child = nil--Entity "Child" Script.pin = Vec3(0,1,0) --Vec3 "Hinge Pin" Script.useLimits=false--bool "use limits" Script.limits = Vec2(-45,45) --Vec2 "Limits" Script.useMotor=flase--bool "use motor" Script.motorspeed=50--float "Steer speed" function Script:Start() System:Print("steer start") if self.child == nil then Debug:Error("No child assigned.") end if self.parent == nil then Debug:Error("No parent assigned.") end self.entity:Hide() local pos = self.entity:GetPosition() self.joint = Joint:Hinge(pos.x, pos.y, pos.z, self.pin.x, self.pin.y, self.pin.z, self.child, self.parent) if self.useLimits then self.joint:EnableLimits() self.joint:SetLimits(self.limits.x,self.limits.y) end if self.useMotor then self.joint:SetMotorSpeed(self.motorspeed) self.joint:EnableMotor() end end function Script:UpdateWorld() local direction=0 if App.window:KeyDown(Key.Left) then direction=self.limits.x end if App.window:KeyDown(Key.Right) then direction=self.limits.y end self.joint:SetAngle(direction) end  
    here the distro:
    simpleCarDistro.zip
    here the project:
    simpleCar.zip
    and here a video...
     
    enjoy
     
    Juan
  5. Charrua
    6) How to expose a Cpp Class to LUA using tolua++
     
    At a glance:
     
    A way to create instances of objects defined in Cpp from LUA scripts, so functions created in Cpp side should be called from LUA side. Somewhat the opposite direction of items 4, 5 and 6 of this series.
     
    The way is not so difficult once you travelled it, as always!
     
    First you have to write a header with your class declaration, then you have to use tolua++ application to translate it to LUA
     
    I'll started a new cpp empty project, so i have only:
     
    Main.cpp, App.h and App.cpp
     
    Added:
    cToLuaTest.h declaration of the class
    cToLuaTest.cpp implementation of the class
    cToLua translation to lua generated by tolua++
     
    cToLua.cpp si generated by tolua++ with the following command line:
     
    (on a terminal good great and legendary DOS window.. haa those times!)
     
    tolua++ -o cToLua cToLuaTest.h    
    the command line explained:
     
    a) first parameter -o nomGeneratedFile :desired name of outputfile
    b) second parameter cToLuaTest.h :name of input file
     
    this generates a long.. more than expected file, that at the end has a function:
    https://dl.dropboxusercontent.com/u/78894295/leadwerks/cToLua.cpp
     
    note: you will have to include as source this file, so name it differently than the
    cToLuaTest.cpp which implements the class. If you do not especify -o, then the same name as input is assumend, becarefull
     
    inside the cToLua.cpp generated by tolua++, almost at the end there is a special function:
     
    /* Open function */ TOLUA_API int tolua_cToLua_open(lua_State* tolua_S) { ... }    
    which is used to bind the class to lua.
     
    as a helper (from one of the threads cited on first tut) i adopted to write another header to declare the above function.
     
    lua_gluecode.h
    #pragma once #include "Leadwerks.h" using namespace Leadwerks; /* Exported function */ TOLUA_API int tolua_clasetolua_open(lua_State* tolua_S);    
     
    Following the class declaration and the implementation
    class cToLuaTest { public: cToLuaTest(void){}; ~cToLuaTest(void){}; void luaCalling(void); float average(void); float aFloat; int intA, intB, intC; };    
    #include "cToLuaTest.h" //prototypes #include "Leadwerks.h" //uso System de leadwerks por eso la inclusion using namespace Leadwerks; void cToLuaTest::luaCalling(void){ System::Print("lua is calling me!"); } float cToLuaTest::average(void){ return (float)(intA+intB+intC)/3.0f; }    
    Did you expected something more elaborated? sorry then
     
    Now the bind part, in Main.app:
     
    1) include the file lua_gluecode.h
    2) the following couple of sentences (before app->start)
     
    #include "lua_gluecode.h" ... int main(int argc,const char *argv[]) { ... many many lines away of default main.cpp main function ... else { if (Interpreter::L == NULL) Interpreter::Reset(); //--- this line tolua_cToLua_open(Interpreter::L); // and this line //Execute mobile-style App script App* app = new App; if (app->Start()) { while (app->Loop()) {} ... rest of main.cpp }    
    build and voila!, you have all done cpp side!
    now, go LUA side.
     
    as a test, in App.lua, function Start:
     
    function App:Start() ... --Load a map local mapfile = System:GetProperty("map","Maps/start.map") if Map:Load(mapfile)==false then return false end System:Print("app.lua start") -- cToLuaTest object created in lua c2luaTest = cToLuaTest:new() --create an instance of cToLuaTest class object if c2luaTest ~= nil then --test if ok System:Print("c2luaTest not nil") c2luaTest:luaCalling() --use one of it's methods, just print "lua is calling me!" on the cpp side end --set some properties c2luaTest.intA = 10 c2luaTest.intA = 35 c2luaTest.intA = 15 --call average an print out the result System:Print(c2luaTest:average()) return true end --of App:Start    
    so easy, isn't it?
     
     
    the end
     
    Home, Previous
  6. Charrua
    5) How to receive returned parameters from LUA functions
    (yes one or more)
    come on, every time i edit the post, code /code get bad formatted and have to manually (again) correct it (really don't like to see un-indented code).
     
     
    So, with an example will see how to receive parameters from LUA function scripts.
     
    The example is basically the same as the one exposed in the previous tut (5/7) the only difference is that the function called has a Return sentence so is sending us something, and following the Invoke method, we have to catch that value.
    It's good to test if the kind of value received is of the correct type and if so, then we have to convert it from LUA type to Cpp type, here an example:
     
    LUA function declared on a script attached to the entity used next
     
    function Script:average(intA, intB, intC) System:Print("executing average") return (intA + intB + intC)/3 end    
     
    float call_average(Entity* e, string funcName, int a, int b, int c) { if (e->component == NULL) return false; bool success = false; //Get the component table int stacksize = Interpreter::GetStackSize(); //Get the global error handler function int errorfunctionindex = 0; #ifdef DEBUG Interpreter::GetGlobal("LuaErrorHandler"); errorfunctionindex = Interpreter::GetStackSize(); #endif e->Push(); Interpreter::GetField("script"); if (Interpreter::IsTable()) { System::Print(" entity has script"); Interpreter::GetField(funcName); if (Interpreter::IsFunction()) { System::Print(" " + funcName + " defined"); Interpreter::PushValue(-2);//Push script table onto stack Interpreter::PushInt(a); Interpreter::PushInt(b); Interpreter::PushInt(c); #ifdef DEBUG errorfunctionindex = -(Interpreter::GetStackSize() - errorfunctionindex + 1); #endif success = Interpreter::Invoke(4, 1, errorfunctionindex); //in=4, out=1 // //---------------------------- here the receiving part ******NEW****** // if (success) { if (Interpreter::IsNumber()) { float retValue = Interpreter::ToNumber(); System::Print(retValue); return retValue; } } /* //how to retrieve more than one returned parameter: //if function returns two or more parameters, they are poped in inverse order //so be careful to ask for them in reverse order (from right to left) Interpreter::Pop(); //pop it if (Interpreter::IsNumber()) { float retValue = Interpreter::ToNumber(); System::Print(retValue); } */ } } Interpreter::SetStackSize(stacksize); }    
    Types that can be tested:
    Interpreter::isBool, isFunction, isBreakpoint, isNumber, isConnected, isString, isObject, isTable
     
    Types that can be converted to:
    Interpreter::toNumber, toString, toBool o toObject
     
    not so easy but with a recipe all things are made easy, no?
     
    just in case you missed it on the code...
    if you like you may return more than one value from LUA
    LUA admit Return to give a list of comma separated values
     
    Return value1, value1, ...
     
    they are pushed and when popped, came in reverse order
     
    you have to use
    Interpreter::pop()
    or retrieving each value and then testing it's type and converting back from lua type to c++ type
     
    next tut is more long and perhaps i'll post later...
     
    Next, Home, Previous
  7. Charrua
    4) How to call an entity's script LUA function passing parameters
     
    following code is some what weird but you only have to pay attention on a few things
     
    the idea is Push the parameters in the order they are declared in the lua function (from left to right)
    and pay attention in the Interpreter:Invoke method. You have to set the first parameter (number of staked values) one more of the amount of parameters passed.
     
    consider the following LUA function:
     
    function Script:turnMe(turn) System:Print("calling me from C") self.entity:Turn(turn.x,turn.y,turn.z,true) end    
    note that "turn" is a Vec3 variable
     
    so you have to push the correct type of variable to lua stack and then tell Interpreter::Invoke that there are 2 things on the stack (as the code explains, one is always present: thank's Josh for that beautiful piece of code!)
     
    here is a generic function to call any LUA script function that has one parameter of type Vec3:
    bool call_turnMe(Entity* e, string funcName, Vec3 vec3){ if (e->component == NULL) return false; bool success = false; //Get the component table int stacksize = Interpreter::GetStackSize(); //Get the global error handler function int errorfunctionindex = 0; #ifdef DEBUG Interpreter::GetGlobal("LuaErrorHandler"); errorfunctionindex = Interpreter::GetStackSize(); #endif e->Push(); Interpreter::GetField("script"); //test if entity has a script if (Interpreter::IsTable()) { System::Print(" entity has script"); Interpreter::GetField(funcName); if (Interpreter::IsFunction()) //test if funcName is a function { System::Print(" " + funcName + " defined"); Interpreter::PushValue(-2); //Push script table onto stack Interpreter::PushVec3(vec3); //we only pass one variable but stack has one more #ifdef DEBUG errorfunctionindex = -(Interpreter::GetStackSize() - errorfunctionindex + 1); #endif success = Interpreter::Invoke(2, 0, errorfunctionindex); // send 2 params, receive none (next tut for receive parameters) } } Interpreter::SetStackSize(stacksize); //keep stack under control }    
    Interpreter has a nice amount of pushes:
     
    pushAABB, pushFloat, pushInt, pushString, pushCObject, pushObject, pushBool, pushNull,
    pushMat3, pushMat4, pushVec2, pushVec3, pushVec4, pushValue
     
    just remember: push parameters with the correct type and in order in which they was declared in the LUA function.
     
    next tut, show how to pass 3 ints and receive a float, and also show how to receive more than one value from a lua script, because Return in lua may contain a list of values.
     
    that's it
     
    Next, Home, Previous
  8. Charrua
    3) How to call an entity's script LUA function easely (without parameters)
     
    That's really easy. Suppose you have an entity in your scene with a script attached in which a function "noParams" has been declared
     
    function Script:noParams() System:Print("executing noParams") end    
    using the loop exposed on tut 2/7 of this series, you can find that entity and then, to call one of their functions you only have to do:
     
    entity->CallFunction("noParams"); // yes only the name    
    also you may check if the entity has a script at all:
     
    this is a more generic function to call functions with no parameters:
    void call_noParams(Entity* e, string funcName){ if (e->script != NULL) e->CallFunction(funcName); // CallFunction only if entity has a script attached }    
    that's it!
     
    Next, Home, Previous
  9. Charrua
    2) How to send objects created by Cpp code to LUA
     
    Here is a way of sendign CPP created objects to LUA scripts as GLobal Variables, so
    any LUA script on you map can simply use it.
     
    I create on cpp: window, world and context and pass them this way:
     
    System::Print("sending objects...."); stacksize = Interpreter::GetStackSize(); Interpreter::PushObject(window); Interpreter::SetGlobal("window"); Interpreter::PushObject(context); Interpreter::SetGlobal("context"); Interpreter::PushObject(world); Interpreter::SetGlobal("world"); Interpreter::SetStackSize(stacksize);    
    so simple, only after knowing it!
     
    i modify App.lua to:
    not define window, world and context and use them as:
    if window:Closed() or window:KeyDown(Key.Escape) then --logFile:Release() return false end --Update the app timing Time:Update() --Update the world world:Update() --Render the world world:Render()   note : that's a fragment of App.lua main loop (function App:Loop() )
     
    instead of the default:
    if self.window:Closed() or self.window:KeyDown(Key.Escape) then --logFile:Release() return false end --Update the app timing Time:Update() --Update the world self.world:Update() --Render the world self.world:Render()    
    with tuts 1 and 2 we has ways of sending Leadwerks objects (any Leadwerk's object) to LUA and retrieving LUA objects.
    Objects sent to lua, will be global to any function on any LUA scritp.
     
    Next, Home, Previous
  10. Charrua
    1) How to get objects created on the editor from Cpp
     
    Easy as it seems, getting an editor created object from cpp code is done basically looping on the entities collection, but there are some things that is good to know.
     
    First one simple way of doing it:
     
    System::Print("start scanning...."); for (auto iter = world->entities.begin(); iter != world->entities.end(); iter++) { Entity* entity = *iter; System::Print(entity->GetKeyValue("name")); if (entity->GetKeyValue("name") == "findMe"){ System::Print(" found entity findMe !!!"); if (entity->script != NULL){ foundEntity = entity; } } }    
    It's supposed that you have a map with an entity named "findMe"
    if you like you can use: SetKeyValue to give some "custom" attributes to your entities and then
    instead of testing by a already existing key like name you may test by any custom key of your's
     
    if (entity->GetKeyValue("room_number") == "8"){ System::Print(" entity of room 8"+entity->GetKeyValue("name")); if (entity->script != NULL){ foundEntity = entity; } }    
    entities collection has all of them, cameras, lights, models, etc but in some particular situations searching for specific class is necessary: cameras, light has special methods and to work properly it's better to use the correct object.
     
    for example to get a camera created on the editor you may:
     
    //now look for the editor camera: for (Camera* e : world->cameras) { System::Print(e->GetKeyValue("name")); if (e->GetKeyValue("name") == "EditorCamera"){ camera = e; System::Print("found editor camera"); } }    
    note "camera" is declared on app.h as: Camera* camera; so it's a global already defined
     
    that's it, so simple, he?
     
    Next, Back
×
×
  • Create New...