# HG changeset patch # User John Tsiombikas # Date 1423891632 -7200 # Node ID 2f872a1799144884eaaa28de435fd7336692b581 # Parent 0e5da17d589ced306767a9ea8dd82450b8d9da33 first component test: - prs, xform, physics components with dependencies - topological sort of components to update them in the correct order - debug visualization component todo: remove draw() from components, doesn't make sense diff -r 0e5da17d589c -r 2f872a179914 Makefile --- a/Makefile Sat Feb 14 01:35:42 2015 +0200 +++ b/Makefile Sat Feb 14 07:27:12 2015 +0200 @@ -1,5 +1,6 @@ ccsrc = $(wildcard src/*.cc) obj = $(ccsrc:.cc=.o) +dep = $(obj:.o=.d) bin = test warn = -pedantic -Wall @@ -18,6 +19,15 @@ $(bin): $(obj) $(CXX) -o $@ $(obj) $(LDFLAGS) +-include $(dep) + +%.d: %.cc + @$(CPP) $(CXXFLAGS) $< -MM -MT $(@:.d=.o) >$@ + .PHONY: clean clean: rm -f $(obj) $(bin) + +.PHONY: cleandep +cleandep: + rm -f $(dep) diff -r 0e5da17d589c -r 2f872a179914 src/co_dbgvis.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/co_dbgvis.cc Sat Feb 14 07:27:12 2015 +0200 @@ -0,0 +1,48 @@ +#ifndef __APPLE__ +#include +#else +#include +#endif +#include "co_dbgvis.h" +#include "gobj.h" + +static CoSphereVis reg_co_sphere; + +static Component *cons_sphere() { return new CoSphereVis; } + + +CoSphereVis::CoSphereVis() + : color(1, 1, 1) +{ + name = "spherevis"; + radius = 1.0; + co_xform = 0; + + register_component(name, cons_sphere); +} + +void CoSphereVis::update(float dt) +{ + if(!co_xform) { + co_xform = COCAST(CoXForm, gobj->get_component("xform")); + } +} + +void CoSphereVis::draw() const +{ + if(co_xform) { + Matrix4x4 xform = co_xform->xform.transposed(); + + glPushMatrix(); + glMultMatrixf(xform.m[0]); + } + + float diffuse[] = { color.x, color.y, color.z, 1.0f }; + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, diffuse); + + glutSolidSphere(radius, 16, 8); + + if(co_xform) { + glPopMatrix(); + } +} diff -r 0e5da17d589c -r 2f872a179914 src/co_dbgvis.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/co_dbgvis.h Sat Feb 14 07:27:12 2015 +0200 @@ -0,0 +1,22 @@ +#ifndef CO_DBGVIS_H_ +#define CO_DBGVIS_H_ + +#include "gobj.h" +#include "comp.h" +#include "co_xform.h" + +class CoSphereVis : public Component { +protected: + CoXForm *co_xform; + +public: + float radius; + Vector3 color; + + CoSphereVis(); + + void update(float dt); + void draw() const; +}; + +#endif // CO_DBGVIS_H_ diff -r 0e5da17d589c -r 2f872a179914 src/co_phys.cc --- a/src/co_phys.cc Sat Feb 14 01:35:42 2015 +0200 +++ b/src/co_phys.cc Sat Feb 14 07:27:12 2015 +0200 @@ -1,4 +1,6 @@ +#include #include "co_phys.h" +#include "sim.h" static CoRigid reg_co_rigid; @@ -15,6 +17,42 @@ register_component(name, cons_rigid); } -void CoRigid::update() +const char **CoRigid::update_before() const { + static const char *before[] = { "prs", 0 }; + return before; } + +void CoRigid::add_impulse(const Vector3 &v) +{ + impulse += v; +} + +void CoRigid::update(float dt) +{ + if(!gobj) return; + + if(!co_prs) { + if(!(co_prs = COCAST(CoPRS, gobj->get_component("prs")))) { + assert(co_prs); + return; + } + } + + float damping = world ? world->get_damping() : 0.005; + + Vector3 newpos = co_prs->pos + vel * dt; + + Vector3 accel = impulse; + impulse.x = impulse.y = impulse.z = 0.0f; // reset impulse + + if(world) { + accel += world->get_gravity(); + } + + vel = (vel - vel * damping * dt) + accel * dt; + + // TODO collisions + + co_prs->pos = newpos; +} diff -r 0e5da17d589c -r 2f872a179914 src/co_phys.h --- a/src/co_phys.h Sat Feb 14 01:35:42 2015 +0200 +++ b/src/co_phys.h Sat Feb 14 07:27:12 2015 +0200 @@ -3,15 +3,27 @@ #include #include "comp.h" +#include "co_xform.h" + +class SimWorld; class CoRigid : public Component { +protected: + CoPRS *co_prs; // cached PRS component of the parent object + + const char **update_before() const; + public: + SimWorld *world; float mass, elast, friction; - Vector3 pos, vel; + Vector3 vel, force; + Vector3 impulse; CoRigid(); - void update(); + void add_impulse(const Vector3 &v); + + void update(float dt); }; #endif // COMP_PHYS_H_ diff -r 0e5da17d589c -r 2f872a179914 src/co_xform.cc --- a/src/co_xform.cc Sat Feb 14 01:35:42 2015 +0200 +++ b/src/co_xform.cc Sat Feb 14 07:27:12 2015 +0200 @@ -15,20 +15,28 @@ register_component(name, cons_xform); } +// ---- class CoPRS ---- + CoPRS::CoPRS() + : scale(1, 1, 1) { name = "prs"; register_component(name, cons_prs); } -void CoPRS::update() +const char **CoPRS::update_before() const +{ + static const char *before[] = { "xform", 0 }; + return before; +} + +void CoPRS::update(float dt) { if(!gobj) return; if(!co_xform) { - Component *co = gobj->get_component("xform"); - if(!co || !(co_xform = dynamic_cast(co))) { + if(!(co_xform = COCAST(CoXForm, gobj->get_component("xform")))) { assert(co_xform); return; } diff -r 0e5da17d589c -r 2f872a179914 src/co_xform.h --- a/src/co_xform.h Sat Feb 14 01:35:42 2015 +0200 +++ b/src/co_xform.h Sat Feb 14 07:27:12 2015 +0200 @@ -12,16 +12,18 @@ }; class CoPRS : public Component { -private: +protected: CoXForm *co_xform; // cached xform component of the parent object + const char **update_before() const; + public: Vector3 pos, scale; Quaternion rot; CoPRS(); - void update(); + void update(float dt); }; diff -r 0e5da17d589c -r 2f872a179914 src/comp.cc --- a/src/comp.cc Sat Feb 14 01:35:42 2015 +0200 +++ b/src/comp.cc Sat Feb 14 07:27:12 2015 +0200 @@ -10,28 +10,41 @@ { name = "unknown"; gobj = 0; - upd_prio = 0; } Component::~Component() { } +void Component::attach(GObject *gobj) +{ + this->gobj = gobj; +} + +void Component::detach() +{ + gobj = 0; +} + +const char **Component::update_before() const +{ + static const char *before[] = { 0 }; + return before; +} + const char *Component::get_name() const { return name; } -void Component::update() +void Component::update(float dt) { } -bool Component::operator <(const Component &c) const +void Component::draw() const { - return upd_prio < c.upd_prio; } - void register_component(const char *name, Component *(*cons_func)()) { if(!early_comp_cons) { diff -r 0e5da17d589c -r 2f872a179914 src/comp.h --- a/src/comp.h Sat Feb 14 01:35:42 2015 +0200 +++ b/src/comp.h Sat Feb 14 07:27:12 2015 +0200 @@ -3,13 +3,31 @@ #include -class GameObject; +#ifdef NDEBUG +#define COCAST(type, x) ((type*)x) +#else +#define COCAST(type, x) dynamic_cast(x) +#endif + +class GObject; class Component { protected: const char *name; - GameObject *gobj; - int upd_prio; // update priority (0: normal) + GObject *gobj; + + /* attach/detach to a particular GObject. + * This is only called from GObject::add_component and + * GObject::remove_component respectively + */ + virtual void attach(GObject *gobj); + virtual void detach(); + + /* Returns an array of component names which depend on this and + * must not be updated first if both exist in the same object. + * Terminated by a null pointer. + */ + virtual const char **update_before() const; public: Component(); @@ -17,11 +35,10 @@ const char *get_name() const; - virtual void update(); + virtual void update(float dt); + virtual void draw() const; - bool operator <(const Component &c) const; // for sorting based on priority - - friend class GameObject; + friend class GObject; }; void register_component(const char *name, Component *(*cons_func)()); diff -r 0e5da17d589c -r 2f872a179914 src/gobj.cc --- a/src/gobj.cc Sat Feb 14 01:35:42 2015 +0200 +++ b/src/gobj.cc Sat Feb 14 07:27:12 2015 +0200 @@ -1,25 +1,27 @@ #include +#include +#include #include #include "gobj.h" -GameObject::GameObject() +GObject::GObject() { sorted = true; } -GameObject::~GameObject() +GObject::~GObject() { for(size_t i=0; iget_name(); if(comp_by_name.find(name) != comp_by_name.end()) { - fprintf(stderr, "component %s already exists in this game object (%p)\n", name, (void*)this); + fprintf(stderr, "component %s already exists in this gobject (%p)\n", name, (void*)this); return false; } @@ -27,7 +29,7 @@ comp.push_back(c); comp_by_name[name] = c; - c->gobj = this; + c->attach(this); } catch(...) { fprintf(stderr, "failed to add component: %s\n", name); @@ -38,10 +40,11 @@ return true; } -bool GameObject::remove_component(Component *c) +bool GObject::remove_component(Component *c) { for(size_t i=0; idetach(); comp.erase(comp.begin() + i); comp_by_name.erase(c->get_name()); return true; @@ -50,7 +53,7 @@ return false; } -bool GameObject::delete_component(Component *c) +bool GObject::delete_component(Component *c) { if(remove_component(c)) { delete c; @@ -59,7 +62,7 @@ return false; } -Component *GameObject::get_component(const char *name) const +Component *GObject::get_component(const char *name) const { std::map::const_iterator it; if((it = comp_by_name.find(name)) != comp_by_name.end()) { @@ -68,13 +71,119 @@ return 0; } -void GameObject::update() +void GObject::update(float dt) { if(!sorted) { - std::sort(comp.begin(), comp.end()); + sort_components(); + sorted = true; } + printf("obj(%p) update\n", (void*)this); for(size_t i=0; iupdate(); + printf(" updating component: %s\n", comp[i]->get_name()); + comp[i]->update(dt); } } + +void GObject::draw() const +{ + // TODO optimization: draw only specific components? + for(size_t i=0; idraw(); + } +} + +struct TopoSucNode { + int idx; + TopoSucNode *next; +}; + +struct TopoItem { + Component *co; + int npre; // number of predeccessors + TopoSucNode *suc; // successor list + TopoItem *qnext, *qprev; // queue list links +}; + +int countq(TopoItem *q) +{ + int n = 0; + while(q) { + n++; + q = q->qnext; + } + return n; +} + +// topological sort of components based on update_before() relationships +void GObject::sort_components() +{ + int nitems = (int)comp.size(); + TopoItem *items = (TopoItem*)alloca(nitems * sizeof *items); + TopoItem *queue = items; + + // initialize the items + for(int i=0; i 0 ? items + i - 1 : 0; + } + + // populate the array + for(int i=0; iupdate_before(); + while(*iter) { + const char *sucname = *iter++; + + for(int j=0; jget_name(), sucname) == 0) { + // found a component(j) depending on i, increment its predeccessor counter + items[j].npre++; + // remove it from the queue + if(queue == items + j) { + queue = items[j].qnext; + assert(items[j].qprev == 0); + } + if(items[j].qprev) items[j].qprev->qnext = items[j].qnext; + if(items[j].qnext) items[j].qnext->qprev = items[j].qprev; + items[j].qprev = items[j].qnext = 0; + + // and add it to our successor list + TopoSucNode *n = (TopoSucNode*)alloca(sizeof *n); + n->idx = j; + n->next = items[i].suc; + items[i].suc = n; + } + } + } + } + + // now remove each item from the queue and add it to the sorted list + int idx = 0; + while(queue) { + assert(idx < nitems); + comp[idx++] = queue->co; + + // traverse the successor list, decrementing their predeccessor counts + TopoSucNode *n = queue->suc; + queue = queue->qnext; + + while(n) { + TopoItem *it = items + n->idx; + n = n->next; + + assert(it->npre > 0); + if(--it->npre <= 0) { + // if the count reached zero, add it to the queue + it->qprev = 0; + it->qnext = queue; + queue = it; + } + } + } + + assert(idx == nitems); +} diff -r 0e5da17d589c -r 2f872a179914 src/gobj.h --- a/src/gobj.h Sat Feb 14 01:35:42 2015 +0200 +++ b/src/gobj.h Sat Feb 14 07:27:12 2015 +0200 @@ -1,29 +1,32 @@ -#ifndef GAMEOBJECT_H_ -#define GAMEOBJECT_H_ +#ifndef GOBJECT_H_ +#define GOBJECT_H_ #include #include #include #include "comp.h" -class GameObject { +class GObject { private: std::vector comp; std::map comp_by_name; bool sorted; + void sort_components(); + public: - GameObject(); - ~GameObject(); + GObject(); + ~GObject(); - bool add_component(Component *c); // takes ownership - bool remove_component(Component *c); + bool add_component(Component *c); // takes ownership + bool remove_component(Component *c); // whoever calls this, now has ownership bool delete_component(Component *c); Component *get_component(const char *name) const; - void update(); + void update(float dt = 0.0f); + void draw() const; }; -#endif // GAMEOBJECT_H_ +#endif // GOBJECT_H_ diff -r 0e5da17d589c -r 2f872a179914 src/sim.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/sim.cc Sat Feb 14 07:27:12 2015 +0200 @@ -0,0 +1,66 @@ +#include +#include "sim.h" + +SimWorld::SimWorld() + : gravity(0, -9, 0) +{ + damping = 0.005; +} + +void SimWorld::add_object(GObject *obj) +{ + CoRigid *co = COCAST(CoRigid, obj->get_component("phys")); + if(co) { + add_rigid_body(co); + } else { + fprintf(stderr, "SimWorld::add_object: trying to add object without rigid body component\n"); + } +} + +void SimWorld::add_rigid_body(CoRigid *co) +{ + if(std::find(rigid.begin(), rigid.end(), co) == rigid.end()) { + rigid.push_back(co); + co->world = this; + } +} + +void SimWorld::remove_object(GObject *obj) +{ + CoRigid *co = COCAST(CoRigid, obj->get_component("phys")); + if(co) { + remove_rigid_body(co); + } else { + fprintf(stderr, "SimWorld::remove_object: failed to remove object without rigid body component\n"); + } +} + +void SimWorld::remove_rigid_body(CoRigid *co) +{ + std::list::iterator it = std::find(rigid.begin(), rigid.end(), co); + if(it != rigid.end()) { + rigid.erase(it); + } else { + fprintf(stderr, "SimWorld::remove_rigid_body: failed to remove missing rigid body\n"); + } +} + +void SimWorld::set_damping(float damping) +{ + this->damping = damping; +} + +float SimWorld::get_damping() const +{ + return damping; +} + +void SimWorld::set_gravity(const Vector3 &v) +{ + gravity = v; +} + +const Vector3 &SimWorld::get_gravity() const +{ + return gravity; +} diff -r 0e5da17d589c -r 2f872a179914 src/sim.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/sim.h Sat Feb 14 07:27:12 2015 +0200 @@ -0,0 +1,30 @@ +#ifndef COENG_SIM_H_ +#define COENG_SIM_H_ + +#include +#include "co_phys.h" +#include "gobj.h" + +class SimWorld { +private: + std::list rigid; + Vector3 gravity; + float damping; + +public: + SimWorld(); + + void add_object(GObject *obj); // convenience function, calls add_rigid_body + void add_rigid_body(CoRigid *co); + + void remove_object(GObject *obj); // convenience function, calls remove_rigid_body + void remove_rigid_body(CoRigid *co); + + void set_damping(float damping); + float get_damping() const; + + void set_gravity(const Vector3 &v); + const Vector3 &get_gravity() const; +}; + +#endif // COENG_SIM_H_ diff -r 0e5da17d589c -r 2f872a179914 src/test.cc --- a/src/test.cc Sat Feb 14 01:35:42 2015 +0200 +++ b/src/test.cc Sat Feb 14 07:27:12 2015 +0200 @@ -1,6 +1,7 @@ #include #include #include +#include #ifndef __APPLE__ #include @@ -8,6 +9,11 @@ #include #endif +#include "gobj.h" +#include "co_xform.h" +#include "co_dbgvis.h" +#include "sim.h" + static bool init(); static void cleanup(); static void display(); @@ -17,6 +23,11 @@ static void mouse(int bn, int st, int x, int y); static void motion(int x, int y); +float cam_theta, cam_phi = 25, cam_dist = 8; + +std::vector objects; +SimWorld simworld; + int main(int argc, char **argv) { @@ -26,7 +37,7 @@ glutCreateWindow("component system test"); glutDisplayFunc(display); - glutIdleFunc(idle); + //glutIdleFunc(idle); glutReshapeFunc(reshape); glutKeyboardFunc(keyb); glutMouseFunc(mouse); @@ -46,6 +57,26 @@ glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + float ldir[] = {-1, 1, 2, 0}; + glLightfv(GL_LIGHT0, GL_POSITION, ldir); + + GObject *obj = new GObject; + obj->add_component(new CoSphereVis); + obj->add_component(new CoXForm); + obj->add_component(new CoPRS); + obj->add_component(new CoRigid); + COCAST(CoPRS, obj->get_component("prs"))->pos = Vector3(5, 1, 0); + objects.push_back(obj); + + obj = new GObject; + obj->add_component(new CoSphereVis); + obj->add_component(new CoXForm); + obj->add_component(new CoPRS); + COCAST(CoPRS, obj->get_component("prs"))->pos = Vector3(-5, 1, 0); + objects.push_back(obj); + return true; } @@ -53,10 +84,48 @@ { } +static void update() +{ + static unsigned int prev_upd; + unsigned int msec = glutGet(GLUT_ELAPSED_TIME); + float dt = (float)(msec - prev_upd) / 1000.0f; + prev_upd = msec; + + for(size_t i=0; iupdate(dt); + } +} + static void display() { + update(); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0, 0, -cam_dist); + glRotatef(cam_phi, 1, 0, 0); + glRotatef(cam_theta, 0, 1, 0); + + float floor_col[] = {0.2, 0.8, 0.2, 1}; + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, floor_col); + + glBegin(GL_QUADS); + glNormal3f(0, 1, 0); + glVertex3f(-10, 0, 10); + glVertex3f(10, 0, 10); + glVertex3f(10, 0, -10); + glVertex3f(-10, 0, -10); + glEnd(); + + for(size_t i=0; iget_component("spherevis")); + if(co) { + co->draw(); + } + } + glutSwapBuffers(); assert(glGetError() == GL_NO_ERROR); } @@ -83,13 +152,35 @@ } } -static int bnstate[16]; +static bool bnstate[16]; static int prev_x, prev_y; static void mouse(int bn, int st, int x, int y) { + bnstate[bn - GLUT_LEFT_BUTTON] = st == GLUT_DOWN; + prev_x = x; + prev_y = y; } static void motion(int x, int y) { + int dx = x - prev_x; + int dy = y - prev_y; + prev_x = x; + prev_y = y; + + if(bnstate[0]) { + cam_theta += dx * 0.5; + cam_phi += dy * 0.5; + + if(cam_phi < -90) cam_phi = -90; + if(cam_phi > 90) cam_phi = 90; + + glutPostRedisplay(); + } + if(bnstate[2]) { + cam_dist += dy * 0.1; + + if(cam_dist < 0.0) cam_dist = 0.0; + } }