PDA

View Full Version : Basic design



jessejames
02-10-2009, 10:10 AM
Hello all,
I am learning OpenGL and having lots of fun. This is much eaiser that i had initially thought and i wish i would not have put it off for so long *chuckle*

I am currently building a very simple surface modeler CAD program and i am trying to wrap my head around how to do this in OpenGL? This program will only consist of two types of entities (lines, faces) Everything will be built from those items by the user.

Right now my main concern is creating entities within the scene. One of the main stumbling blocks right now is how will i keep track of the entities i create? Is there an internal way OpenGL holds a reference to all the objects or will i need to keep track of this myself by use of a scene graph or such? No problem if i need to do this myself, but i would hate to re-invent the wheel :)

for instance:


glBegin(GL_LINES)
vert1
vert2
vert3
vert4
glEnd()


OK, we have 2 lines here. I will need to access these lines separate, but i also need to have the abitlity to group these lines as one object and translate, rotate, scale them as one. Understanding how i will do this in OpenGL is holding back at the moment.

I need a reference to each entity so i can say


move(entity1, offsetVector)
rotate(group1, angle, vector)
etc...


Any advice or suggestions are very welcome.

Sergio Moura
02-10-2009, 11:33 AM
Hi!

Assuming you are using C++, no, OpenGL can't do that. You can. Here's what I did:



class GLObject {
public:
virtual void draw() = 0;
};

class CubeObject : public GLObject {
public:
virtual void draw() {
// draw a cube
}
};

class ObjectCache {
public:
std::map<std::string, GLObject*> objects;
void draw() {
std::map<std::string, GLObject*>::iterator itr = objects.begin();
while (itr != objects.end()) {
itr->second->draw();
}
}

GLObject&amp; operator[](const std::string&amp; name) {
std::map<std::string, GLObject*>::iterator itr = objects.find(name);
return(*(itr->second));
}
};


Of course, there are many many ways to optimize it, but I think you can get the idea.

Also, I use frustum to find out what is visible so I don't draw what I don't need (i have a scene with more than 500 complex objects)

That's how I do it, anyway.

- Sergio

Dj3hut1
02-10-2009, 12:56 PM
hello,

you can store your entity in a display list ( http://fly.cc.fer.hr/~unreal/theredbook/chapter04.html ).
With the id of your display list object you can do your operations ( moving and rotating )

dj3hut1

jessejames
02-11-2009, 10:30 PM
hello sergio,

Thanks for the great advice. Your code may be exactly what i need but i may need you help to completly understand it. Here is what i do understand -- at least i hope :)

1.) looks like we have a base class "GLObject" with one method "draw"
2.) a "CubeObject" class that inherits from "GLObject" with one method "draw"
3.) and an "ObjectCache" class which i understand what it is for ,but not how data is added or queried.

FYI: I am using Python along with the PyOpenGL bindings to create this application. I know speed will be an issue but at this time my main concern is learning OpenGL with a language i am comfortable with. A C re-write will eventually be inevitable so i don't mind. So far i have had good luck translating C code to Python.

Python has a few good built-in data structueres like list, dict maybe you can offer your suggestions in this direction

Thanks

jessejames
02-11-2009, 11:18 PM
Hello Dj3hut1,

Thanks for the advice. I have looked at display list but something is just not snapping in my brain about them. From what i have read all a display list holds is a list of commands. Since i will be creating geometry at anypoint in space and with different dimentions each time i am wondering how i will make this work? But maybe you can enlighten me.

I've read about things like retained-mode and immedient-mode and i think retained-mode is what i need although i do not fully inderstand the concepts. At the highest level of my application i would like to do the following....


line_segment(*args):
create line segment here from array of points

arc(*args):
etc...

rectangle(*args):
etc...

My goal is to build a very simple surface modeler with two main entities (lines and faces) At this point getting something working in a commandline manner will be great. Later i plan to build an interactive UI where all modifcations are made within a single 3D veiwport with the help of axis snapping, vertex snapping, picking, etc... I do not plan to have texturing, multiple scenes, layers or elaborate lighting(this can come in the C version). Lighting will be kept very simple(ambient) and only a single color on all entities within the model. Grouping and instancing will be supported. Tool types will include lines, 2d primitives types, measurements, and extrusion along path.

One thing i notice about code i have read is the fact that for instance in GLUT the geometry is constantly being redrawn on the screen even when no changes are being made. This seems like a complete waste of compute cycles to me but i may be missing something here? Since i am building a modeling app and not a game i don't think i really need a constant draw/redraw happening. Updatating the display should only occur when the user makes changes to the active model(transformations, drawing, etc...) Any explaination of these ideas would help greatly.

Mainly i need to see the big picture. I know how this code will look(except for the GL parts). I have already created a working 2D CAD application with interactive axis snapping, vert snapping and transformations. But the big difference here is the GL code, not so much the jump to 3D.

I guess my main problem is i do not completly understand the GL pipeline(and there are different implementations for diff apps). This is where i could use some good advice. Drawing entites and rendering the view is easy but until i can fully understand the usage of GL in a Modeling/CAD centric way i feel i am stumbling around in the dark.

OK let's take an example. Say I have a cube. I want to extrude the right quad of this cube by one pixel in the direction of the normal vector(posX). Must i rebuild the entire cube, or can i simply add 1 to each of the four vertices of the right quad and then update the display? Knowing this will get me off and running :)

Thanks for the help!

Sergio Moura
02-12-2009, 04:08 AM
hello sergio,

Thanks for the great advice. Your code may be exactly what i need but i may need you help to completly understand it. Here is what i do understand -- at least i hope :)

1.) looks like we have a base class "GLObject" with one method "draw"
2.) a "CubeObject" class that inherits from "GLObject" with one method "draw"
3.) and an "ObjectCache" class which i understand what it is for ,but not how data is added or queried.

FYI: I am using Python along with the PyOpenGL bindings to create this application. I know speed will be an issue but at this time my main concern is learning OpenGL with a language i am comfortable with. A C re-write will eventually be inevitable so i don't mind. So far i have had good luck translating C code to Python.

Python has a few good built-in data structueres like list, dict maybe you can offer your suggestions in this direction

Thanks

Well... if you're using Python, then you can make an array (or a dict) of GLObjects.
Assuming you have



def ObjectCache:
objects = []
__init__(self):
pass;
draw(self):
// Iterate each object from objects and call draw

cache = new ObjectCache;
obj1 = new CubeObject;
cache.objects.push_back(obj1);
quit = false;
while (!quit):
// Clear the scene
cache.draw(); // Draw the scene
// Handle keyboard input -- if any
// Copy the back buffer into the screen buffer


That's one way of how I would do it, but I don't remember the array functions or how to init/handle openGL in Python.

If you have a dict, you can query the data more easily, but you'll have to keep track of the object names



def ObjectCache:
objects = {}
__init__(self):
pass;
draw(self):
// Iterate each object from objects and call draw

obj = new ObjectCache;
cube = new CubeObject;
obj.objects['Cube01'] = cube;
// other initialization stuff
while (!quit):
// Clear the scene
cache.draw(); // Draw the scene
// Handle keyboard input -- if any
// Copy the back buffer into the screen buffer

// Play around with the cube
obj.objects['Cube01'].rotateY(angle);


I have an working reference you can use in my project here (http://www.open-ragnarok.org/svn/trunk/roengine/gl_object.h). It's in C++, but you can see the idea.

To speed things up, you can combine these with the display lists that Dj3hut1 mentioned.

- Sergio

jessejames
02-12-2009, 11:02 AM
Hello again Sergio,

Some real good advice in that last post. I think i am beginning to see the top of the mountain!

here is some Python code i drew up from what you sent me. I basically used my 2D object model design. I think i understand everything except how i will "inject" commands into this constant draw/redraw loop. here is my code

The Entites class creates "said" entity by calling a predefined class and assigning an ID to the object before stuffing the instance in the dict. I sub-classed the dict object to simplify the code.



class Entities(dict):
ID = 0 #class attribute
def __init__(self):
dict.__init__(self)
def add_edge(self, ID, coords):
self[ID]= Edge(ID, coords)
ID += 1
def add_face(self, ID, coords):
self[ID]= Face(coords)
ID += 1
def add_point(self, ID, coords):
self[ID]= EndPoint(coords)
ID += 1
def draw(self):
for obj in self:
obj.draw()

class Entity(object):
def __init__(self):
pass
def __del__(self):
pass
def rotate(self, angle, vector):
pass
def rotY(self, angle):
pass
def translate(self, dx, dy, dz):
pass
def scale(self, sx, sy, sz):
pass
def color(self, rgb):
pass
etc...

class Face(Entity):
def __init__(self, ID, coords):
Entity.__init__(self)
self ID = ID
self.coords = coords
def draw(self):
# GL code to draw face from coord list

class Point(Entity):
def __init__(self, coords):
Entity.__init__(self)
self.coords = coords
def draw(self):pass
# GL code to draw point from coord list

class Edge(Entity):
def __init__(self, ID, coords):
self.ID = ID
self.coords = coords
def draw(self):
# GL code to draw edge from coord list

#-- Main Program --#
ent = Entities()
ent.add_face(blah)
ent.add_edge(blah)
looping = 1 #quit is a python builtin
while looping:
# Clear the scene
ent.draw()#-- Draw the scene--#
# Handle keyboard input -- if any
# Copy the back buffer into the screen buffer

#-- Play around with the cube--#
ent['Cube01'].rotY(angle)


OK tell me if i am correct on this. OpenGL keeps no information on the objects in memory that i personally can access? The basic flow of a GL program is...
1.) clear buffer
2.) call all cache objects draw methods(redraw all objects)
2.) flush to buffer
3.) flip buffer

Question #1
So I must call a draw method on every object in my cache every iteration of the mainloop? correct?

Question #2
I understand the mainloop flow, but not how to insert new commands into this loop. Imagine a commandline interface. I guess i could use stdin and grab a command that way, but how do you handle it? I think this would work because calling input() would hold execution of the mainloop until the user returns a command.

Many Thanks

Sergio Moura
02-12-2009, 01:22 PM
Glad I can be of any help!



Question #1
So I must call a draw method on every object in my cache every iteration of the mainloop? correct?


Well... Yeah, you must draw everything in every loop. But, say you have a very complex object with many calculated vertices that you draw exactly the same in every frame... This way, you should use lists (see info on glCallList()).



Question #2
I understand the mainloop flow, but not how to insert new commands into this loop. Imagine a commandline interface. I guess i could use stdin and grab a command that way, but how do you handle it? I think this would work because calling input() would hold execution of the mainloop until the user returns a command.


Since you're thinking about a commandline interface, I see no way other than suspending the drawing to wait for the input or draw in one thread and read the input in the other, but I have no idea on how to use threads in Python.

What I usually do, when I'm testing stuff, is use SDL to see if a key is pressed or released, this way I can "walk" around a scene and "rotate" and so on. There is an interface of SDL in Python somewhere. I've stumbled upon it once, but I have no idea what it's called. Google should answer that to you.

Personally, I'm building a GUI and some handlers to receive some input from the users in graphics mode and answering mouse clicks, but that's not a trivial stuff.

Hope I could be of some help.

- Sergio

jessejames
02-12-2009, 04:32 PM
Man thanks a ton!

I have my object model pretty much figured out now and i feel much more confidant. And your right, i will need to use display lists for all the static objects in my scene -- this will really speed up execution.

for the GUI I will be using the glcanvas included in the wxPython package to do my initial write-up. I could even use Python's built-in GUI(Tkinter) with a GLUT window packed into the root Window and easily catch keypresses that way too!

I plan to get "interactive" as soon as i can get a stable commandline version up and running to work out the bugs :)

Things are coming together fast now, thanks for your wonderful help. Keep your eye's peeled, i might need your help again :)

PS: as soon i as learn this GL thing you guy's will see me helping out here too!

Later!

jessejames
03-06-2009, 07:48 PM
Hello all,
I have been away for some time but i am begining my quest for OpenGL-fu again. I hope you guy's can see this post since the thread is almost a month old.

I can now successfully create some geometry and store the objects in a simplified cache. I have a much better understanding of how GL works but still have much to learn.

My current delimma is how will i create my geometry thru a command line interface. I tried grabbing std_io from within the main GLUT loop but that causes everything to freeze up. So currently all i can do is hardcode my geometry and then run the program. I really don't want to use a thread for input and a thread for GLUT. There must be a way thru the "glutIdleFunc()" or something??

Below is a simplified version of my code so far. I have a class called "Entities" that is my cache. "Entity", which is a base class for all primitives and two primitive classes, "Face" and "Edge". I know this code is horrible at the moment so please go easy on me since i am just a beginner to OpenGL and Graphics in general. Eventually this whole thing will be wraped into a "Model" class but for now getting anything to work is good enough for me :)




class Entities(list):
def __init__(self):
list.__init__(self)

def draw(self):
for obj in self:
obj.draw()

def add_item(self, item):
self.append( item ) #just append a class object to entities

def add_face(self, coords):
self.append( Face(coords) )

def add_edge(self, coords):
self.append( Edge(coords) )

def remove(self, item):
super(Entities, self).remove(item)

class Entity():
def __init__(self):
pass

class Face(Entity):
def __init__(self, pts):
self.type = 'Face'
self.vertices = [Point3d(p) for p in pts]
v1 = self.vertices[0].vector_to(self.vertices[1])
v2 = self.vertices[1].vector_to(self.vertices[2])
self.normal = v1.cross(v2).normalized()
Entity.__init__(self)

def draw(self):
N = self.normal
glBegin(GL_QUADS)
glNormal3f(N.x, N.y, N.z)
for v in self.vertices:
glVertex3f(v.x, v.y, v.z)
glEnd()

def reverse(self):
self.normal = -self.normal

def extrude(self, amt, new=0):
f2 = [p.offset(self.normal*amt) for p in self.vertices]
f1 = self.vertices
l = [(f2[c], f1[c], f1[c-1], f2[c-1]) for c in range(len(f1))] + [tuple(f2)]
self.vertices = f2
return l

class Edge(Entity):
def __init__(self, pts):
self.type = 'Edge'
self.vertices = [Point3d(p) for p in pts]
Entity.__init__(self)

def draw(self):
glColor3f(0.0,1.0,0.0)
glBegin(GL_LINES)
for v in self.vertices:
glVertex3f(v.x, v.y, v.z)
glEnd()

#------------------#
#-- GL functions --#
#------------------#

def update_view():
glColor3f(1.0,0.0,0.0)#set square to red
ents.draw()


def init():#-- Lights --#
glEnable(GL_DEPTH_TEST)
glEnable(GL_LIGHTING)
glEnable(GL_LIGHT0)

def display():#-- GL function --#
glClearDepth(1) #for lighting
glClearColor(0.0,0.0,0.0,1.0)
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glLoadIdentity()
gluLookAt(5.0,5.0,5.0, #eye
0.0,0.0,0.0, #target
0.0,1.0,0.0) #up
update_view()
glutSwapBuffers()

def reshape(w, h):#-- GL window resize event -- #
glViewport(0,0,w,h)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(60,w/h, 1.0, 100)
glMatrixMode(GL_MODELVIEW)

def keyboard(key, w, h):
print key, w, h
if key == ESCAPE:
sys.exit(0)

def main():
glutInit(sys.argv)
glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA)
glutInitWindowSize(500,500)
glutInitWindowPosition(100,100)
glutCreateWindow("Blank Window")
init() #lighting
glutDisplayFunc(display) #--display--#
#glutIdleFunc(display) #change any idle values accordingly
glutReshapeFunc(reshape) #--reshape--#
glutKeyboardFunc(keyboard)
glutMainLoop()

#-- create a face and extrude it into 3D and create an edge--#

ents = Entities()
ents.add_face(((0.0,0.0,0.0),(1.0,0.0,0.0),(1.0,1. 0,0.0),(0.0,1.0,0.0)))
ents.add_edge(((0.5,0.5,0.0),(0.5,0.5,5.0)))
coords = ents[0].extrude(1)
for tup in coords:
ents.add_face(tup)
print ents

main()


So the main issue here is how to interrupt the GLUT main function so i can read input from the user?

ZbuffeR
03-07-2009, 04:51 AM
async io is not easy, using a separate thread is probably best.
You needs seem related to a quake-like console, read this discussion, there are some interesting links :
http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=244372#Post244372

jessejames
03-09-2009, 01:25 PM
Thanks Zbuffer,

I have now switched from glut to wxPython.glCanvas so this fixed my "stuck in a loop" problem :).

I have made much progress now but camera manipulations seem to be a problem. I actually have zoom working correctly and my orbit works but very poorly. Since zoom, orbit, and pan are such fundamental needs for transversing a 3D enviroment i am wondering why OpenGL has not simplified this issue -- oh well?

Here is my code but there is a problem. I need left/right mouse movements to rotate around the Yaxis, no problem here. However the up/down mouse movements need to rotate a combination of Xaxis and Zaxis depending on the direction my camera is pointing. Any ideas or links to example code would be great.

Thanks all

EDIT:
i just thought of something...
I could use the the camera direction vector and up vector to get a cross vector for which to use as rotation axis Although i would need the inverse of this vector to offset the camera's target point to find a 3D point which would be the new eye of my camera. Then apply the new eye and new up vectors to the glLookAt() function -- Whew, but there must be an easier way??

Question: How would i fetch the camera's current eye, target, and up info? Currently i set these parameters at run time and modifiy them with every move of the camera, but there is a real need for the ability to query the camera's info as Point(eye), Point(target), and Vector(up).