Irrlicht + Box2D (3D Graphics + 2D Physics)

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Post Reply
jorgerosa
Competition winner
Posts: 117
Joined: Wed Jun 30, 2010 8:44 am
Location: Portugal
Contact:

Irrlicht + Box2D (3D Graphics + 2D Physics)

Post by jorgerosa »

Irrlicht + Box2D (Integrated)

This Code Snippet: Box2D (2D Physics engine) integrated with Irrlicht (3D Graphics engine) successfully!
Models are: One bouncing ball and the ground. I´m sharing this code with you all, but I need help too...

• Everything there works great (until now...) but I need to integrate the cobra function ( http://irrlicht.sourceforge.net/forum// ... hp?t=42350 ), or something similar, in this code.
Can anyone help, please?... - The idea is to have a visual debugger (showing where the physic bodies are), but drawn by the irrlicht itself (not the Box2D debug draws, or any other external debugger).
• I just couldn´t make that cobra code work in this project, I am not so skilled as cobras, snakes, sheeps and tigers... But I can beat snails... :)

Code: Select all

 
#pragma once
 
/// System headers: (These headers are not all nedded in this sample code, but just in case...)
#include <fstream>
#include <iostream>
#include <exception>
#include <sstream>
#include <vector>
#include <string>
#include <cstring>
#include <stdio.h>
#include <dirent.h>
#include <functional>
#include <algorithm>
#include <ctype.h>
#include <ctime>
#include <cstdlib>
#include <iomanip>
#include <cmath>
#include <stdint.h>
#include <iterator>
#include <numeric>
#include <tchar.h>
using namespace std;
 
/// Irrlicht headers:
#include <irrlicht.h>
using namespace irr;
using namespace core;
using namespace video;
using namespace scene;
using namespace io;
using namespace gui;
 
/// Box2D headers:
#include <Box2D/Box2D.h>
#include <Box2D/Common/b2Math.h>
 
 
 
/// Irrlicht Global Variables #1:
irr::u32 SCREEN_WIDTH  = 1280;
irr::u32 SCREEN_HEIGHT = 720;
bool FULLSCREEN        = false;
bool STENCIL_SHADOWS   = false;
bool VSYNC             = true; // Limit rendering to the screen refresh rate (true == to avoid crashes and old hardware damages)
 
/// Irrlicht Global Variables #2:
IrrlichtDevice* device           = NULL; // Create Device
irr::video::IVideoDriver* driver = NULL; // Create Driver
irr::scene::ISceneManager* smgr  = NULL; // Create Scene Manager
irr::io::IFileSystem* filesys    = NULL; // File System;
/// Window Text:
stringw windowtexts = L"Irrlicht vs Box2D ... Fight!";
 
/// Common Global Variables and Formulas:
#define BOXTOIRR_SCALE 30.0f /// Pixels per meter
#define IRRTOBOX_SCALE (1.0f/BOXTOIRR_SCALE) /// Meters per pixel
#define DEGTORAD 0.0174532925199432957f
#define RADTODEG 57.295779513082320876f
#define PI 3.141592653589793238463f
 
/// Box2D Global Variables:
b2World* world;
  
 
 
 
/// Start:
int main(int argc, char** argv){
 
 
    /// Loop to chose driver from all available:
    video::E_DRIVER_TYPE driverType;
    for (int i=0; i<irr::video::EDT_COUNT; i++){
    if (irr::IrrlichtDevice::isDriverSupported(irr::video::E_DRIVER_TYPE(i))){ driverType = irr::video::E_DRIVER_TYPE(i); };
    };
 
    /// Create device from the above found driver:
    device = createDevice(driverType, core::dimension2d<u32>(SCREEN_WIDTH, SCREEN_HEIGHT), 16, FULLSCREEN, STENCIL_SHADOWS, VSYNC, 0);
    if (device == 0){ return 0; }; // If device fails... Exit...
 
    driver  = device->getVideoDriver();  // Create Driver
    filesys = device->getFileSystem();   // Load data
    smgr    = device->getSceneManager(); // Create Scene Manager
 
    /// Global light:
    smgr->setAmbientLight(video::SColorf(1.0, 1.0, 1.0, 0.0)); // Ambient Light. SColorf == r, g, b, a (0 = transparent | 255 = opaque)
 
    /// Device - Other Stuff: (Window)
    device->setWindowCaption(windowtexts.c_str());
    device->getCursorControl()->setVisible(true);  // Mouse cursor visibility
    device->setResizable(true);                    // Resizable window
 
    /// Load Data: ( "assets" is the name of the folder where I have my 3D models and textures )
    device->getFileSystem()->addFileArchive("assets", true, true, EFAT_ZIP, "none");
    
    
    /// -----------------------------------------------------------------------------------------------------   
    
    
    /// Add Camera:
    scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0, core::vector3df(0,0,0), core::vector3df(0,0,0), -1, true);
    camera->setPosition(core::vector3df(0,800,10000));
    camera->setRotation(core::vector3df(0,0,0));
    camera->setTarget(core::vector3df(0,800,-10000));
    camera->setFOV(0.20f); /// To make the (almost) 2D view effect
    camera->setFarValue(20000.0f);  
    
    
    /// Add Ball Node:
    IAnimatedMeshSceneNode* node1 = smgr->addAnimatedMeshSceneNode(smgr->getMesh("ball.obj")); // Any other mesh will work too, it just for physics tests
    node1->setPosition(core::vector3df(-500,1000,0));
    node1->setRotation(core::vector3df(0.0f,0.0f,0.0f));
    node1->setScale(core::vector3df(1.0f,1.0f,1.0f));
    node1->setMaterialFlag(video::EMF_LIGHTING, true);           // Node is affected by LIGHT?
    node1->setMaterialFlag(video::EMF_FOG_ENABLE, false);        // Node is affected by FOG?
    node1->setMaterialFlag(video::EMF_BACK_FACE_CULLING, true);  // Render both sides?
    node1->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, true);
    node1->setMaterialFlag(video::EMF_ANISOTROPIC_FILTER, true); // Increase view distance quality (similar to sharpness)
    /// Textures:
    for(u32 i=0; i<node1->getMaterialCount(); i++){
    node1->getMaterial(i).setTexture(0, driver->getTexture("ball_texture.jpg"));
    node1->getMaterial(i).getTextureMatrix(0).setTextureScale(1.0f,1.0f); /// Tile the texture
    node1->getMaterial(i).EmissiveColor = video::SColor(255,150,150,150);
    };
    
    
    /// -----------------------------------------------------------------------------------------------------
    
    
    /// Initialize the world of physics:
    world = new b2World(b2Vec2(0.0f, -10.0f));
    
    
    /// -----------------------------------------------------------------------------------------------------   
 
    
    /// Add physics for the ball node: (This one will have a cirle shape)
    b2BodyDef def1;
    def1.type = b2_dynamicBody; /// b2_dynamicBody ==> ball | b2_kinematicBody ==> ground
    def1.position = b2Vec2( -100*IRRTOBOX_SCALE, 1000*IRRTOBOX_SCALE);
    def1.angle = 0.0f;
    def1.linearVelocity.Set(0.0f, 0.0f);
    def1.angularVelocity = 0.0f;
    def1.linearDamping = 0.0f;
    def1.angularDamping = 0.0f;
    def1.allowSleep = true;
    def1.awake = true;
    def1.fixedRotation = false;
    def1.bullet = false;
    def1.active = true;
    def1.userData = (void*)"ball_def";
 
    b2CircleShape shape1;
    shape1.m_p.Set(0,0); /// Set origin relative to body
    core::vector3df nodesize(node1->getTransformedBoundingBox().getExtent()); /// Get height from irrlicht node, then send it to box2d:
    shape1.m_radius = nodesize.Y*IRRTOBOX_SCALE;
 
    b2FixtureDef fix1;
    fix1.shape = &shape1;
    fix1.density = 60;
    fix1.restitution = 0.65f;
    fix1.friction = 1.0f;
    fix1.userData = (void*)"ball_fix";
 
    b2Body* body1;
    body1 = world->CreateBody(&def1);
    body1->CreateFixture(&fix1);
    
    
    /// -----------------------------------------------------------------------------------------------------   
    
    
    /// Add physics for the ground: (No related node, yet... This one will have a rectangle shape)
    b2BodyDef def2;
    def2.type = b2_kinematicBody; /// b2_dynamicBody ==> ball | b2_kinematicBody ==> ground
    def2.position = b2Vec2( -(100*IRRTOBOX_SCALE), (100*IRRTOBOX_SCALE) );
    def2.angle = 0.05f; /// Add just a small angle to the ground
    def2.linearVelocity.Set(0.0f, 0.0f);
    def2.angularVelocity = 0.0f;
    def2.linearDamping = 0.0f;
    def2.angularDamping = 0.0f;
    def2.allowSleep = true;
    def2.awake = true;
    def2.fixedRotation = true;
    def2.bullet = false;
    def2.active = true;
    def2.userData = (void*)"ground_def";
 
    b2PolygonShape shape2;
    shape2.SetAsBox(2000.0f, 5.0f);
 
    b2FixtureDef fix2;
    fix2.shape = &shape2;
    fix2.density = 0;
    fix2.restitution = 0.0f;
    fix2.friction = 1.0f;
    fix2.userData = (void*)"ground_fix";
 
    b2Body* body2;
    body2 = world->CreateBody(&def2);
    body2->CreateFixture(&fix2);
 
    
    /// -----------------------------------------------------------------------------------------------------
 
    
    /// Initializing Timers:
    f32 deltatime = 0.0;
    f32 lastFPS = -1;
    u32 then = device->getTimer()->getTime();
    float32 timestep = 1.0f/250.0f;
    int32 velocityiterations = 6;
    int32 positioniterations = 2;
 
 
        /// Main Loop:
        while(device->run()){
 
            /// Clear:
            driver->beginScene(true, true, video::SColor(0,0,0,0));
 
            /// Calculate frame delta time:
            const u32 now = device->getTimer()->getTime();
            deltatime = (f32)(now - then);
            then = now;
 
            /// Physics time:
            world->Step(deltatime*timestep, velocityiterations, positioniterations);
           
            /// Reset all physics forces:
            world->ClearForces();
 
 
            /// 1) Calculate ball NODE position according its related BODY physics position:
            b2Vec2 physpos = body1->GetPosition();
            core::vector3df irrpos = node1->getPosition();
            irrpos.X = physpos.x*BOXTOIRR_SCALE;
            irrpos.Y = physpos.y*BOXTOIRR_SCALE;
            /// Apply position to the node:
            node1->setPosition(irrpos);
 
            /// 2) Calculate ball NODE rotation according its related BODY physics rotation:
            f32 irrrotZ = body1->GetAngle()*RADTODEG;
            /// Apply rotation to the node:
            node1->setRotation(core::vector3df(node1->getRotation().X, node1->getRotation().Y, irrrotZ));
 
 
            /// Start render:
            smgr->drawAll();
 
            /// RESULT: All is set. Everything (graphics and physics) should be running fine now!...
            /// BUT... For a visual physics debugger, we will need more code to be added in this place...
            /// NEXT STEP: My tries to create a visual physics debugger are in the nexts posts...
 
            /// Ends render:
            driver->endScene();
 
        };
 
 
    /// Free and close Irrlicht:
    if(device){ device->closeDevice(); };
    if(device){ device->drop(); };
    /// Free and close Box2D:
    /// Later...
    return 0;
    /// Gone for a beer... But brb...
 
};
 
Last edited by jorgerosa on Thu Dec 27, 2018 5:48 am, edited 29 times in total.
jorgerosa
Competition winner
Posts: 117
Joined: Wed Jun 30, 2010 8:44 am
Location: Portugal
Contact:

Re: Irrlicht + Box2D (Physics)

Post by jorgerosa »

• Ok, I´ve figured a way to "debug draw" Box2D circle shapes (full code in this post),
but I need to do it to all the other Box2D shapes (polygons) too... Help, please?...

EDIT: Just an idea - More ways to draw in 2D in irrlicht:
http://irrlicht.sourceforge.net/docu/cl ... 853b944ad4
Maybe using irrlicht "IVideoDriver::draw2DLine" to connect and draw all the vertices in the Box2D bodies shapes...
But how can we do this?... :(

Code: Select all

      
/// Just an handy external function to convert any 3D to 2D positions:
/// (Converts from 3D models coordinates into 2D screen coordinates)
position2d<s32> Convert3DTo2DPositions(core::vector3df pos3D) {
 
    s32 sw = driver->getScreenSize().Width;
    s32 sh = driver->getScreenSize().Height;
    sw/=2;
    sh/=2;
 
    f32 transformedPos[4];
    core::matrix4 trans;
    trans*=smgr->getActiveCamera()->getProjectionMatrix();
    trans*=smgr->getActiveCamera()->getViewMatrix();
    transformedPos[0]=pos3D.X;
    transformedPos[1]=pos3D.Y;
    transformedPos[2]=pos3D.Z;
    transformedPos[3]=1.0f;
    trans.multiplyWith1x4Matrix(transformedPos);
    f32 zDiv = transformedPos[3] == 0.0f ? 1.0f : (1.0f/transformedPos[3]);
 
    s32 screenx = (s32)(sw*transformedPos[0]*zDiv)+sw;
    s32 screeny = ((s32)(sh-(sh*(transformedPos[1]*zDiv))));
 
    return position2d<s32>(screenx, screeny);
};
 
 
/// --------------------------------------------------------------------------------------------------
/// Now, in the main loop, the code is EXACTLY as is in the first post, plus this 1st "debug draw" try:
/// --------------------------------------------------------------------------------------------------
 
        (...)
 
        /// Start render:
        smgr->drawAll();
 
        /// Converting 3D to 2D positions: (In this case: 3D positions from Box2D, to 2D position to Irrlicht)
        core::vector3df pos3D = core::vector3df(body1->GetPosition().x*BOXTOIRR_SCALE, body1->GetPosition().y*BOXTOIRR_SCALE, 0); /// Get body position (in Box2D world)
        position2d<s32> pos2D = Convert3DTo2DPositions(pos3D);
        /// Get body radius:
        f32 radius = body1->GetFixtureList()->GetShape()->m_radius*BOXTOIRR_SCALE;
        
        /// Finally we draw our "physics debugger":        
        driver->draw2DPolygon(core::position2d<s32>(pos2D.X, pos2D.Y), radius, video::SColor(255,255,0,0), 30);
 
       /// And... works great with Box2D circle shapes!...
       /// BUT... we will need to draw all the other kind of shapes (polygons) too... !!! HEEELP !!!...
 
       /// Just an idea - More ways to draw in 2D in irrlicht:
       /// http://irrlicht.sourceforge.net/docu/classirr_1_1video_1_1_i_video_driver.html#aaf1318379f3d70c9347cfa853b944ad4
       /// Maybe using irrlicht "IVideoDriver::draw2DLine" to connect and draw all the vertices in the Box2D bodies shapes...
       /// But how can we do this?... :( 
 
        /// Ends render:
        driver->endScene();
 
        (...)
 
Last edited by jorgerosa on Thu Dec 27, 2018 5:37 am, edited 1 time in total.
jorgerosa
Competition winner
Posts: 117
Joined: Wed Jun 30, 2010 8:44 am
Location: Portugal
Contact:

Re: Irrlicht + Box2D (3D Graphics + 2D Physics)

Post by jorgerosa »

This is my second try for a "physics debug draw", I´ll post the code here for someone to change it, and HELP, please...
Contains all code (even the messy one), so could maybe bring you some ideas... or not...

Code: Select all

 
/// Just an handy external function to convert any 3D to 2D positions:
/// (Converts from 3D models coordinates into 2D screen coordinates)
position2d<s32> Convert3DTo2DPositions(core::vector3df pos3D) {
 
    s32 sw = driver->getScreenSize().Width;
    s32 sh = driver->getScreenSize().Height;
    sw/=2;
    sh/=2;
 
    f32 transformedPos[4];
    core::matrix4 trans;
    trans*=smgr->getActiveCamera()->getProjectionMatrix();
    trans*=smgr->getActiveCamera()->getViewMatrix();
    transformedPos[0]=pos3D.X;
    transformedPos[1]=pos3D.Y;
    transformedPos[2]=pos3D.Z;
    transformedPos[3]=1.0f;
    trans.multiplyWith1x4Matrix(transformedPos);
    f32 zDiv = transformedPos[3] == 0.0f ? 1.0f : (1.0f/transformedPos[3]);
 
    s32 screenx = (s32)(sw*transformedPos[0]*zDiv)+sw;
    s32 screeny = ((s32)(sh-(sh*(transformedPos[1]*zDiv))));
 
    return position2d<s32>(screenx, screeny);
};
 
 
 
/// --------------------------------------------------------------------------------------------------
/// Now, in the main loop, the code is EXACTLY as is in the first post, plus this "2nd debug draw" try:
/// --------------------------------------------------------------------------------------------------
 
        (...)
 
        /// Start render:
        smgr->drawAll();
        
 
        /// Debug Box2D physics:
        if(true){
        b2Body* body = world->GetBodyList();
        while(body != NULL) {
        b2Fixture* fixt = body->GetFixtureList();
        while(fixt != NULL) {
 
        /// IF its a Box2D circle shape:
        if(fixt->GetType() == b2Shape::e_circle){
        b2CircleShape* circle = (b2CircleShape*)fixt->GetShape();
        /// 1) Convert 3D to 2D positions:
        core::vector3df pos3D = core::vector3df(body->GetPosition().x*BOXTOIRR_SCALE, body->GetPosition().y*BOXTOIRR_SCALE, 0); /// Get body position (in Box2D world)
        position2d<s32> pos2D = Convert3DTo2DPositions(pos3D);
        /// Get circle shape radius:
        f32 radius = circle->m_radius*BOXTOIRR_SCALE;
        /// 2) Draw it:
        driver->draw2DPolygon(core::position2d<s32>(pos2D.X, pos2D.Y), radius, video::SColor(255,255,0,0), 30); /// a,r,g,b
        };
 
        /// IF its a Box2D poly shape:
        if(fixt->GetType() == b2Shape::e_polygon){
        b2PolygonShape* poly = (b2PolygonShape*)fixt->GetShape();
 
        float minx = FLT_MAX, miny = FLT_MAX;
        float maxx = FLT_MIN, maxy = FLT_MIN;
        for(int i=0; i<poly->m_count; i++){ /// poly->m_count ==> poly->GetVertexCount()
            b2Vec2 vertexWorldPos = body->GetWorldPoint(poly->m_vertices[i]); /// poly->m_vertices[i] ==> poly->GetVertex(i)
            minx = b2Min(minx, vertexWorldPos.x);
            miny = b2Min(miny, vertexWorldPos.y);
            maxx = b2Max(maxx, vertexWorldPos.x);
            maxy = b2Max(maxy, vertexWorldPos.y);
            
            // THESE ARE ONLY MY TRIES AND MESSING WITH STUFF, BUT TTHEY ARE HERE, MAYBE FOR IDEAS...
 
            // driver->draw2DPolygon(core::position2d<s32>(minx, miny), 30, video::SColor(255,0,0,255), 30); /// a,r,g,b
            // driver->draw2DPolygon(core::position2d<s32>(maxx, maxy), 30, video::SColor(255,0,0,255), 30); /// a,r,g,b
 
            // TRYING TO DRAW A LINE, CONNECTING 2 VERTICES...
            driver->draw2DLine(core::position2d<s32>(minx, miny), core::position2d<s32>(maxx, maxy), video::SColor(255,255,0,0));
 
            /// driver->draw2DRectangle(SColor(100,255,0,0), rect<s32>(minx*BOXTOIRR_SCALE, miny+20*BOXTOIRR_SCALE, maxx*BOXTOIRR_SCALE, maxy+20*BOXTOIRR_SCALE), 0);
 
            /// 1) Convert 3D to 2D positions:
            core::vector3df pos3D = core::vector3df(body->GetPosition().x*BOXTOIRR_SCALE, body->GetPosition().y*BOXTOIRR_SCALE, 0); /// Get body position (in Box2D world)
            position2d<s32> pos2D = Convert3DTo2DPositions(pos3D);
            /// Get poly shape radius: (Even its a poly shape, not circle, we still can retrieve its radius)
            f32 radius = poly->m_radius*BOXTOIRR_SCALE*500;
            cout << "radius: " << radius << endl;
            /// 2) Draw it: (Draws a circle where sould be a squared object (ground), but in case...
            driver->draw2DPolygon(core::position2d<s32>(pos2D.X, pos2D.Y), radius, video::SColor(255,0,255,0), 30); /// a,r,g,b
 
 
            // cout << "minx: " << minx << " miny: " << miny << endl;
            // driver->draw2DLine(core::position2d<s32>(minx, miny), core::position2d<s32>(maxx, miny), video::SColor(255,255,0,0));
            // driver->draw2DLine(core::position2d<s32>(maxx, maxy), core::position2d<s32>(minx, maxy), video::SColor(255,255,0,0));
        };
 
 
        /*
        // Using GL (but its not what we want), the code should be someting like this:
        glBegin(GL_LINES);
        glVertex2f( minx, miny );
        glVertex2f( maxx, miny );
        glVertex2f( maxx, maxy );
        glVertex2f( minx, maxy );
        glEnd();
        */
 
        };
 
        fixt = fixt->GetNext();
        };
        body = body->GetNext();
        };
        };
        
        
        /// Ends render:
        driver->endScene();
 
        (...)
 
Post Reply