3D Circle Drawer

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Bate
Posts: 364
Joined: Sun Nov 01, 2009 11:39 pm
Location: Germany

Post by Bate »

yep, for example me :)

so how do I delete it afterwards?
Acki
Posts: 3496
Joined: Tue Jun 29, 2004 12:04 am
Location: Nobody's Place (Venlo NL)
Contact:

Post by Acki »

well, because it is a scene node you do it this way:

Code: Select all

// creation
circleclass* circleNode = new circleclass(core::vector3df(50, 0, 100), 20, 40, core::vector3df(0,1,0), smgr->getRootSceneNode(), smgr);
circleNode->drop();

// deletion
circleNode->remove();
I also updated the post with the code and added an example for setting the color, too... ;)
while(!asleep) sheep++;
IrrExtensions:Image
http://abusoft.g0dsoft.com
try Stendhal a MORPG written in Java
Abraxas)
Posts: 227
Joined: Sun Oct 18, 2009 7:24 am

Post by Abraxas) »

First of all:
Thank You Acki

Secondly, thanks :) I appreciate the fact that you don't treat me as an inferior programmer, even though I posted in the project announcement I've only been using c++ for a month.... (was doing VB before).

I'm definitely going to learn as much as possible from this code.

But to be honest, the purpose of my code was to show how to generate an N-sided polygon using linear algebra. It wasnt to show off or anything, which I think a few people assumed.

I spent some time looking at different ways to do it (deconstructing the plane vectors for example) but the quickest, and also simplest way to do it was to use the original implicit definition of the plane and unit circle to find the vertices.

Acki, would you mind answering two questions?

Code: Select all

vert.Pos = center + radius * cos(2 * core::PI / vertexcount * x) * v1 + radius * sin(2 * core::PI / vertexcount * x) * v2; 
    vert.Color = video::SColor(255, 255,0,0); 
    lstIndices.push_back(lstVertices.size()); 
    lstVertices.push_back(vert); 
    Box.addInternalPoint(vert.Pos); 
    
vert.Pos = center + radius * cos(2 * core::PI / vertexcount * (x + 1)) * v1 + radius * sin(2 * core::PI / vertexcount * (x + 1)) * v2; 
    vert.Color = video::SColor(255, 255,0,0); 
    lstIndices.push_back(lstVertices.size()); 
    lstVertices.push_back(vert); 
    Box.addInternalPoint(vert.Pos);
 
How come your doing this? doesnt this mean you have 2 copies of every vertex besides the first and the last+1?

And I don't know much about dynamic creation, but when you make the pointer, why do you need the new operator? does that prevent a stack overflow?

Along with that question, if I drop() the node, does it still exist somewhere else in memory? would that be in the heap?

Also, using brace namespace method, is that just old C style or is there an after-compilation difference?
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

randomMesh wrote:

Code: Select all

circleclass* c = new circleclass(core::vector3df(-50 + (2 * t), 0, 100), 20, 40, core::vector3df(0,1,0), smgr->getRootSceneNode(), smgr);
If you create object on the heap, you should delete it after use, or memory leaks will occur.
Actually, this is one case where the 'irrlicht rules' are wrong. The memory is allocated with new, but the scene node is inserted into the scene graph. Once the node is in the scene graph and the reference count is stabilized (the call to drop right after the node is associated with the scene), the node should be removed with a call to node->remove(), which will properly delete the node.

To avoid confusion, I typically do something like this.

Code: Select all

class MySceneNode : public ISceneNode
{
public:
  static MySceneNode* addMySceneNode(ISceneNode* parent, ISceneManager* mgr, s32 id = -1);

private:
  MySceneNode(ISceneNode* parent, ISceneManager* mgr, s32 id);
};

/* static */ MySceneNode*
MySceneNode::addMySceneNode(ISceneNode* parent, ISceneManager* mgr, s32 id)
{
  if (!parent)
    parent = smgr->getRootSceneNode();

  MySceneNode* node = new MySceneNode(parent, smgr, id);
  node->drop();

  return node;
}

MySceneNode::MySceneNode(ISceneNode* parent, ISceneManager* mgr, s32 id)
  : ISceneNode(parent, smgr, id)
{
}
That way users add the scene node to the scene graph just like they would with one of the ISceneManager methods, and because the method is named add... they know not do delete the object explicitly.
Abraxas) wrote:How come your doing this? doesnt this mean you have 2 copies of every vertex besides the first and the last+1?
Yes. The code could be simplified to store each vertex only once. This would require that you use the EPT_LINES primitive type and refer to each vertex twice using the index buffer. A better solution would be to use EPT_LINE_STRIP, which would cut the size of the index buffer in half.
Abraxas) wrote:I don't know much about dynamic creation, but when you make the pointer, why do you need the new operator? does that prevent a stack overflow
The new operator is used to dynamically allocate an object. On many systems, the amount of memory that is available for the stack is restricted to some small size. Allocating large objects on the stack can cause the program to use up all of this available space, and the result is usually memory corruption due to stack overflow.
Abraxas) wrote:if I drop() the node, does it still exist somewhere else in memory? would that be in the heap?
It depends. You should really read about the IReferenceCounted class in the documentation. The gist of it is, every object derived from that class has a reference count. When an object is created, it gets an initial reference count of zero. When something calls grab() on the object, the reference count is incremented, and when something calls drop(), the reference count is decremented. When the reference count goes to zero, the object deletes itself.
Abraxas) wrote:using brace namespace method, is that just old C style or is there an after-compilation difference
Namespaces put classes and functions into seperate groups to avoid problems when classes or functions with the same names need to be used. This construct does not exist in the C language, and the namespace of functions and methods are actually encoded into their names so that they can be found at link time.

Travis
Acki
Posts: 3496
Joined: Tue Jun 29, 2004 12:04 am
Location: Nobody's Place (Venlo NL)
Contact:

Post by Acki »

even vitek explained this already I will post my explanations too, they are mainly the same (what a wonder) but in other words... :lol:
Abraxas) wrote:First of all:
Thank You Acki

Secondly, thanks :) I appreciate the fact that you don't treat me as an inferior programmer, even though I posted in the project announcement I've only been using c++ for a month.... (was doing VB before).
you're welcome !!! :)
well, why should I offend you for this ???
you made a code and it worked, although your coding style is not the best... ;)
the reason why I posted was that you first claimed the code to be efficient (IIRC) and I wanted to show you that it isn't... :lol:
Abraxas) wrote:I'm definitely going to learn as much as possible from this code.
that's the way it should be !!! :)
Abraxas) wrote:How come your doing this? doesnt this mean you have 2 copies of every vertex besides the first and the last+1?
well, it's simply because each line segment needs 2 points (start and end) from 0-1, 1-2, 2-3 and so on...
and you see each number is 2 times there (also start and end)...
this was the easiest way to do this at that moment... ;)
Abraxas) wrote:And I don't know much about dynamic creation, but when you make the pointer, why do you need the new operator? does that prevent a stack overflow?
because the new operator creates the object on the heap and returns a pointer...
this has nothing to do with stack overflows and such, it's simply the way how it works...
you could get a stack overflow if you create it on the stack though... ;)
Abraxas) wrote:Along with that question, if I drop() the node, does it still exist somewhere else in memory? would that be in the heap?
yes, the drop is how Irrlicht handles objects to prevent them from deleting when still in use (called ReferenceCount)...
when you create an object this way (new operator) Irrlicht increases the reference counter so you need to drop it again... ;)
Abraxas) wrote:Also, using brace namespace method, is that just old C style or is there an after-compilation difference?
I did this because in Irrlicht the scene nodes are in this namespace(s)...
and no, it's not old C style, but an advanced C++ concept !!!
maybe this page can help you understand C++ better: http://www.cplusplus.com/doc/tutorial/
while(!asleep) sheep++;
IrrExtensions:Image
http://abusoft.g0dsoft.com
try Stendhal a MORPG written in Java
Ulf
Posts: 281
Joined: Mon Jun 15, 2009 8:53 am
Location: Australia

Post by Ulf »

This is going back a bit in this thread but I just want to make a point about vertexPrimitiveList.
Acki wrote:I think it would be much more efficient to use a VertexPrimitiveList...
It's not any more efficient than calling drawLine. Why would it be?
vitek wrote:Unless you are going to use primitive lists, the very efficient comment in the title should be removed entirely. A single draw...() call is much more efficient than 10000 of them.
It does the same number of calls to draw either way!

It uses drawline anyway. How else?

Code: Select all

for (u32 i=0; i < primitiveCount-1; ++i)
	draw3DLine(((S3DVertex*)vertices)[indexList[i]].Pos,
	((S3DVertex*)vertices)[indexList[i+1]].Pos,
	((S3DVertex*)vertices)[indexList[i]].Color);
I can hear birds chirping
:twisted:

I live in the Eye of Insanity.
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

Ulf wrote:This is going back a bit in this thread but I just want to make a point about vertexPrimitiveList.
Acki wrote:I think it would be much more efficient to use a VertexPrimitiveList...
It's not any more efficient than calling drawLine. Why would it be?
It is. A call to draw3DLine() does essentially the same thing as a single call to drawVertexPrimitiveList() with a single line primitive. To draw a line with 10000 segments, you have to make 10000 function calls, each of which call through to the driver, which is very expensive. A single call to drawVertexPrimitiveList() can be used to draw 10000 line segments and avoid all of this overhead.
Ulf wrote:
vitek wrote:Unless you are going to use primitive lists, the very efficient comment in the title should be removed entirely. A single draw...() call is much more efficient than 10000 of them.
It does the same number of calls to draw either way!
No, it doesn't.
Ulf wrote:It uses drawline anyway. How else?
As mentioned above, draw3DLine() draws just one line primitive per function call. The drawVertexPrimitiveList() can draw many thousands of primitives in a single call. This could be done even more efficiently by using hardware buffers, but Irrlicht only supports drawing triangles using them.

Travis
Ulf
Posts: 281
Joined: Mon Jun 15, 2009 8:53 am
Location: Australia

Post by Ulf »

@vitek

Ok. Using openGL as an example, are you saying that draw3DLine must call glBegin and glEnd every time, but drawVertexPrimitiveList does not? Does drawVertexPrimitiveList only call them once and draw all lines in a loop? I noticed that after looking at it carefully.

**EDIT**
But with all the extra work drawVertexPrimitiveList does, is it necessarily faster? Do the begin and end take up so much time?

**EDIT**
Also, each video driver does it differently.

The NULL driver does nothing.

The software driver does it as follows (as shown before)

Code: Select all

for (u32 i=0; i < primitiveCount-1; ++i)
	draw3DLine(((S3DVertex*)vertices)[indexList[i]].Pos,
	((S3DVertex*)vertices)[indexList[i+1]].Pos,
	((S3DVertex*)vertices)[indexList[i]].Color);
So in the software drivers case, it's exactly the same. It draws 3D lines over and over in a loop. That is the same as if you do that yourself.

Software Driver2 after setting up hundreds of parameters, ends up calling the following:

Code: Select all

// re-tesselate ( triangle-fan, 0-1-2,0-2-3.. )
for ( g = 0; g <= vOut - 6; g += 2 )
{
	// rasterize
	CurrentShader->drawTriangle ( CurrentOut.data + 0 + 1,
	CurrentOut.data + g + 3,
	CurrentOut.data + g + 5);
}
Is drawTriangle faster than calling drawLine 3 times? Doubt it. Must be virtually the same.

The openGL and directX drivers do it bit differently. They have many more parameters and I don't understand it fully yet.
But don't they basically do the same thing? They set up an array, then they loop through it calling drawline between every 2 vertices.
I don't mean they call the Irrlicht drawline, they call their own drawline function.

Where are the benefits in speed obtained?
Last edited by Ulf on Wed Nov 25, 2009 8:26 am, edited 1 time in total.
I can hear birds chirping
:twisted:

I live in the Eye of Insanity.
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

Yes, glBegin, glEnd, and almost all other gl* methods are quite costly. As vitek said, these are driver calls, which often also includes major state changes in the OpenGL state machine. So unless you only draw one or two lines each frame, a vertex primitive list will be significantly faster.
Ulf
Posts: 281
Joined: Mon Jun 15, 2009 8:53 am
Location: Australia

Post by Ulf »

hybrid wrote:Yes, glBegin, glEnd, and almost all other gl* methods are quite costly. As vitek said, these are driver calls, which often also includes major state changes in the OpenGL state machine. So unless you only draw one or two lines each frame, a vertex primitive list will be significantly faster.
Ok I understand.

But, as I showed in my edits to my post, am I correct in saying that some implementations of drawVertexPrimitiveList do nothing more than call drawLine in a loop anyway?

So really only the directX and openGL versions are faster?

Or is the software driver 2 faster as well? It just seems to draw triangles instead of lines.. couldn't be much different.

What do you say hybrid?


I think I asked this before... but what is the SDL version?
Is that for Windows to connect with SDL library?
And where is the Mac version? Is that in a separate distribution?
I can hear birds chirping
:twisted:

I live in the Eye of Insanity.
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

Ulf wrote:But, as I showed in my edits to my post, am I correct in saying that some implementations of drawVertexPrimitiveList do nothing more than call drawLine in a loop anyway?
In the worst case, the drawVertexPrimitiveList() calls draw3DLine(), making it just as slow, possibly slightly slower due to function call overhead. In the best case, thousands of primitives can be rendered with a single call. In my testing with the OpenGL driver, I get 1000fps using drawVertexPrimitiveList() and 125 with draw3DLine() when rendering 10000 lines.

Unfortunately You can't do an apples-to-apples comparison of rendering lines with the burning renderer because it crashes when trying to render lines. It is known to only support rendering triangles. I did a quick test rendering 10000 triangles [drawVertexPrimitiveList(..., EPT_TRIANGLES) vs drawTriangle()] and the results are as follows.

Code: Select all

          drawVertexPrimitiveList()  draw3DTriangle()
opengl    837 fps                     116 fps
burning   142 fps                     75 fps
software  213 fps                     13 fps
Ulf wrote:So really only the directX and openGL versions are faster? Or is the software driver 2 faster as well? It just seems to draw triangles instead of lines.. couldn't be much different.
Yes, the accelerated drivers (the ones that are complete and are used most frequently) have the most to gain from using drawVertexPrimitiveList(). As mentioned, the burning driver doesn't work with non-triangle primitive types, and I consider that to be a bug. I also noticed problems when rendering triangles with the software driver and draw3DTriangle().

Travis
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

Yes, the bug in drawVertexPrimitiveList is already being tracked by a ticket.
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

Cool. The problem with drawing lines with the software driver turns out to be due to the fact that the lines are implemented as a triangles. If the line width is large, and the triangle is small, it looks funny/wrong.

Travis
Ulf
Posts: 281
Joined: Mon Jun 15, 2009 8:53 am
Location: Australia

Post by Ulf »

@vitek @hybrid

Thanks for the insight. I do look into the Irrlicht code to work it out but it's nice to get pointers as it is very time consuming to trace everything.

I'll take that on board. I know I'm already doing the wrong thing by using the 2D drawing, as everyone suggested to use hardware acceleration and use orthogonal projection.

But I'll learn won't I?

Anyway, I learned that, in my 2D game engine I should separate the draw functionality from the objects if I want to be able to render many objects at once.
The only problem with that is, each object may be from a different source texture. So I won't really be able to draw a batch of images with draw2DImageBatch because it only takes a single texture, but many frames.
So if the objects are from the same texture it can be done, but not otherwise.

My objects that don't have textures, instead having color and being filled can be drawn in a batch using drawVertexPrimitiveList.

Eventually I will probably change it to orthogonal 3D, but I'd like to experiment with 2D first.
I can hear birds chirping
:twisted:

I live in the Eye of Insanity.
Bate
Posts: 364
Joined: Sun Nov 01, 2009 11:39 pm
Location: Germany

Post by Bate »

I finally had some time to code my drawGeodesicLine function. It draws the geodesic line (shortest path) between 2 points on a sphere (with given radius and origin (0,0,0)). The math is correct and everything works fine but I would like to put it in a SceneNode just like Acki did with the circleDrawer. Unfortunately, I didn't succeed in doing so; guess that's because I don't understand everything in his code :oops:

So could maybe someone help me with this? (winking at Acki :wink:)

Here's how it works so far...

Hint:
It does not work if p1 and p2 are pole and exact anti-pole, e.g. p1(50,0,0) p2(-50,0,0), since there is no distinct solution (each path has the same length). In that case you have to distort one point a little bit: p1(50, 0.1f, 0.1f). Also, the closer you get to the pole issue the more lines you need to draw (higher value for k) in order to get a perfect circlular path. That is because the algorithm uses a linear dispersion for the distances between the points and I wouldn't bother to make it a spherical dispersion like -e^-(x^2) in order to get equally partitioned segments on the sphere.

Code: Select all

// FUNCTION

void geo(irr::core::array<S3DVertex> &arrayVertices,
         irr::core::array<u16> &arrayIndices,
         irr::core::vector3df p1, // starting point
         irr::core::vector3df p2, // endpoint
         irr::f32 r,              // radius
         irr::f32 k)              // precision (how many lines)
{

// put p1 as first vertex
arrayIndices.push_back(arrayVertices.size());
arrayVertices.push_back( S3DVertex(p1, vector3df(0,0,0), SColor(255, 255,0,0), vector2df(0,0) ));

// calc vertices
for(f32 i = 1; i < k; i++)
{

f32 t;

// nevermind whats going on here :)
// but I can explain the maths if someone's interested
t = squareroot(
                ( pow(r,2) * pow(k,2) ) /
                  (
                    ( pow((k-i),2) * pow(p1.X,2) + 2 * (k-i) * p1.X * i * p2.X + pow(i,2) * pow(p2.X,2) ) +
                    ( pow((k-i),2) * pow(p1.Y,2) + 2 * (k-i) * p1.Y * i * p2.Y + pow(i,2) * pow(p2.Y,2) ) +
                    ( pow((k-i),2) * pow(p1.Z,2) + 2 * (k-i) * p1.Z * i * p2.Z + pow(i,2) * pow(p2.Z,2) )
                  )
              );

// the next vertex
S3DVertex newVertex;

// set postion of next vertex
newVertex.Pos = t * (
                      ( ((k-i) / k) * p1 ) +
                      ( (i/k) * p2 )
                    );

// set color
newVertex.Color = SColor(255, 255,0,0);

// put next vertex in array
arrayIndices.push_back(arrayVertices.size());
arrayVertices.push_back(newVertex);
}

// put p2 as last vertex
arrayIndices.push_back(arrayVertices.size());
arrayVertices.push_back( S3DVertex(p2, vector3df(0,0,0), SColor(255, 255,0,0), vector2df(0,0) ));
}

Code: Select all

USAGE

// my arrays
irr::core::array<S3DVertex> arrayVertices;
irr::core::array<u16> arrayIndices;

// test points
vector3df p1 = vector3df(52,0,0);
vector3df p2 = vector3df(0,52,0);

// function call
  // radius = 52
  // precision = 25
geodesicLine(arrayVertices, arrayIndices, p1, p2, 52, 25);

Code: Select all

// RENDER LOOP

SMaterial mt;
mt.AntiAliasing = EAAM_QUALITY;
mt.AmbientColor = mt.DiffuseColor = mt.EmissiveColor = SColor(255, 255, 0, 0);

driver->setMaterial(mt);
driver->setTransform(video::ETS_WORLD, core::matrix4());

// primcount is "arrayIndices.size() - 1" since I'm using LINE_STRIP
driver->drawVertexPrimitiveList(&arrayVertices[0], arrayVertices.size(), &arrayIndices[0], (arrayIndices.size() - 1), EVT_STANDARD, EPT_LINE_STRIP, EIT_16BIT);
Post Reply