How to draw an ellipse?

If you are a new Irrlicht Engine user, and have a newbie-question, this is the forum for you. You may also post general programming questions here.
Post Reply
NeomerArcana
Posts: 3
Joined: Sun May 25, 2014 5:20 am

How to draw an ellipse?

Post by NeomerArcana »

Hi all,

I'm very new to the engine and new to 3D game programming in general. I've made plenty of 2D games before.

What I'm doing is trying to make a simple game featuring a randomly generated solar system. I've got some nice planets orbiting a sun and moons orbiting planets etc. These orbits are all done using fly circle animators. What I'd like to do now is draw the path of these animators.

How would I do this?

My first hope was that there's a way to extract the path from the animator as a vertex list or something, and draw that? But I cannot see anything like this.

My second though was that I would need to use the same parameters passed to the createFlyCircleAnimator call to actually create a list of n vertices that match the ellipse, and store that somewhere for drawing. Probably something like a custom scene node. However, the math makes me scratch my head.
NeomerArcana
Posts: 3
Joined: Sun May 25, 2014 5:20 am

Re: How to draw an ellipse?

Post by NeomerArcana »

So I've adapted a couple of the examples and come up with something. First off, you can see that my ellipses look... like spiderwebs. Second, the inner "planet" you can see the orbit is not on the same plane, and I'm unsure how to get an ellipse to match.

Code: Select all

/** Example 004 Movement
 
This Tutorial shows how to move and animate SceneNodes. The
basic concept of SceneNodeAnimators is shown as well as manual
movement of nodes using the keyboard.  We'll demonstrate framerate
independent movement, which means moving by an amount dependent
on the duration of the last run of the Irrlicht loop.
 
Example 19.MouseAndJoystick shows how to handle those kinds of input.
 
As always, I include the header files, use the irr namespace,
and tell the linker to link with the .lib file.
*/
#ifdef _MSC_VER
// We'll also define this to stop MSVC complaining about sprintf().
#define _CRT_SECURE_NO_WARNINGS
#pragma comment(lib, "Irrlicht.lib")
#endif
 
#include <irrlicht.h>
#include <vector>
#include "driverChoice.h"
#include <cmath>
 
using namespace irr;
 
/*
To receive events like mouse and keyboard input, or GUI events like "the OK
button has been clicked", we need an object which is derived from the
irr::IEventReceiver object. There is only one method to override:
irr::IEventReceiver::OnEvent(). This method will be called by the engine once
when an event happens. What we really want to know is whether a key is being
held down, and so we will remember the current state of each key.
*/
class MyEventReceiver : public IEventReceiver
{
public:
    // This is the one method that we have to implement
    virtual bool OnEvent(const SEvent& event)
    {
        // Remember whether each key is down or up
        if (event.EventType == irr::EET_KEY_INPUT_EVENT)
            KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
 
        return false;
    }
 
    // This is used to check whether a key is being held down
    virtual bool IsKeyDown(EKEY_CODE keyCode) const
    {
        return KeyIsDown[keyCode];
    }
 
    MyEventReceiver()
    {
        for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
            KeyIsDown[i] = false;
    }
 
private:
    // We use this array to store the current state of each key
    bool KeyIsDown[KEY_KEY_CODES_COUNT];
};
 
class Ellipse3D : public scene::ISceneNode
{
private:
    core::aabbox3d<f32> m_box;
    std::vector<core::vector3df> m_vertices;
    video::SMaterial Material;
 
public:
    Ellipse3D(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id, core::vector3df center, f32 radius, f32 ellipseRadius)
    : scene::ISceneNode(parent,mgr,id)
    {
        Material.Wireframe = false;
        Material.Lighting = false;
 
        for(int i = 0; i < 360; ++i)
        {
            f32 a2 = (i+1) % 360;
            m_vertices.push_back(core::vector3df(radius*std::cos(i),0,ellipseRadius*std::sin(i)));
        }
 
        m_box.reset(m_vertices[0]);
        for(s32 i=1; i<100; ++i)
            m_box.addInternalPoint(m_vertices[i]);
    }
    virtual void OnRegisterSceneNode()
    {
        if (IsVisible)
            SceneManager->registerNodeForRendering(this);
 
        ISceneNode::OnRegisterSceneNode();
    }
 
    virtual void render()
    {
        u16 indices[] = {   0,2,3, 2,1,3, 1,0,3, 2,0,1  };
        video::IVideoDriver* driver = SceneManager->getVideoDriver();
 
        driver->setMaterial(Material);
        driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
        for(int i = 0; i < 100; ++i)
        {
            if(i != 100)
                driver->draw3DLine(m_vertices[i], m_vertices[i+1], video::SColor(255,255,255,255));
            else
                driver->draw3DLine(m_vertices[i], m_vertices[0], video::SColor(255,255,255,255));
        }
 
    }
    virtual const core::aabbox3d<f32>& getBoundingBox() const
    {
        return m_box;
    }
 
    virtual u32 getMaterialCount() const
    {
        return 1;
    }
 
    virtual video::SMaterial& getMaterial(u32 i)
    {
        return Material;
    }
};
 
 
 
/*
The event receiver for keeping the pressed keys is ready, the actual responses
will be made inside the render loop, right before drawing the scene. So lets
just create an irr::IrrlichtDevice and the scene node we want to move. We also
create some other additional scene nodes, to show that there are also some
different possibilities to move and animate scene nodes.
*/
int main()
{
    // ask user for driver
    video::E_DRIVER_TYPE driverType=driverChoiceConsole();
    if (driverType==video::EDT_COUNT)
        return 1;
 
    // create device
    MyEventReceiver receiver;
 
    IrrlichtDevice* device = createDevice(driverType,
            core::dimension2d<u32>(1600, 900), 32, false, false, false, &receiver);
 
    if (device == 0)
        return 1; // could not create selected driver.
 
    video::IVideoDriver* driver = device->getVideoDriver();
    scene::ISceneManager* smgr = device->getSceneManager();
 
    /*
    Create the node which will be moved with the WSAD keys. We create a
    sphere node, which is a built-in geometry primitive. We place the node
    at (0,0,30) and assign a texture to it to let it look a little bit more
    interesting. Because we have no dynamic lights in this scene we disable
    lighting for each model (otherwise the models would be black).
    */
 
    scene::ISceneNode * node = smgr->addSphereSceneNode(40.0,100);
    if (node)
    {
        node->setPosition(core::vector3df(0,0,0));
        node->setMaterialTexture(0, driver->getTexture("diffuse.jpg"));
        node->setMaterialFlag(video::EMF_LIGHTING, false);
        node->setMaterialType(video::EMT_LIGHTMAP);
        node->setMaterialFlag(video::EMF_ANTI_ALIASING,true);
        scene::ISceneNodeAnimator* anim =smgr->createRotationAnimator(core::vector3df(0,0,0.02));
        node->addAnimator(anim);
    }
 
    scene::ISceneNode * nodee = node->clone();
    nodee->setScale(core::vector3df(1.5,1.5,1.5));
    nodee->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
 
 
    scene::ILightSceneNode* light = smgr->addLightSceneNode(0,core::vector3df(0,0,0),video::SColorf(1.0,1.0,0.0,0),500);
    light->setLightType(video::ELT_POINT);
    light->getLightData().AmbientColor.set(1.0,0.01,0.01,0.01);
    light->getLightData().DiffuseColor.set(1.0,1.0,1.0,1.0);
    light->getLightData().SpecularColor.set(1.0,1.0,1.0,1.0);
 
    scene::ISceneNode * node1 = smgr->addSphereSceneNode(15.0f,100);
    if (node1)
    {
        node1->setPosition(core::vector3df(0,0,500));
        node1->setMaterialTexture(0, driver->getTexture("../../media/wall.bmp"));
        node1->setMaterialFlag(video::EMF_LIGHTING, true);
        scene::ISceneNodeAnimator* anim =
            smgr->createFlyCircleAnimator(core::vector3df(0,0,0), 500.0f, 0.0004f, core::vector3df(0.f, 1.f, 0.f),0.f,300.1f);
 
        if (anim)
        {
            node1->addAnimator(anim);
            anim->drop();
        }
    }
 
    scene::ISceneNode* ellipse = new Ellipse3D(smgr->getRootSceneNode(),smgr,-1,core::vector3df(0,0,0),500,300.1);
 
    scene::ISceneNode * node2 = smgr->addSphereSceneNode(5.0,100);
    if (node2)
    {
        node2->setPosition(core::vector3df(0,0,80));
        node2->setMaterialTexture(0, driver->getTexture("../../media/wall.bmp"));
        node2->setMaterialFlag(video::EMF_LIGHTING, true);
        scene::ISceneNodeAnimator* anim =
            smgr->createFlyCircleAnimator(core::vector3df(0,0,0), 80.00f, 0.001f, core::vector3df(0.5f, 1.f, 0.f));
        if (anim)
        {
            node2->addAnimator(anim);
            anim->drop();
        }
    }
 
    scene::ISceneNode* ellipse2 = new Ellipse3D(smgr->getRootSceneNode(),smgr,-1,core::vector3df(0,0,0),80,80);
 
    /*
    To be able to look at and move around in this scene, we create a first
    person shooter style camera and make the mouse cursor invisible.
    */
    scene::ICameraSceneNode* cam = smgr->addCameraSceneNodeFPS(0,100.0f,0.5f,-1,0,0,false,0.f,false);
    cam->setTarget(core::vector3df(0,0,0));
    cam->setPosition(core::vector3df(0, 300, cam->getPosition().Z));
    cam->setFarValue(999999);
    device->getCursorControl()->setVisible(true);
 
 
    /*
    We have done everything, so lets draw it. We also write the current
    frames per second and the name of the driver to the caption of the
    window.
    */
    int lastFPS = -1;
 
    // In order to do framerate independent movement, we have to know
    // how long it was since the last frame
    u32 then = device->getTimer()->getTime();
 
    // This is the movemen speed in units per second.
    const f32 MOVEMENT_SPEED = 5.f;
 
    while(device->run())
    {
        // Work out a frame delta time.
        const u32 now = device->getTimer()->getTime();
        const f32 frameDeltaTime = (f32)(now - then) / 1000.f; // Time in seconds
        then = now;
 
        /* Check if keys W, S, A or D are being held down, and move the
        sphere node around respectively. */
        core::vector3df nodePosition = node->getPosition();
 
        if(receiver.IsKeyDown(irr::KEY_KEY_W))
            nodePosition.Y += MOVEMENT_SPEED * frameDeltaTime;
        else if(receiver.IsKeyDown(irr::KEY_KEY_S))
            nodePosition.Y -= MOVEMENT_SPEED * frameDeltaTime;
 
        if(receiver.IsKeyDown(irr::KEY_KEY_A))
            nodePosition.X -= MOVEMENT_SPEED * frameDeltaTime;
        else if(receiver.IsKeyDown(irr::KEY_KEY_D))
            nodePosition.X += MOVEMENT_SPEED * frameDeltaTime;
 
        node->setPosition(nodePosition);
 
        driver->beginScene(true, true, video::SColor(255,0,0,0));
 
        smgr->drawAll(); // draw the 3d scene
        device->getGUIEnvironment()->drawAll(); // draw the gui environment (the logo)
 
        driver->endScene();
 
        int fps = driver->getFPS();
 
        if (lastFPS != fps)
        {
            core::stringw tmp(L"Movement Example - Irrlicht Engine [");
            tmp += driver->getName();
            tmp += L"] fps: ";
            tmp += fps;
 
            device->setWindowCaption(tmp.c_str());
            lastFPS = fps;
        }
        //cam->setTarget(core::vector3df(0,0,30));
    }
 
    /*
    In the end, delete the Irrlicht device.
    */
    device->drop();
 
    return 0;
}
 
/*
That's it. Compile and play around with the program.
**/
 
NeomerArcana
Posts: 3
Joined: Sun May 25, 2014 5:20 am

Re: How to draw an ellipse?

Post by NeomerArcana »

I sort of solved it by adding a new pure virtual method to the ISceneAnimator class. It should return the position of the node being affected by the animator at the provided time. Here is the implementation for CSceneNodeAnimatorFlyCircle:

Code: Select all

core::vector3df CSceneNodeAnimatorFlyCircle::GetPositionAtTime(f32 t)
{
    f32 r2 = RadiusEllipsoid == 0.f ? Radius : RadiusEllipsoid;
    return (Center + (Radius*cosf(t)*VecU) + (r2*sinf(t)*VecV ) );
}
I just had all the other node animators return a 0,0,0 vector for now.

So, now I can use the animator to get the position of the node at any time and store them in a custom scene node to draw with a 3D line later.

Code: Select all

scene::ISceneNodeAnimator* a = mgr->createFlyCircleAnimator(center,radius,0,direction,0,ellipseRadius);
        for(int i = 0; i < 360; ++i)
        {
            float time = 23.0f / 360 * i;
            m_vertices.push_back(a->GetPositionAtTime(time));
        }
What I don't understand is why a value of 23 here works. Any higher and I start getting my spiralgraph spiderweb thing again (ever so slightly for low numbers). Does anyone know what's happening, and why this works?
booster
Posts: 15
Joined: Thu Nov 19, 2015 8:53 pm

Re: How to draw an ellipse?

Post by booster »

I started with yours but here's where my play tookme...

Code: Select all

 
 
#include <irrlicht.h>
#include "driverChoice.h"
 
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
 
using namespace irr;
/*
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
*/
#ifdef _IRR_WINDOWS_
#pragma comment(lib, "Irrlicht.lib")
#endif
 
class RXEllipseSceneNode : public scene::ISceneNode{
    core::aabbox3d<irr::f32> Box;
    std::vector<core::vector3df> Vertices;
    video::SMaterial Material;
    
    irr::f32 fmajor;
    irr::f32 fminor;
    irr::s32 isteps;
    video::SColor c;
    
public:
 
    RXEllipseSceneNode(scene::ISceneNode* parent, scene::ISceneManager* mgr, irr::s32 id, irr::f32 major=20.0f, irr::f32 minor=10.0f, irr::s32 steps=32, video::SColor color=video::SColor(255,255,255,255))
        : scene::ISceneNode(parent, mgr, id)
    {
        Material.Wireframe = false;
        Material.Lighting = false;
        
        c=color;
        
        fmajor = major; //20.0f;
        fminor = minor; //10.0f;
        
        isteps = steps; //16;
        irr::f32 step = (irr::core::PI*2)/isteps;
    
        std::vector<core::vector3df> m_vertices;
        for(irr::f32 i = 0; i < isteps; ++i)
            Vertices.push_back(core::vector3df(fmajor*std::cos(i*step),0,fminor*std::sin(i*step)));
        
        Box.reset(Vertices[0]);
        for(s32 i=1; i<isteps; ++i)
            Box.addInternalPoint(Vertices[i]);
        
    }
    
    virtual void OnRegisterSceneNode(){
        if(IsVisible)
            SceneManager->registerNodeForRendering(this);
 
        ISceneNode::OnRegisterSceneNode();
    }
    
    virtual void render(){
        video::IVideoDriver* driver = SceneManager->getVideoDriver();
        
        driver->setMaterial(Material);
        driver->setTransform(video::ETS_WORLD, irr::core::IdentityMatrix);
        
        for(irr::f32 i = 0; i < isteps; ++i){
            if(i<isteps-1)
                driver->draw3DLine(Vertices[i], Vertices[i+1], c);
            else
                driver->draw3DLine(Vertices[i], Vertices[0], c);
        }
    }
    
    virtual const core::aabbox3d<irr::f32>& getBoundingBox() const{
        return Box;
    }
 
    virtual irr::u32 getMaterialCount() const{
        return 1;
    }
 
    virtual video::SMaterial& getMaterial(u32 i){
        return Material;
    }   
};
 
class MyEventReceiver : public IEventReceiver{
public:
    virtual bool OnEvent(const SEvent& event){
        if (event.EventType == irr::EET_KEY_INPUT_EVENT)
            KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
        return false;
    }
    virtual bool IsKeyDown(EKEY_CODE keyCode) const{
        return KeyIsDown[keyCode];
    }
    MyEventReceiver(){
        for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
            KeyIsDown[i] = false;
    }
private:
    bool KeyIsDown[KEY_KEY_CODES_COUNT];
};
 
int main(){
    
    MyEventReceiver receiver;
 
    irr::IrrlichtDevice* device = createDevice(video::EDT_OPENGL, core::dimension2d<u32>(640, 480), 16, false, false, false, &receiver);
 
    if(device == 0)
        return 1; // exit, could not create selected driver.
    
    video::IVideoDriver* driver = device->getVideoDriver();
    scene::ISceneManager* smgr = device->getSceneManager();
    
    smgr->setAmbientLight(video::SColorf(0.6,0.6,0.6,1));       //  r,g,b,a
    scene::ILightSceneNode* light1 = smgr->addLightSceneNode(0, core::vector3df(0,0,0), video::SColorf(0.3f,0.6f,0.6f), 24.0f, 1);  //  parent,pos,color,radius,id
    
    scene::ICameraSceneNode* cam = smgr->addCameraSceneNode();
    cam->setTarget(core::vector3df(0,0,0));
    cam->setPosition(core::vector3df(18,18,18));
    
    irr::f32 major = 20.0f;                             //  Major Axis
    irr::f32 minor = 10.0f;                             //  Minor Axis
    irr::s32 steps = 32;                                    //  Divisions, change to 32
    video::SColor color = video::SColor(255,118,118,184);       //  Line Colour
    
    //  ADD AN ELLIPSE
    RXEllipseSceneNode *o = new RXEllipseSceneNode(smgr->getRootSceneNode(), smgr, 666, major, minor, steps, color);
    
    //  SPHERE ANIMATED ALONG AN ELLIPSE
    scene::ISceneNode * planet = smgr->addSphereSceneNode(3.0f,100);    //  r,polycount, ...
    //scene::IAnimatedMesh* earthMesh = smgr->getMesh("../../media/earth.x");
    if(planet){
        planet->setMaterialTexture(0, driver->getTexture("../../media/earth.jpg"));
        /*
        // load heightmap, create normal map from it and set it
        video::ITexture* earthNormalMap = driver->getTexture("../../media/earthbump.jpg");
        if (earthNormalMap)
        {
            driver->makeNormalMapTexture(earthNormalMap, 20.0f);
            planet->setMaterialTexture(1, earthNormalMap);
            planet->setMaterialType(video::EMT_NORMAL_MAP_TRANSPARENT_VERTEX_ALPHA);
        }
        */
        planet->setMaterialFlag(video::EMF_LIGHTING, true);
 
        //  center,radius,speed,direction,startPosition,radiusEllipsoid
        scene::ISceneNodeAnimator* anim = smgr->createFlyCircleAnimator(core::vector3df(0,0,0), major, 0.001f, core::vector3df(0.f, 1.f, 0.f),0.f,minor);
        if(anim){
            planet->addAnimator(anim);
            anim->drop();
        }
        
        // add spin animator
        anim = smgr->createRotationAnimator(core::vector3df(0,-0.5f,0));
        if(anim){
            planet->addAnimator(anim);
            anim->drop();
        }
    }
    
    
    //  GAME ON
    bool run = true;
    while(device->run() && device && run){
        //
        if(receiver.IsKeyDown(irr::KEY_ESCAPE))
            run = false;
        
        //
        driver->beginScene(true, true, video::SColor(0,66,60,60));
        smgr->drawAll();
        driver->endScene();
    }
    device->drop();
    cout << "rawstar7.co.uk" << endl;
}
 
I was only born yesterday :D so anyone wanna fill me in on the earth mesh please?
Cube_
Posts: 1010
Joined: Mon Oct 24, 2011 10:03 pm
Location: 0x45 61 72 74 68 2c 20 69 6e 20 74 68 65 20 73 6f 6c 20 73 79 73 74 65 6d

Re: How to draw an ellipse?

Post by Cube_ »

I sort of solved it by adding a new pure virtual method to the ISceneAnimator class. It should return the position of the node being affected by the animator at the provided time. Here is the implementation for CSceneNodeAnimatorFlyCircle:
that is indeed the correct method to do it (cosines and sines that are stretched on either x or y to create an elipsis - I recommend you reset the values every turn so as to prevent them from losing precision due to rounding and becoming spirals, otherwise you'll eventually end up with values too large and it'll overflow - try it, irrlicht's scene node animator might already prevent that)
"this is not the bottleneck you are looking for"
Post Reply