long time_msec;
int win_width, win_height;
bool opt_gear_wireframe;
+bool show_walk_mesh;
static float cam_dist = 0.0;
static float cam_theta, cam_phi = 20;
return false;
}
- /*
- datamap_set_path("data");
- if(!datamap_load_map("data.map")) {
- fprintf(stderr, "failed to load datafile mappings\n");
- }
-
- unsigned int sflags = SCNLOAD_FLIPTEX;
- if(!(scn->load("data/testscene/patoma.fbx", sflags)) ||
- !(scn->load("data/testscene/kolones.fbx", sflags))) {
- fprintf(stderr, "failed to load test scene\n");
- return false;
- }
- */
-
if(!(sdr = create_program_load("sdr/test.v.glsl", "sdr/test.p.glsl"))) {
fprintf(stderr, "failed to load test shaders\n");
return false;
glUseProgram(sdr);
scn->draw();
glUseProgram(0);
+
+ if(show_walk_mesh && scn->walk_mesh) {
+ glPushAttrib(GL_ENABLE_BIT);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_ONE, GL_ONE);
+ glDisable(GL_LIGHTING);
+
+ glColor3f(0.5, 0.4, 0.05);
+ scn->walk_mesh->draw();
+
+ glPopAttrib();
+ }
}
void app_keyboard(int key, bool pressed)
{
if(pressed) {
+ printf("key: %d (mod: %x)\n", key, app_get_modifiers());
switch(key) {
case 27:
app_quit();
break;
+
+ case 'w':
+ if(app_get_modifiers() & MOD_CTRL) {
+ show_walk_mesh = !show_walk_mesh;
+ }
+ break;
}
}
extern int win_width, win_height;
extern bool opt_gear_wireframe;
+enum {
+ MOD_SHIFT = 1,
+ MOD_ALT = 2,
+ MOD_CTRL = 4
+};
+
bool app_init();
void app_cleanup();
// the following functions are implemented by the backend (main.cc)
void app_quit();
void app_swap_buffers();
+unsigned int app_get_modifiers();
#endif // APP_H_
static void key_press(unsigned char key, int x, int y);
static void key_release(unsigned char key, int x, int y);
static void mouse(int bn, int st, int x, int y);
+static void proc_modkeys();
static unsigned int start_time;
+static unsigned int modkeys;
int main(int argc, char **argv)
{
exit(0);
}
+unsigned int app_get_modifiers()
+{
+ return modkeys;
+}
+
static bool init()
{
glewInit();
static void key_press(unsigned char key, int x, int y)
{
+ proc_modkeys();
app_keyboard(key, true);
}
static void key_release(unsigned char key, int x, int y)
{
+ proc_modkeys();
app_keyboard(key, false);
}
int bidx = bn - GLUT_LEFT_BUTTON;
bool down = st == GLUT_DOWN;
+ proc_modkeys();
app_mouse_button(bidx, down, x, y);
}
+
+static void proc_modkeys()
+{
+ int glutmod = glutGetModifiers();
+
+ modkeys = 0;
+ if(glutmod & GLUT_ACTIVE_SHIFT) {
+ modkeys |= MOD_SHIFT;
+ }
+ if(glutmod & GLUT_ACTIVE_CTRL) {
+ modkeys |= MOD_CTRL;
+ }
+ if(glutmod & GLUT_ACTIVE_ALT) {
+ modkeys |= MOD_ALT;
+ }
+}
#endif
static bool proc_node(Scene *scn, struct ts_node *node);
+static bool proc_scenefile(Scene *scn, struct ts_node *node);
static struct ts_attr *attr_inscope(struct ts_node *node, const char *name);
+static void print_scene_graph(SceneNode *n, int level);
+
bool load_scene(Scene *scn, const char *fname)
{
struct ts_node *root = ts_load(fname);
bool res = proc_node(scn, root);
ts_free_tree(root);
+
+ printf("loaded scene: %s\n", fname);
+ printf("scene graph:\n");
+ print_scene_graph(scn->nodes, 0);
return res;
}
// do this last to allow other contents of the node to do their thing
if(strcmp(node->name, "scenefile") == 0) {
- const char *fname = ts_get_attr_str(node, "file");
- if(fname) {
- struct ts_attr *adpath = attr_inscope(node, "datapath");
- if(adpath && adpath->val.type == TS_STRING) {
- printf("adding data path: %s\n", adpath->val.str);
- datamap_set_path(adpath->val.str);
- }
-
- int namesz = datamap_lookup(fname, 0, 0);
- char *namebuf = (char*)alloca(namesz + 1);
- if(datamap_lookup(fname, namebuf, namesz + 1)) {
- fname = namebuf;
- }
-
- if(!(scn->load(fname, SCNLOAD_FLIPTEX))) {
- return false;
- }
- }
- datamap_reset();
+ return proc_scenefile(scn, node);
} else if(strcmp(node->name, "remap") == 0) {
const char *match = ts_get_attr_str(node, "match");
return true;
}
+static bool proc_scenefile(Scene *scn, struct ts_node *node)
+{
+ const char *fname = ts_get_attr_str(node, "file");
+ if(fname) {
+ // datapath
+ struct ts_attr *adpath = attr_inscope(node, "datapath");
+ if(adpath && adpath->val.type == TS_STRING) {
+ printf("adding data path: %s\n", adpath->val.str);
+ datamap_set_path(adpath->val.str);
+ }
+
+ // walkmesh
+ char *walkmesh_regexp = 0;
+ struct ts_attr *awmesh = attr_inscope(node, "walkmesh");
+ if(awmesh && awmesh->val.type == TS_STRING) {
+ walkmesh_regexp = awmesh->val.str;
+ }
+
+ int namesz = datamap_lookup(fname, 0, 0);
+ char *namebuf = (char*)alloca(namesz + 1);
+ if(datamap_lookup(fname, namebuf, namesz + 1)) {
+ fname = namebuf;
+ }
+
+ if(!(scn->load(fname, SCNLOAD_FLIPTEX))) {
+ return false;
+ }
+
+ // extract the walk mesh if necessary
+ // XXX as written, this will match even objects loaded by previous scenefile blocks
+ Scene *wscn;
+ if(walkmesh_regexp && (wscn = scn->extract_nodes(walkmesh_regexp))) {
+ // apply all transformations to the meshes
+ wscn->apply_xform();
+
+ int nmeshes = wscn->meshes.size();
+ for(int i=0; i<nmeshes; i++) {
+ Mesh *m = wscn->meshes[i];
+
+ if(scn->walk_mesh) {
+ scn->walk_mesh->append(*m);
+ } else {
+ scn->walk_mesh = m;
+ wscn->remove_mesh(m); // to save it from destruction
+ }
+ }
+
+ delete wscn;
+ }
+ }
+ datamap_reset();
+
+ return true;
+}
+
static struct ts_attr *attr_inscope(struct ts_node *node, const char *name)
{
struct ts_attr *attr = 0;
}
return attr;
}
+
+static void print_scene_graph(SceneNode *n, int level)
+{
+ if(!n) return;
+
+ for(int i=0; i<level; i++) {
+ fputs(" ", stdout);
+ }
+
+ int nobj = n->get_num_objects();
+ if(nobj) {
+ printf("%s - %d obj\n", n->get_name(), n->get_num_objects());
+ } else {
+ printf("%s\n", n->get_name());
+ }
+
+ for(int i=0; i<n->get_num_children(); i++) {
+ print_scene_graph(n->get_child(i), level + 1);
+ }
+}
+#include <regex>
#include "scene.h"
+#include "objmesh.h"
static void destroy_node_tree(SceneNode *n);
{
nodes = 0;
+ walk_mesh = 0;
+
if(tset) {
texset = tset;
own_texset = false;
destroy_node_tree(nodes);
nodes = 0;
+ for(int i=0; i<(int)meshes.size(); i++) {
+ delete meshes[i];
+ }
+ meshes.clear();
+
+ delete walk_mesh;
+ walk_mesh = 0;
+
+ for(int i=0; i<(int)objects.size(); i++) {
+ delete objects[i];
+ }
+ objects.clear();
+
if(own_texset) {
delete texset;
}
texset = 0;
}
+static void destroy_node_tree(SceneNode *n)
+{
+ if(!n) return;
+
+ int nsub = n->get_num_children();
+ for(int i=0; i<nsub; i++) {
+ destroy_node_tree(n->get_child(i));
+ }
+ delete n;
+}
+
// Scene::load defined in sceneload.cc
+void Scene::add_object(Object *obj)
+{
+ objects.push_back(obj);
+}
+
+bool Scene::remove_object(Object *obj)
+{
+ std::vector<Object*>::iterator it = std::find(objects.begin(), objects.end(), obj);
+ if(it != objects.end()) {
+ objects.erase(it);
+ return true;
+ }
+ return false;
+}
+
+bool Scene::have_object(Object *obj) const
+{
+ return std::find(objects.begin(), objects.end(), obj) != objects.end();
+}
+
+void Scene::add_mesh(Mesh *m)
+{
+ meshes.push_back(m);
+}
+
+bool Scene::remove_mesh(Mesh *m)
+{
+ std::vector<Mesh*>::iterator it = std::find(meshes.begin(), meshes.end(), m);
+ if(it != meshes.end()) {
+ meshes.erase(it);
+ return true;
+ }
+ return false;
+}
+
+bool Scene::have_mesh(Mesh *m) const
+{
+ return std::find(meshes.begin(), meshes.end(), m) != meshes.end();
+}
+
+void Scene::add_node(SceneNode *n)
+{
+ // we always want to have a dedicated root node
+ if(!nodes) {
+ nodes = new SceneNode;
+ nodes->scene = this;
+ nodes->set_name("root");
+ }
+
+ nodes->add_child(n);
+}
+
+bool Scene::remove_node(SceneNode *n)
+{
+ SceneNode *par;
+ if(!n || !(par = n->get_parent())) {
+ return false;
+ }
+
+ int nsub = n->get_num_children();
+ for(int i=0; i<nsub; i++) {
+ SceneNode *c = n->get_child(i);
+ n->remove_child(c);
+ par->add_child(c);
+ }
+
+ return par->remove_child(n);
+}
+
+bool Scene::have_node(SceneNode *n) const
+{
+ return n->scene == this;
+}
+
+static void find_nodes_rec(std::list<SceneNode*> *res, SceneNode *tree, const std::regex &re)
+{
+ if(std::regex_match(tree->get_name(), re)) {
+ res->push_back(tree);
+ }
+
+ int num = tree->get_num_children();
+ for(int i=0; i<num; i++) {
+ find_nodes_rec(res, tree->get_child(i), re);
+ }
+}
+
+Scene *Scene::extract_nodes(const char *qstr)
+{
+ std::regex re{qstr};
+
+ std::list<SceneNode*> nodelist;
+ find_nodes_rec(&nodelist, nodes, re);
+ if(nodelist.empty()) {
+ return 0;
+ }
+
+ Scene *res = new Scene(texset);
+
+ for(SceneNode *n : nodelist) {
+
+ int nobj = n->get_num_objects();
+ for(int i=0; i<nobj; i++) {
+ Object *obj = n->get_object(i);
+ if(obj->get_type() == OBJ_MESH) {
+ // XXX this assumes that meshes aren't shared between objects.
+ // maybe we'll have to refcount them at some point, and copy if nref>1
+ ObjMesh *om = (ObjMesh*)obj;
+ remove_mesh(om->mesh);
+ res->add_mesh(om->mesh);
+ }
+
+ remove_object(obj);
+ res->add_object(obj);
+ }
+
+ remove_node(n);
+ res->add_node(n);
+ }
+ return res;
+}
+
+void Scene::apply_xform()
+{
+ nodes->apply_xform();
+}
+
void Scene::update(float dt)
{
if(nodes) {
}
}
}
-
-static void destroy_node_tree(SceneNode *n)
-{
- if(!n) return;
-
- int nsub = n->get_num_children();
- for(int i=0; i<nsub; i++) {
- destroy_node_tree(n->get_child(i));
- }
- delete n;
-}
#define SCENE_H_
#include <vector>
+#include <list>
#include "mesh.h"
#include "snode.h"
#include "texture.h"
bool own_texset;
public:
+ // meshes objects and nodes are owned by Scene
std::vector<Mesh*> meshes;
std::vector<Object*> objects;
SceneNode *nodes;
- TextureSet *texset;
+ Mesh *walk_mesh;
+
+ TextureSet *texset; // only owned by Scene if own_texset is true
explicit Scene(TextureSet *tset = 0);
~Scene();
bool load(const char *fname, unsigned int flags = 0);
+ void add_object(Object *obj);
+ bool remove_object(Object *obj);
+ bool have_object(Object *obj) const;
+
+ void add_mesh(Mesh *m);
+ bool remove_mesh(Mesh *m);
+ bool have_mesh(Mesh *m) const;
+
+ void add_node(SceneNode *n);
+ bool remove_node(SceneNode *n);
+ bool have_node(SceneNode *n) const;
+
+ /* find and remove all nodes whose names match the regexp
+ * XXX at the moment this has the effect of flattening the hierarchy in the
+ * result scene. If we ever need verbatim extraction of whole subtrees we'll
+ * need to change the algorithm.
+ */
+ Scene *extract_nodes(const char *qstr);
+
+ /* bake all node transformations to their respective meshes and make the
+ * node transformations identity.
+ * XXX this assumes no mesh is shared by two nodes.
+ */
+ void apply_xform();
+
void update(float dt);
void draw() const;
};
static bool load_material(Scene *scn, Material *mat, const aiMaterial *aimat);
static SceneNode *load_node(Scene *scn, const aiScene *aiscn, unsigned int flags, const aiNode *ainode);
static Mesh *load_mesh(Scene *scn, const aiScene *aiscn, unsigned int flags, const aiMesh *aimesh);
-static void print_nodes(SceneNode *node, int lvl = 0);
/*static const char *mprop_semantic(int x);
static int count_textures(const aiMaterial *aimat);*/
static int assimp_textype(aiTextureType type);
root_pos = root_matrix.get_translation();
root_rot = root_matrix.get_rotation();
root_scaling = root_matrix.get_scaling();
-
- printf("assimp root node: %s\n", aiscn->mRootNode->mName.data);
- printf(" pos: %f %f %f\n", root_pos.x, root_pos.y, root_pos.z);
- printf(" rot: %f %+f %+f %+f\n", root_rot.w, root_rot.x, root_rot.y, root_rot.z);
- printf(" scaling: %f %f %f\n", root_scaling.x, root_scaling.y, root_scaling.z);
}
// load all meshes
if(!nodes) {
nodes = new SceneNode;
+ nodes->scene = this;
nodes->set_name("root");
nodes->set_position(root_pos);
nodes->set_rotation(root_rot);
aiReleaseImport(aiscn);
printf("loaded scene file: %s, %d meshes\n", fname, (int)meshes.size());
nodes->update(0);
- print_nodes(nodes);
return true;
}
} else {
mat->name = "unknown";
}
- printf("load_material: %s\n", mat->name.c_str());
+ //printf("load_material: %s\n", mat->name.c_str());
if(aiGetMaterialColor(aimat, AI_MATKEY_COLOR_DIFFUSE, &aicol) == 0) {
mat->diffuse = Vec3(aicol[0], aicol[1], aicol[2]);
assert(tex);
mat->textures.push_back(tex);
- printf(" DBG(%p)\n", (void*)tex);
-
if(textype != MTL_TEX_UNKNOWN && !mat->stdtex[textype]) {
mat->stdtex[textype] = tex;
}
Mesh *mesh = mesh_by_aimesh[aimesh];
if(mesh) {
ObjMesh *obj = new ObjMesh;
+ obj->set_name(mesh->get_name());
obj->mesh = mesh;
// also grab the material of this mesh
load_material(scn, &obj->mtl, aiscn->mMaterials[aimesh->mMaterialIndex]);
static Mesh *load_mesh(Scene *scn, const aiScene *aiscn, unsigned int flags, const aiMesh *aimesh)
{
Mesh *mesh = new Mesh;
+ mesh->set_name(aimesh->mName.data);
int num_verts = aimesh->mNumVertices;
int num_faces = aimesh->mNumFaces;
iptr[2] = aimesh->mFaces[i].mIndices[flags & SCNLOAD_FLIPYZ ? 1 : 2];
iptr += 3;
}
-
- AABox bbox = mesh->get_aabbox();
- printf("mesh bounding box: %f %f %f -> %f %f %f\n", bbox.min.x, bbox.min.y, bbox.min.z,
- bbox.max.x, bbox.max.y, bbox.max.z);
-
return mesh;
}
-static void print_nodes(SceneNode *node, int lvl)
-{
- Vec3 pos = node->get_node_position();
- Quat rot = node->get_node_rotation();
- Vec3 scale = node->get_node_scaling();
-
- const char *type = node->get_num_objects() > 0 ? "mesh node" : "null node";
-
- for(int i=0; i<lvl; i++) {
- fputs(" ", stdout);
- }
- printf("%s[%s] p(%g %g %g) rq(%g %+gi %+gj %+gk) s(%g %g %g)\n", type, node->get_name(),
- pos.x, pos.y, pos.z, rot.w, rot.x, rot.y, rot.z, scale.x, scale.y, scale.z);
-
- if(node->get_num_objects()) {
- Mat4 xform = node->get_matrix();
- xform.print(stdout);
- }
-
- int nchld = node->get_num_children();
- for(int i=0; i<nchld; i++) {
- print_nodes(node->get_child(i), lvl + 1);
- }
-}
-
static int assimp_textype(aiTextureType type)
{
switch(type) {
#include <assert.h>
#include <algorithm>
#include "snode.h"
+#include "objmesh.h"
SceneNode::SceneNode()
: scale(1, 1, 1)
{
+ scene = 0;
parent = 0;
name = 0;
}
SceneNode::SceneNode(Object *obj)
: scale(1, 1, 1)
{
+ scene = 0;
parent = 0;
name = 0;
add_object(obj);
children.push_back(node);
node->parent = this;
+ node->scene = scene;
}
bool SceneNode::remove_child(SceneNode *node)
if(it != children.end()) {
assert(node->parent == this);
node->parent = 0;
+ node->scene = 0;
+ children.erase(it);
return true;
}
return false;
{
update_node(dt);
- for(size_t i=0; i<children.size(); i++) {
+ int num = children.size();
+ for(int i=0; i<num; i++) {
children[i]->update(dt);
}
}
+void SceneNode::apply_xform()
+{
+ update_node();
+
+ // apply post-order to make sure we don't affect the children xform by our reset
+
+ int nchild = children.size();
+ for(int i=0; i<nchild; i++) {
+ children[i]->apply_xform();
+ }
+
+ int nobj = obj.size();
+ for(int i=0; i<nobj; i++) {
+ if(obj[i]->get_type() == OBJ_MESH) {
+ ObjMesh *om = (ObjMesh*)obj[i];
+ if(om->mesh) {
+ om->mesh->apply_xform(xform);
+ }
+ }
+ }
+
+ pos = Vec3(0, 0, 0);
+ rot = Quat::identity;
+ scale = Vec3(1, 1, 1);
+}
bool SceneNode::intersect(const Ray &ray, HitPoint *hit) const
{
#include "object.h"
#include "gmath/gmath.h"
+class Scene;
+
class SceneNode {
private:
char *name;
Mat4 inv_xform;
public:
+ Scene *scene; // scene to which this node belongs
Mat4 dbg_xform;
SceneNode();
void update_node(float dt = 0.0f);
void update(float dt = 0.0f);
+ void apply_xform();
+
bool intersect(const Ray &ray, HitPoint *hit) const;
};