Jump to content

Blogs

Summer Game Tournament Roundup

Here are the results of the Summer Games Tournament. Make sure you update your mailing address, because posters are being sent out immediately! Invade The arcade classic "Space Invaders" has been re-imagined with modern graphics and cute 3D aliens! Constanta Constant is an abstract game about capturing cubes. Make sure you read the instructions! Death Rooms Procedurally generated levels and a lot of interesting rooms make this FPS worth trying. Watch out for traps!  

Josh

Josh

Between The Realities - Announce!

Between The Realities is an indie game in the genre of first-person shooter, which is being developed by our creative association Antology Games Studio. At the moment the game is in active development at the pre-beta stage, the release is expected in the winter of 2019. Our project is entirely inspired by oldschool games of the late 90s, such as Blood and Kingpin: Life of Crime, and by films like Streets of Fire (1984) and Dark City (1998). Annotation:
Another world. Different reality. Different definition of good and evil. Target City is a city in the middle of the Void that has remained from the planet Earth. It's a city controlled by a mysterious cult. Members of the Cult worship the Angels who betrayed the Creator's ideals. Angels are doing experiments on people, trying to teach them to live without their soul, given by a Heaven's energy. But that was before. Because tonight everything will change. The man named Jonathan, who lost memory of his previous life, was chosen by the Creator. He got the soul back and wants to give a freedom for all living citizens... And only one angel, who stayed on the right side, will help him to fight off the dark invaders. They'll win back this city. Together. And even death can’t stop them, because they are balancing between realities...

adams-antology

adams-antology

Voxel Madness

After three days of intense work, I am proud to show you this amazing screenshot: What is so special about this image? I am now successfully uploading voxel data to the GPU and writing lighting into another texture, using a texture buffer object to store the voxel positions as unsigned char uvec3s. The gray color is the ambient light term coming from the Blinn-Phong shading used in the GI direct light calculation. The next step is to create a light grid for the clustered forward renderer so that each light can be added to the calculation. Since voxel grids are cubic, I think I can just use the orthographic projection method to split lights up into different cells. In fact, the GI direct light shader actually includes the same lighting shader file that all the model shaders use. Once I have that done, that will be the direct lighting step, and then I can move on to calculating a bounce with cone step tracing. Clustered forward rendering, real-time global illumination, and physically-based rendering are all going to come together really nicely, but this is definitely one of the hardest features I have ever worked on! Here are a few wacky screenshots from the last few days. Why are half my voxels missing?! Why is only one texture layer being written to?! Ah, finally rendering to six texture layers simultaneously...

Josh

Josh

Shadow Caching

I have shadow caching working now in Turbo. This feature is already in Leadwerks Game Engine 4. The idea is that static scene geometry should not be redrawn when a dynamic object moves. Imagine a character (6000 polys) walking across a highly detailed room (100,000 polys), with one point light in the room. If we mark the scene geometry as static and the character as dynamic, then we can render a shadow map cache of the static scene once. When the character moves, the static cache is copied into the rendering buffer, and then the character is drawn on top of that, instead of re-rendering the entire scene. When used correctly, this will make a huge difference in the amount of geometry the renderer has to draw to update lighting. Here is my test. The helmet spins, causing the point light shadow to re-draw. The surrounding scene is marked as static and consists of 260,000 polys. By using shadow caching we can reduce scene polys rendered from about 540,000 to 280,000, in this case. I actually changed the light type to a point light, which is more demanding since it uses six passes to oover all faces of the cubemap. After performing optimizations, the test runs at 180 FPS, with a point light, with shadow caching enabled. Without shadow caching it ran at about 118. This is with Intel integrated graphics, so a discrete card is sure to be much faster. I also found that using variance shadow maps and multisampled shadows DO make a big difference in performance on Intel graphics (about half the framerate with 4X MSAA VSMs), but I don't think they will make any difference on a high-end card. There is still a bit of an issue with shadow updates syncing with the rendering thread, but all in all it was a good day's work.  

Josh

Josh

Steam Release & Bladequest: Chapter Two!

Hi guys! Bladequest – The First Chapter now finally is on Steam! I am very excited to see how it is going! Check it out if you want! I think it’s pretty cool to have my game on Steam :). Recently I did some redesign and restructuring, affecting my logo, the newsletter and the website. I actually shut the website down and now use another service, which is easier for me to setup and maintain, so I have more capacity to develop Chapter Two! Preproduction and conception for “Chapter Two” (I guess it will have a different name) is nearly finished. With the next installment I am going to make a huge step towards the idea of the final game. I soon will tell you more about it in one of my future videos. Don’t want to miss it? Consider subscribing to my YouTube channel! And as little bonus, here are two shots from some experimenting I did for Chapter Two. What could that be? Markus from Phodex Games! Give Feeback for Bladequest!
My Youtube Channel!
Join the Bladequest Gazette!

Phodex Games

Phodex Games

Physically-based Rendering

An online implementation of physically-based rendering in the Khronos Github was pointed out to me by @IgorBgz90 and @shadmar. This is very useful because it's an official implementation of PBR that removes a lot of guesswork. Here is my first attempt, which is not using any cubemap reflections: And here it is with cubemap reflections added: I plan to use the real-time global illumination system to generate the reflection data, instead of using environment probes. This will provide more realistic lighting that responds dynamically to changes in the environment. Thanks again to the devs who showed me this, along with the implementation they were working on. Here's one final revision:  

Josh

Josh

Adding LOD to the Model Class

The Model class is being slightly restructured to add support for built-in LOD without the need for separate entities. Previously, a list of surfaces was included in the Model class itself: class Model { std::vector<shared_ptr<Surface> > surfaces; }; This is being replaced with a new LOD class, which allows multiple lists of surfaces containing less detail to be stored in the same model: class LOD { std::vector<shared_ptr<Surface> > surfaces; }; class Model { std::vector<LOD> lods; }; To iterate through all surfaces in the first LOD, you do this: for (int i = 0; i < model->lods[0].surfaces.size(); ++i) { auto surf = lods[0].surfaces[i]; } To iterate through all LODs and all surfaces, you do this: for (int n = 0; n < model->lods.size(); ++n) { for (int i = 0; i < model->lods[n].surfaces.size(); ++i) { auto surf = lods[n].surfaces[i]; } } In the future editor, I plan to add a feature to automatically reduce the detail of a mesh, adding the simplified mesh as an additional LOD level so you can automatically generate these. How this will work with our super-efficient batching system, I am not sure of yet.

Josh

Josh

Bladequest – The First Chapter Conclusion

Hi guys, First of all check out the newest episode of DevTalk, showing some progress of the development of Bladequest: Chapter Two! Bladequest – The First Chapter is available since about one and a half month now and so far and has totally exceeded my expectations with about 2000 downloads in total. It was a very interesting project for me, and I got a lot of positive feedback and you definitely want to see more from Bladequest. I think the foundation is there, to create a really cool game. The First Chapter can be considered as finished now. As far as no major error/bug appears it will not recieve any updates and I will focus on Chapter Two, to be able to deliver much better quality and more content, as seen in The First Chapter. I recognized that the idea behind Bladequest does not get 100% clear when reading the description, or playing the game. It can easily be misunderstood as an episodic game, were in fact it is not. I definitely plan to work on this in the future and also have a cool idea how to very easily show what I have in mind with Bladequest. I am also very exciting to tell you more about the final game concept, as The First Chapter really just is a sneak peek (like a pre-alpha) of what is to come. So to be able to leave Bladequest – The First Chapter with a clear conscience, I gave the website some final touches and released Patch 1.5.0, so I can now focus on Chapter Two. The last exciting thing happening with The First Chapter is the upcoming Steam release. I am really looking forward to this and can’t wait to see how it is going. Thanks for all your support and feedback so far. I am so excited to create Chapter Two you won’t believe it :D, just have to overcome my currently bad health situation :(, to be able to work 24/7 again haha :D. Markus from Phodex Games! Give Feeback for Bladequest!
My Youtube Channel!
Your Games Wishlist so I can craft a game to your needs!

Phodex Games

Phodex Games

Umbra Sucks. Check out our Easy No-Bake Occlusion Culling

With the help of @martyj I was able to test out occlusion culling in the new engine. This was a great chance to revisit an existing feature and see how it can be improved. The first thing I found is that determining visibility based on whether a single pixel is visible isn't necessarily a good idea. If small cracks are present in the scene one single pixel peeking through can cause a lot of unnecessary drawing without improving the visual quality. I changed the occlusion culling more to record the number of pixels drawn, instead just using a yes/no boolean value: glBeginQuery(GL_SAMPLES_PASSED, glquery); In OpenGL 4.3, a less accurate but faster GL_ANY_SAMPLES_PASSED_CONSERVATIVE (i.e. it might produce false positives) option was added, but this is a step in the wrong direction, in my opinion. Because our new clustered forward renderer uses a depth pre-pass I was able to implement a wireframe rendering more that works with occlusion culling. Depth data is rendered in the prepass, and the a color wireframe is drawn on top. This allowed me to easily view the occlusion culling results and fine-tune the algorithm to make it perfect. Here are the results: As you can see, we have pixel-perfect occlusion culling that is completely dynamic and basically zero-cost, because the entire process is performed on the GPU. Awesome!

Josh

Josh

Constanta entry

Entry for tournament is on 😎 Check game information on link below and tell me this is not an odd game haha. I had  a limited amount of time for this because i go in holiday.Now its done was pretty painful even if i kept things simple , especially the debugging. Has multiplayer , and one way to capture territories. I will continue with this after tournament.   Future plans: Ai clients.
Adding more ways to capture territories. Alerts to players so they know they losing teritories.
Host a single master server for everyone to play.
Add end game and reset server data or increase the worlds count , ive not decided yet. Code cleanup     Cheers

aiaf

aiaf

Workshop car for LE4.5 - ready to drive

I worked today on publishing a version of the drivable car workshop item for Leadwerks 4.5 https://steamcommunity.com/sharedfiles/filedetails/?id=1440937059 free to use, works with the FPS player controller. Here is a demo:   enjoy!  

Marcousik

Marcousik

Accidental Masterpieces

Lighting is nearly complete. and it is ridiculously fast! There are still some visual glitches to work out, mostly with lights intersecting the camera near plane, but it's nearly perfect. I turned the voxel tree back on to see what the speed was, and to check if it was still working, and I saw this image of the level partially voxelized. The direct lighting shader I am using in the rest of the scene will be used to calculate lighting for each voxel on the GPU, and then bounces will be performed to quickly calculate approximate global illumination. This is fun stuff!

Josh

Josh

Introducing new and awesome features!

Check out my latest DevTalk video. I am showing some awesome new features coming to Bladquest – Chapter Two! I really took your feedback serious to develop some cool stuff. So check it out! Give Feeback for Bladequest!
My Youtube Channel!
Your Games Wishlist so I can craft a game to your needs!

Phodex Games

Phodex Games

Smoothed car drive - Leadwerks 4.5

I'm just happy to get this work  here is a demo: This is actually better than driving in 4.3 because cars are now  able to jump or crash without experimenting crazy rotations or whatever  👍     Here is the script to make this:   > Here the values I used:       > And here what changed: Is written in dark: ------------------------------------- function Script:Start() -- [...........] -- Construit les amortisseurs: / suspensions
local n
local pos
    for n=0,3 do
        pos=self.entity:GetPosition(true)
        self.Axes[n]:SetMass(self.TireMass)
        self.Amortisseurs[n]=Joint:Slider(pos.x, pos.y, pos.z, 0,1,0, self.Axes[n], self.entity)         self.Amortisseurs[n]:EnableLimits()
        self.Amortisseurs[n]:SetLimits(-0.25/2,0.25/2)         self.Amortisseurs[n]:SetTargetAngle(-self.Amort)    --at the middle if 0
        self.Amortisseurs[n]:SetMotorSpeed(10000)    ------------- vitesse de la pompe/ suspensions speed
        self.Amortisseurs[n]:SetStrength(self.Force)    --defatul is 1000
        self.Amortisseurs[n]:EnableMotor()    

    end -- [...........]        -----  I removed the backward suspensions with 2* forces because this only depends on the positions of the suspensions on the chassis. ----- Making them symmetrical is ok to solve this.   ----------------------------------------
function Script:UpdatePhysics()
if self.MyCar==1 and InCar>0 then     
-- Traite la direction:    
local turning=0
    local direction=self.Volants[0]:GetAngle()     if window:KeyDown(Key.Left) then
        direction=direction-5 turning=-1
    elseif window:KeyDown(Key.Right) then
        direction=direction+5 turning=1
    elseif window:KeyDown(Key.Left)==false and window:KeyDown(Key.Right)== false then 
        if Math:Round(direction)>0 then direction=direction-5
        elseif Math:Round(direction)<0 then direction=direction+5
        end
    end
    self.Volants[0]:SetAngle(direction)
    self.Volants[1]:SetAngle(direction) -- Traite l'acceleration: local Gas=0
        if window:KeyDown(Key.Up) then
            Gas=1
            self.currspeed = self.currspeed + 10
            if self.currspeed>self.SpeedMax then
                self.currspeed=self.SpeedMax
            end
        
        elseif window:KeyDown(Key.Down) then
            Gas=-1
            self.currspeed = self.currspeed - 10
            if self.currspeed<-self.SpeedMax then
                self.currspeed=-self.SpeedMax
            end
        end         if Gas==0 then  self.currspeed=0
            for n=0,3 do
            self.Rouages[n]:DisableMotor()
            end
        else
            for n=0,3 do
            if self.Rouages[n]:MotorEnabled()==false then self.Rouages[n]:EnableMotor() end
            self.Rouages[n]:SetTargetAngle(self.Rouages[n]:GetAngle()+1000)             self.Rouages[n]:SetMotorSpeed(self.currspeed)
            self.Tires[n]:SetOmega(self.Tires[n]:GetOmega()*Vec3(5,5,5))   ---------  make the car go faster
            end
        end --Smoothing: if turning==0 then
    self.entity:SetOmega(self.entity:GetOmega()*Vec3(-1,-1,-1))
else self.entity:SetOmega(0,turning*Gas,0) end     self.Axes[0]:SetOmega(self.Axes[0]:GetOmega()*Vec3(-1,-1,-1))
    self.Axes[1]:SetOmega(self.Axes[1]:GetOmega()*Vec3(-1,-1,-1))
    self.Axes[2]:SetOmega(self.Axes[2]:GetOmega()*Vec3(-1,-1,-1))
    self.Axes[3]:SetOmega(self.Axes[3]:GetOmega()*Vec3(-1,-1,-1))     self.Axes[0]:SetVelocity(self.Axes[0]:GetVelocity()*Vec3(1,0.5,1))
    self.Axes[1]:SetVelocity(self.Axes[1]:GetVelocity()*Vec3(1,0.5,1))
    self.Axes[2]:SetVelocity(self.Axes[2]:GetVelocity()*Vec3(1,0.5,1))
    self.Axes[3]:SetVelocity(self.Axes[3]:GetVelocity()*Vec3(1,0.5,1))
-- fake wheels update:
    
        self.Wheels[0]:SetMatrix(self.Tires[0]:GetMatrix())
        self.Wheels[1]:SetMatrix(self.Tires[1]:GetMatrix())
        self.Wheels[2]:SetMatrix(self.Tires[2]:GetMatrix())
        self.Wheels[3]:SetMatrix(self.Tires[3]:GetMatrix())         self.Wheels[0]:SetPosition(self.PosWheels[0]+Vec3(0,self.Amortisseurs[0]:GetAngle()/6,0))
        self.Wheels[1]:SetPosition(self.PosWheels[1]+Vec3(0,self.Amortisseurs[1]:GetAngle()/6,0))
        self.Wheels[2]:SetPosition(self.PosWheels[2]+Vec3(0,self.Amortisseurs[2]:GetAngle()/6,0))
        self.Wheels[3]:SetPosition(self.PosWheels[3]+Vec3(0,self.Amortisseurs[3]:GetAngle()/6,0))
end  

Marcousik

Marcousik

Map Viewer available to subscribers

A map viewer application is now available for beta subscribers. This program will load any Leadwerks map and let you fly around in it, so you can see the performance difference the new renderer makes. I will be curious to hear what kind of results you see with this: Program is not tested with all hardware yet, and functionality is limited.

Josh

Josh

View Your Sales in Leadwerks Marketplace

You can now view detailed sales records of your game assets in Leadwerks Marketplace. First, log into your Leadwerks account and navigate to the Leadwerks Marketplace main page. In the bottom-right, below the categories, a link to your paid files will appear. Here you can see a list of all your paid items: When you click on an item, you can see a list of people who have purchased it, along with sales dates. If you wish to give a free license to any member for any reason, you can do so by clicking the "Generate Purchase" button. A window will pop up where you can type in the member's name and add the item to their account for free. These tools give you more control over your game assets and better information on sales.

Josh

Josh

Map Loading, Materials, Shaders, and other Details

I have map loading working now. The LoadMap() function has three overloads you can use:: shared_ptr<Map> LoadMap(shared_ptr<World> world, const std::string filename); shared_ptr<Map> LoadMap(shared_ptr<World> world, const std::wstring filename); shared_ptr<Map> LoadMap(shared_ptr<World> world, shared_ptr<Stream> stream); Instead of returning a boolean to indicate success or failure, the LoadMap() function returns a Map object. The Map object gives you a handle to hang onto all the loaded entities so they don't get instantly deleted. When you want to clear the map, you can just set this variable to nullptr/NULL: auto map = LoadMap(world,"Maps/start.map"); map = nullptr; //BOOM!!! The "entities" member of the map object gives you a list of all entities loaded in the map: auto map = LoadMap(world,"Maps/start.map"); for (auto entity : map->entities) { //do something to entity } If you want to clear a map but retain one of the loaded entities, you just set it to a new variable like this. Notice we grab the camera, clear the map, but we still can use the camera: auto map = LoadMap(world,"Maps/start.map"); shared_ptr<Camera> cam; for (auto entity : map->entities) { cam = dynamic_pointer_cast<Camera>(entity); if (cam) break; } map = nullptr; //BOOM!!! cam->SetPosition(1,2,3); //everything is fine Materials and shader assignment has gotten simpler. If no material is assigned, a blank one will be auto-generated in the rendering thread. If a material has no shader assigned, the rendering thread will choose one automatically based on what textures are present. For example, if texture slots one and two are filled then the rendering thread will choose a shader with diffuse and normal maps. In most cases, you don't even need to bother assigning a shader to materials. I might even add separate animation and static shader slots, in which case materials could work for animated or non-animated models, and you wouldn't normally even need to specify the shader. Shaders now support include directives. By using a pragma statement we can indicate to the engine which file to load in, and the syntax won't trigger an error in Visual Studio Code's syntax highlighter: #pragma include Lighting.glsl Shader includes allow us to create many different shaders, while only storing the complicated lighting code in one file that all other shaders include. The #line directive is automatically inserted into the shader source at every line, so that the engine can correctly detect which file and line number any errors originated from. With this all working, I can now load maps side by side in Leadwerks 4 and in the new renderer and get actual performance benchmarks. Here's the first one, showing the example map "02-FPS Controller.map" from the First-Person Shooter game template. In Leadwerks 4, with Intel HD 4000 graphics, we get 71 FPS. (Yes, vertical sync is disabled). And with the new forward renderer we get a massive 400%+ increase in performance: I expect the results will vary a little bit across different hardware, but we can see already that on the low-end hardware the new renderer is a massive improvement. I plan to get a new build of the beta up soon so that you can try your own maps out and test the difference. Physics and scripts are presently disabled, as these systems need additional work to be usable. Oh, and look how much cleaner those shadow edges are!

Josh

Josh

Fun driving in Leadwerks 4.5

Little demo I made about driving in Leadwerks 4.5 with "self-made" vehicle (well the model is not from me) How to make ? -> https://www.leadwerks.com/community/blogs/entry/2216-simple-car-improved/  enjoy!    

Marcousik

Marcousik

Simple Car Improved

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

Charrua

Charrua

On how to loose half day

I had a window popping up every time i was closing the game.Error below: Debug Assertion Failed! Expression: __acrt_first_block == header At first i thought is something related to me not deleting all pointers or closing the app the wrong way.So i fix that up same error.
More investigating i figure it up that somehow is related to poco libs that i use to connect over tcp.If game exit code was before calling poco there was no exception. More wtf and curses for not being able to work on game and debug this. Finally solution was to rebuild poco with /MTd option, default built was with /MD and was causing incompatibilities seems. Glad i got over this , back to entry development

aiaf

aiaf

Joints

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

Charrua

Charrua

×