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.
Abraxas)
Posts: 227
Joined: Sun Oct 18, 2009 7:24 am

3D Circle Drawer

Post by Abraxas) »

I'm not a pro at c++, but I'm no newbie at vector algebra and programming in general.

The following code will generate a circle in 3D space, and render it during the call.

Circle.H

Code: Select all

#include <irrlicht.h>

using namespace irr;
using namespace core;

class circleclass
{
public:
	core::vector3df vertex[10000];
	core::vector3df normal;
	core::vector3df center;
	double radius;
	int vertcount;
	circleclass();
};

circleclass::circleclass()
{
	vertcount = -1;
}


circleclass createCircle(core::vector3df center, double radius, double vertexcount, core::vector3df normal)
{

circleclass temp; //our temporary circleclass holder
int x=0;
	
	// We want the vertex count to reflect a polygon
if (vertexcount < 3) vertexcount = 3;
if (vertexcount > 10000) vertexcount = 10000;

normal.normalize();

double pi = 3.14159265;

	//Here we find the intersection of a plane whos normal is our normal vector,
	//with a sphere with radius 'radius'.

	//The equation of this circle is P + sV1 + tV2
	//where v1 is (0, c, −b) and v2 is (c, 0, −a), we need at least one nonzero one.
vector3df v1 = vector3df(0,normal.Z,-normal.Y);
vector3df v2 = vector3df(normal.Z,0,-normal.X);

	//scale vector to unit length (dividing by (1 − x2)1/2) where x is the 0 component of the vector, in the normal.
if (v1 != vector3df(0,0,0))
{	v1 = v1/ sqrt(1-(normal.X*normal.X));	v2 = v1.crossProduct(normal);	}
else //in the case that v1 = (0,0,0)
{v2 = v2/ sqrt(1-(normal.Y*normal.Y));	v1 = v2.crossProduct(normal);	}

for(x;x<=vertexcount;x++)
{

	//Now setting:	t = Rcos(angle)
	//				s = Rsin(angle)

	//Where R = sqrt(radius2 - d2)
	//Since we have the midpoint, we consider d = 0, R = r
	
	temp.vertex[x] = center		+	radius*cos(2*pi/vertexcount*x)*v1	+	radius*sin(2*pi/vertexcount*x)*v2;
	
	temp.vertcount++;

}




temp.vertex[temp.vertcount + 1] = temp.vertex[0];
temp.radius = radius;
temp.center = center;
temp.normal = normal;

return temp;
}

void drawCircle(circleclass circle, video::IVideoDriver* driver)
{
int x=0;

for (x;x<=circle.vertcount;x++)
driver->draw3DLine(circle.vertex[x],circle.vertex[x+1],video::SColor(255,255,255,255));


}

void drawCircleNormal(circleclass circle, video::IVideoDriver* driver)
{

driver->draw3DLine(circle.center, circle.center + (circle.normal*10) );

}
Implementation:

Code: Select all

#include "Circle.h"
circleclass circle = createCircle(core::vector3df(0,0,0),20,40,core::vector3df(0,1,0));
drawCircle(circle, driver);
Optimize the circle for the amount of sides you need, and at least in my case I set the driver material to what I want the circle to look like.

The comments in the code are for educational purposes!
Last edited by Abraxas) on Mon Nov 23, 2009 12:45 pm, edited 1 time in total.
Acki
Posts: 3496
Joined: Tue Jun 29, 2004 12:04 am
Location: Nobody's Place (Venlo NL)
Contact:

Re: 3D Circle Drawer (Very efficient)

Post by Acki »

Abraxas) wrote:

Code: Select all

void drawCircle(circleclass circle, video::IVideoDriver* driver)
{
int x=0;

for (x;x<=circle.vertcount;x++)
driver->draw3DLine(circle.vertex[x],circle.vertex[x+1],video::SColor(255,255,255,255));


}

void drawCircleNormal(circleclass circle, video::IVideoDriver* driver)
{

driver->draw3DLine(circle.center, circle.center + (circle.normal*10) );

}
I think it would be much more efficient to use a VertexPrimitiveList... ;)
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) »

how could it possibly be faster? This is as optimised as a routine as possible for displaying polygons... is there some kind of overhead reduction with that function?

And obviously, I dont know how to use the funtion, so I made this for people like me who arent megafathers of programming.
netpipe
Posts: 670
Joined: Fri Jun 06, 2008 12:50 pm
Location: Edmonton, Alberta, Canada
Contact:

Post by netpipe »

you could speed it up by using a lookup table for the trig functions rather than calculating them maybe
Acki
Posts: 3496
Joined: Tue Jun 29, 2004 12:04 am
Location: Nobody's Place (Venlo NL)
Contact:

Post by Acki »

Abraxas) wrote:how could it possibly be faster? This is as optimised as a routine as possible for displaying polygons... is there some kind of overhead reduction with that function?
I think a benchmark test would be a good idea, I see what I can do... ;)

also you should not pass the class (a copy of it) to the draw functions:

Code: Select all

void drawCircleNormal(circleclass circle, video::IVideoDriver* driver) 
instead pass only the pointer to the class:

Code: Select all

void drawCircleNormal(circleclass* circle, video::IVideoDriver* driver) 
or even better, why didn't you include the draw functions directly to the class ??? :shock:
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) »

about passing the pointer and the trig tables... I guess those are minor updates to the speed if you are rendering very large-sided polygons.

but I did a forum search and this is the best snippet posted yet so...

enjoy :D
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

Abraxas) wrote:about passing the pointer and the trig tables... I guess those are minor updates to the speed if you are rendering very large-sided polygons.
Assuming that you don't need to create these very often, it shouldn't be a problem to use the trig functions. Passing these objects around by value is just silly (one pointer copy vs 1000 positions).
Abraxas) wrote:but I did a forum search and this is the best snippet posted yet so...
Just because you think your code is 'better' than everyone elses, doesn't make it so. Your failure to respond to and accept positive feedback from other users is a pretty good sign that you think you know what you're doing, but don't.

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.

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

Post by hybrid »

We also have a createSphere in the geometry creator class, which should have not much overhead to yours, even with the mentioned optimizations taken into consideration. You don't need to write anything on your own, and it's propely textured.
Another thing which is probably a problem with your class: The vertex array is created on the stack, this can give severe problems if more than one sphere is created in a stack frame.
zet.dp.ua
Posts: 66
Joined: Sat Jul 07, 2007 8:10 am

Post by zet.dp.ua »

yes, single call will be much more efficient. just create mesh and use it's vertices.
the best scenarios for drawing any shape for fixed function pipeline are:
1. if the shape vertices are frequently modified (animation, physics, etc.) then use batching w/o hardware buffers.
2. if the shape is static or unfrequently modified then create shape once and use hardware buffers.
Abraxas)
Posts: 227
Joined: Sun Oct 18, 2009 7:24 am

Post by Abraxas) »

Ok, so since everybody agrees my way of doing things is bad, why dont one of you post a working example of how to create and display a circle (not sphere...) with a chosen normal, so I can make improvement to my code.

Telling me to 'use function xxx' isnt helpfull for me to learn because I dont know HOW to use function xxx unless I can see it in an example.
shadowslair
Posts: 758
Joined: Mon Mar 31, 2008 3:32 pm
Location: Bulgaria

Post by shadowslair »

Abraxas) wrote:Ok, so since everybody agrees my way of doing things is bad, why dont one of you post a working example of how to create and display a circle (not sphere...) with a chosen normal, so I can make improvement to my code.
We aren`t claiming this is bad, just could be improved a little to turn it at 100% usable code- optimized and proved for big projects and so. On the other hand I believe that each one replying to this topic are fully capable of coding this in less than 2 hours if they need/want it. Telling s.o. where his code could be improved helps him learn some fine points of programming, which in fact is the aim of this forum.
Abraxas) wrote:Telling me to 'use function xxx' isnt helpfull for me to learn because I dont know HOW to use function xxx unless I can see it in an example.
Creating examples for all functions is quite senseless/impossible maybe, but you can always ask for explanation for sth particular. :wink:
"Although we walk on the ground and step in the mud... our dreams and endeavors reach the immense skies..."
Acki
Posts: 3496
Joined: Tue Jun 29, 2004 12:04 am
Location: Nobody's Place (Venlo NL)
Contact:

Post by Acki »

well, I rewrote your code (took me less than 15 mins) to be efficient and made a benchmark test... :lol:

this are 200 circles using your code (FPS = 11):
Image

and this are the same 200 circles using my code (FPS = 104 !!!):
Image

and here is the code:
circleclass.h

Code: Select all

#include <irrlicht.h>

namespace irr{
namespace scene{

class circleclass : public ISceneNode{
  private:
    core::array<video::S3DVertex> lstVertices;
    core::array<u16> lstIndices;
    core::aabbox3df Box;
    video::SMaterial Material;
    video::IVideoDriver* Driver;
    f32 Radius;
    core::vector3df Normal;
    core::vector3df Center;

  public:
    circleclass(core::vector3df center, f32 radius, u32 vertexcount, core::vector3df normal, ISceneNode* parent, ISceneManager* smgr, s32 id=-1);
    virtual void OnRegisterSceneNode();
    virtual void render();
    virtual const core::aabbox3d<f32>& getBoundingBox() const;
    virtual u32 getMaterialCount();
    virtual video::SMaterial& getMaterial(u32 i);
    virtual void setMaterial(video::SMaterial newMaterial);
    void createCircle(core::vector3df center, f32 radius, u32 vertexcount, core::vector3df normal);

};

}} // namespaces
circleclass.cpp

Code: Select all

#include "circleclass.h"
namespace irr{
namespace scene{

circleclass::circleclass(core::vector3df center, f32 radius, u32 vertexcount, core::vector3df normal, ISceneNode* parent, ISceneManager* smgr, s32 id):ISceneNode(parent,smgr,id){
  Driver = SceneManager->getVideoDriver();
  if(Parent) Parent->addChild(this);
  updateAbsolutePosition();
  createCircle(center, radius, vertexcount, normal);
  AutomaticCullingState = EAC_FRUSTUM_BOX;
}

void circleclass::OnRegisterSceneNode(){
  if(IsVisible) SceneManager->registerNodeForRendering(this);
  ISceneNode::OnRegisterSceneNode();
}
void circleclass::render(){
  // Prep to render
  Driver->setMaterial(Material);
  Driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
  // render
  Driver->drawVertexPrimitiveList(&lstVertices[0], lstVertices.size(), &lstIndices[0], lstIndices.size() / 2, video::EVT_STANDARD, EPT_LINES, video::EIT_16BIT);
}

const core::aabbox3d<f32>& circleclass::getBoundingBox() const{
  return Box;
}
u32 circleclass::getMaterialCount(){
  return 1;
}
video::SMaterial& circleclass::getMaterial(u32 i){
  return Material;
}
void circleclass::setMaterial(video::SMaterial newMaterial){
  Material = newMaterial;
}

void circleclass::createCircle(core::vector3df center, f32 radius, u32 vertexcount, core::vector3df normal){
  // We want the vertex count to reflect a polygon
  if (vertexcount < 3) vertexcount = 3;
  if (vertexcount > 10000) vertexcount = 10000;

  normal.normalize();

  //Here we find the intersection of a plane whos normal is our normal vector,
  //with a sphere with radius 'radius'.

  //The equation of this circle is P + sV1 + tV2
  //where v1 id (0, c, -b) and v2 is (c, 0, -a), we need at least one nonzero one.
  core::vector3df v1 = core::vector3df(0, normal.Z, -normal.Y);
  core::vector3df v2 = core::vector3df(normal.Z, 0, -normal.X);

  if(v1 != core::vector3df(0,0,0)){
    v1 = v1 / sqrt(1 - (normal.X * normal.X));
    v2 = v1.crossProduct(normal);
  }else{
    v2 = v2 / sqrt(1 - (normal.Y * normal.Y));
    v1 = v2.crossProduct(normal);
  }

  lstIndices.clear();
  lstVertices.clear();
  Box.reset(center);

  video::S3DVertex vert;
  for(u32 x = 0; x < vertexcount; ++x){
    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);
  }

  Radius = radius;
  Center = center;
  Normal = normal;
}

}} // namespaces
usage:

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();
nothing more, just create it and it works (no manualy drawings needed) !!! ;)
plus you can now use all SceneNode functions (setPosition, setRotation, setScale, setParent, ...) !!!

Example for setting the color:

Code: Select all

SMaterial NM;
NM.EmissiveColor = SColor(255, 255,0,0);
circleNode->setMaterialFlag(EMF_LIGHTING, false);
circleNode->setMaterial(NM);

I let this Radius, Center and Normal vars for advancements in the code... :lol:
although I don't say my code is perfect, but much more efficient than yours... ;)
Last edited by Acki on Fri Mar 19, 2010 2:01 pm, edited 5 times in total.
while(!asleep) sheep++;
IrrExtensions:Image
http://abusoft.g0dsoft.com
try Stendhal a MORPG written in Java
randomMesh
Posts: 1186
Joined: Fri Dec 29, 2006 12:04 am

Post by randomMesh »

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.
"Whoops..."
Acki
Posts: 3496
Joined: Tue Jun 29, 2004 12:04 am
Location: Nobody's Place (Venlo NL)
Contact:

Post by Acki »

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.
of course I did that in my test app... ;)
this is only the creation line I posted... :lol:
while(!asleep) sheep++;
IrrExtensions:Image
http://abusoft.g0dsoft.com
try Stendhal a MORPG written in Java
randomMesh
Posts: 1186
Joined: Fri Dec 29, 2006 12:04 am

Post by randomMesh »

Acki wrote:of course I did that in my test app
I know you did. But others might not know. :P
"Whoops..."
Post Reply