# HG changeset patch # User John Tsiombikas # Date 1408862484 -10800 # Node ID 283cdfa7dda2c2822b1fde40b406f32420774794 # Parent 778ed91cb7fdb02c29f686ada1958ff7ef239ba5 added a crapload of code from goat3dgfx diff -r 778ed91cb7fd -r 283cdfa7dda2 Makefile --- a/Makefile Sat Aug 23 12:03:29 2014 +0300 +++ b/Makefile Sun Aug 24 09:41:24 2014 +0300 @@ -2,7 +2,7 @@ ccsrc = $(wildcard src/*.cc) obj = $(csrc:.c=.o) $(ccsrc:.cc=.o) dep = $(obj:.o=.d) -bin = vrchess +bin = conworlds # comment out to disable LibOVR (oculus sdk) #ovr_cflags = -DUSE_LIBOVR @@ -13,7 +13,7 @@ CFLAGS = -pedantic -Wall -g $(ovr_cflags) CXXFLAGS = -std=c++11 $(CFLAGS) -LDFLAGS = $(libgl_$(sys)) -lm -lvmath -limago $(ovr_libs) +LDFLAGS = $(libgl_$(sys)) -lm -lvmath -limago -lanim $(ovr_libs) libgl_unix = -lGL -lGLU -lglut -lGLEW libgl_mac = -framework OpenGL -framework GLUT -lGLEW diff -r 778ed91cb7fd -r 283cdfa7dda2 conworlds.sln --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/conworlds.sln Sun Aug 24 09:41:24 2014 +0300 @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "conworlds", "conworlds.vcxproj", "{714906B6-FD4C-4ECC-990C-247FA46B8801}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {714906B6-FD4C-4ECC-990C-247FA46B8801}.Debug|Win32.ActiveCfg = Debug|Win32 + {714906B6-FD4C-4ECC-990C-247FA46B8801}.Debug|Win32.Build.0 = Debug|Win32 + {714906B6-FD4C-4ECC-990C-247FA46B8801}.Release|Win32.ActiveCfg = Release|Win32 + {714906B6-FD4C-4ECC-990C-247FA46B8801}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff -r 778ed91cb7fd -r 283cdfa7dda2 conworlds.vcxproj --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/conworlds.vcxproj Sun Aug 24 09:41:24 2014 +0300 @@ -0,0 +1,117 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {714906B6-FD4C-4ECC-990C-247FA46B8801} + Win32Proj + conworlds + + + + Application + true + v120 + MultiByte + + + Application + false + v120 + false + MultiByte + + + + + + + + + + + + + true + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions);USE_LIBOVR + 4996;4244;4305 + $(SolutionDir)\src + + + Console + true + opengl32.lib;freeglutd.lib;glew32.lib;libvmath.lib;libimago2.lib;jpeglib.lib;libpng.lib;zlib.lib;libovrd.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);USE_LIBOVR + 4996;4244;4305 + $(SolutionDir)\src + + + Console + true + true + true + opengl32.lib;freeglut.lib;glew32.lib;libvmath.lib;libimago2.lib;jpeglib.lib;libpng.lib;zlib.lib;libovr.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 778ed91cb7fd -r 283cdfa7dda2 conworlds.vcxproj.filters --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/conworlds.vcxproj.filters Sun Aug 24 09:41:24 2014 +0300 @@ -0,0 +1,94 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;h;inl + + + {e881ab02-1a45-43f6-a15d-ee7f77256a1e} + + + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src\vr + + + src\vr + + + src\vr + + + src\vr + + + src\vr + + + src\vr + + + src\vr + + + src\vr + + + + + src + + + src + + + src + + + src + + + src + + + src + + + src\vr + + + src\vr + + + src\vr + + + src\vr + + + src\vr + + + \ No newline at end of file diff -r 778ed91cb7fd -r 283cdfa7dda2 src/assload.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/assload.cc Sun Aug 24 09:41:24 2014 +0300 @@ -0,0 +1,437 @@ +#include +#include "assload.h" +#include "logger.h" + +#ifdef USE_ASSIMP + +#include +#include +#include "assimp/cimport.h" +#include "assimp/scene.h" +#include "assimp/postprocess.h" +#include "texman.h" +#include "material.h" +#include "scene.h" + +using namespace std; + +static bool load_material(Material *mat, const aiMaterial *aimat); +static Object *load_node(const aiScene *aiscn, const aiNode *ainode); +static Mesh *load_mesh(const aiScene *aiscn, const aiMesh *aimesh); +static Curve *load_curve(const aiScene *aiscn, const aiMesh *aimesh); +static bool load_bones(Mesh *mesh, const aiMesh *aimesh); + +static Vector3 assimp_vector(const aiVector3D &v); +static Quaternion assimp_quat(const aiQuaternion &q); +static Matrix4x4 assimp_matrix(const aiMatrix4x4 &aim); +static long assimp_time(const aiAnimation *anim, double aitime); +static void print_hierarchy(const aiNode *node); + +static map obj_by_name; +static map mesh_by_aimesh; + +bool load_ass(Scene *scn, const char *fname) +{ + static bool init_done; + + if(!init_done) { + static aiLogStream log_stream = aiGetPredefinedLogStream(aiDefaultLogStream_STDOUT, 0); + aiAttachLogStream(&log_stream); + //aiEnableVerboseLogging(1); + init_done = true; + } + + unsigned int proc_flags = aiProcess_JoinIdenticalVertices | + aiProcess_CalcTangentSpace | + aiProcess_Triangulate | + aiProcess_SortByPType | + aiProcess_FlipUVs; + + const aiScene *aiscn = aiImportFile(datafile_path(fname).c_str(), proc_flags); + if(!aiscn) { + error_log("failed to load file: %s\n", fname); + return false; + } + + info_log("NODE HIERARCHY:\n"); + print_hierarchy(aiscn->mRootNode); + info_log("-------------------\n"); + + Vector3 root_pos, root_scaling(1.0, 1.0, 1.0); + Quaternion root_rot; + + if(aiscn->mRootNode) { + Matrix4x4 root_matrix = assimp_matrix(aiscn->mRootNode->mTransformation); + root_pos = root_matrix.get_translation(); + root_rot = root_matrix.get_rotation_quat(); + root_scaling = root_matrix.get_scaling(); + } + + // load all meshes + for(unsigned int i=0; imNumMeshes; i++) { + aiMesh *aimesh = aiscn->mMeshes[i]; + Mesh *mesh; + Curve *curve; + + switch(aimesh->mPrimitiveTypes) { + case aiPrimitiveType_TRIANGLE: + if((mesh = load_mesh(aiscn, aimesh))) { + mesh_by_aimesh[aimesh] = mesh; + scn->meshes.push_back(mesh); + } + break; + + case aiPrimitiveType_LINE: + if((curve = load_curve(aiscn, aimesh))) { + scn->curves.push_back(curve); + } + break; + + default: + error_log("unsupported primitive type: %u\n", aimesh->mPrimitiveTypes); + break; + } + } + + // load all the nodes recursively + for(unsigned int i=0; imRootNode->mNumChildren; i++) { + Object *obj = load_node(aiscn, aiscn->mRootNode->mChildren[i]); + if(obj) { + Object *dummy = new Object; + dummy->set_name((string("dummyroot_") + string(obj->get_name())).c_str()); + dummy->set_position(root_pos); + dummy->set_rotation(root_rot); + dummy->set_scaling(root_scaling); + dummy->add_child(obj); + + obj = dummy; + scn->objects.push_back(obj); + } + } + + // load and attach the bones to the meshes + for(unsigned int i=0; imNumMeshes; i++) { + aiMesh *aimesh = aiscn->mMeshes[i]; + + Mesh *mesh = mesh_by_aimesh[aimesh]; + load_bones(mesh, aimesh); + } + + obj_by_name.clear(); + mesh_by_aimesh.clear(); + + aiReleaseImport(aiscn); + return true; +} + +static bool load_material(Material *mat, const aiMaterial *aimat) +{ + aiColor4D aicol; + float shin, shin_str; + + if(aiGetMaterialColor(aimat, AI_MATKEY_COLOR_DIFFUSE, &aicol) == 0) { + mat->diffuse = Vector3(aicol[0], aicol[1], aicol[2]); + } + if(aiGetMaterialColor(aimat, AI_MATKEY_COLOR_SPECULAR, &aicol) == 0) { + mat->specular = Vector3(aicol[0], aicol[1], aicol[2]); + } + + unsigned int count = 1; + if(aiGetMaterialFloatArray(aimat, AI_MATKEY_SHININESS_STRENGTH, &shin_str, &count) != 0) { + shin_str = 1.0; + } + if(aiGetMaterialFloatArray(aimat, AI_MATKEY_SHININESS, &shin, &count) == 0) { + // XXX can't remember how I came up with this... + mat->shininess = shin * shin_str * 0.0001 * 128.0; + } + + // load textures + struct { int type; aiTextureType aitype; } textypes[] = { + {TEX_DIFFUSE, aiTextureType_DIFFUSE}, + {TEX_NORMAL, aiTextureType_NORMALS}, + {TEX_SPECULAR, aiTextureType_SPECULAR} + }; + + for(int i=0; itex[textypes[i].type] = texset.get(fname); + } + } + } + + return true; +} + +static Object *load_node(const aiScene *aiscn, const aiNode *ainode) +{ + Object *obj = new Object; + obj->set_name(ainode->mName.data); + + if(ainode->mNumMeshes) { + if(ainode->mNumMeshes > 1) { + info_log("%s warning: node %s has more than one meshes (%u)\n", __FUNCTION__, + ainode->mName.data, ainode->mNumMeshes); + } + + aiMesh *aimesh = aiscn->mMeshes[ainode->mMeshes[0]]; + obj->set_mesh(mesh_by_aimesh[aimesh]); + + // also grab the material of this mesh + load_material(&obj->material, aiscn->mMaterials[aimesh->mMaterialIndex]); + } + + // if there are animations, grab the first and try to use it + if(aiscn->mNumAnimations) { + aiAnimation *aianim = aiscn->mAnimations[0]; + aiNodeAnim *ainodeanim = 0; + for(unsigned int i=0; imNumChannels; i++) { + if(strcmp(aianim->mChannels[i]->mNodeName.data, ainode->mName.data) == 0) { + ainodeanim = aianim->mChannels[i]; + break; + } + } + + if(ainodeanim) { + // load all position (translation) keyframes + for(unsigned int i=0; imNumPositionKeys; i++) { + Vector3 pos = assimp_vector(ainodeanim->mPositionKeys[i].mValue); + long msec = assimp_time(aianim, ainodeanim->mPositionKeys[i].mTime); + obj->set_position(pos, msec); + } + + // load all rotation keyframes + for(unsigned int i=0; imNumRotationKeys; i++) { + Quaternion rot = assimp_quat(ainodeanim->mRotationKeys[i].mValue); + if(rot.length_sq() < SMALL_NUMBER) { + continue; + } + rot.normalize(); + long msec = assimp_time(aianim, ainodeanim->mRotationKeys[i].mTime); + obj->set_rotation(rot, msec); + } + + // load all scaling keyframes + for(unsigned int i=0; imNumScalingKeys; i++) { + Vector3 scale = assimp_vector(ainodeanim->mScalingKeys[i].mValue); + long msec = assimp_time(aianim, ainodeanim->mScalingKeys[i].mTime); + obj->set_scaling(scale, msec); + } + + obj->set_extrapolator(EXTRAP_REPEAT); // loop animation + } else { + Matrix4x4 local_matrix = assimp_matrix(ainode->mTransformation); + obj->set_local_matrix(local_matrix); + } + } + + /* recurse to all children */ + for(unsigned int i=0; imNumChildren; i++) { + Object *child = load_node(aiscn, ainode->mChildren[i]); + if(child) { + obj->add_child(child); + } + } + + obj_by_name[obj->get_name()] = obj; + return obj; +} + +static Mesh *load_mesh(const aiScene *aiscn, const aiMesh *aimesh) +{ + Mesh *mesh = new Mesh; + + int num_verts = aimesh->mNumVertices; + int num_faces = aimesh->mNumFaces; + + mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, (float*)aimesh->mVertices); + + if(aimesh->mNormals) { + mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, (float*)aimesh->mNormals); + } + if(aimesh->mTangents) { + mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, (float*)aimesh->mTangents); + } + if(aimesh->mTextureCoords[0]) { + mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 3, num_verts, (float*)aimesh->mTextureCoords[0]); + } + + if(aimesh->mBones) { + float *weights = mesh->set_attrib_data(MESH_ATTR_BONEWEIGHTS, 4, num_verts, 0); + float *boneidx = mesh->set_attrib_data(MESH_ATTR_BONEIDX, 4, num_verts, 0); + + memset(weights, 0, num_verts * 4 * sizeof *weights); + memset(boneidx, 0, num_verts * 4 * sizeof *boneidx); + + int *vertex_bone_count = new int[num_verts]; + memset(vertex_bone_count, 0, num_verts * sizeof *vertex_bone_count); + + for(unsigned int i=0; imNumBones; i++) { + aiBone *aibone = aimesh->mBones[i]; + + // for every vertex affected by this bone: + for(unsigned int j=0; jmNumWeights; j++) { + aiVertexWeight *aiweight = aibone->mWeights + j; + int vidx = aiweight->mVertexId; + int vert_boneidx = vertex_bone_count[vidx]; + if(vert_boneidx >= 4) { + error_log("WARNING vertex with more than 4 bones found\n"); + continue; + } + + weights[vidx * 4 + vert_boneidx] = aiweight->mWeight; + boneidx[vidx * 4 + vert_boneidx] = (float)i; + vertex_bone_count[vidx]++; + } + } + + delete [] vertex_bone_count; + + // normalize weights + for(int i=0; i 1e-4) { + error_log("WARNING vertex with weights < 1 (%f), normalizing...\n", wsum); + + if(wsum < 1e-6) { + // this is clearly broken, let's use the first bone in full + weights[i * 4] = 1.0; + } else { + weights[i * 4] /= wsum; + weights[i * 4 + 1] /= wsum; + weights[i * 4 + 2] /= wsum; + weights[i * 4 + 3] /= wsum; + } + } + } + } + + unsigned int *iptr = mesh->set_index_data(num_faces * 3); + for(int i=0; imFaces[i].mIndices[j]; + } + } + + return mesh; +} + +static Curve *load_curve(const aiScene *aiscn, const aiMesh *aimesh) +{ + Curve *curve = new Curve; + + for(unsigned int i=0; imNumVertices; i++) { + Vector3 pt = assimp_vector(aimesh->mVertices[i]); + curve->add_point(pt); + } + info_log("loaded curve with %d points\n", aimesh->mNumVertices); + + return curve; +} + +static bool load_bones(Mesh *mesh, const aiMesh *aimesh) +{ + if(!aimesh->mNumBones) { + return false; + } + + for(unsigned int i=0; imNumBones; i++) { + aiBone *aibone = aimesh->mBones[i]; + Object *obj = obj_by_name[aibone->mName.data]; + if(!obj) { + error_log("bone %s not found\n", aibone->mName.data); + continue; + } + + obj->set_bone_matrix(assimp_matrix(aibone->mOffsetMatrix)); + mesh->add_bone(obj); + + info_log("adding bone: %s\n", obj->get_name()); + } + + return true; +} + +static Vector3 assimp_vector(const aiVector3D &v) +{ + return Vector3(v[0], v[1], v[2]); +} + +static Quaternion assimp_quat(const aiQuaternion &q) +{ + return Quaternion(q.w, Vector3(q.x, q.y, q.z)); +} + +static Matrix4x4 assimp_matrix(const aiMatrix4x4 &aim) +{ + Matrix4x4 m; + memcpy(m[0], &aim, 16 * sizeof(float)); + return m; +} + +/* convert an assimp keyframe time (ticks) into milliseconds */ +static long assimp_time(const aiAnimation *anim, double aitime) +{ + double sec; + if(anim->mTicksPerSecond < 1e-6) { + // assume time is in frames? + sec = aitime / 30.0; + } else { + sec = aitime / anim->mTicksPerSecond; + } + return (long)(sec * 1000.0); +} + +static void print_hierarchy(const aiNode *node) +{ + static int lvl; + static int lvlopen[256]; + + for(int i=0; i= lvl - 1 ? '+' : '|'); + } else { + putchar(i >= lvl - 1 ? '+' : ' '); + } + } + info_log("- \"%s\"\n", node->mName.data); + + lvlopen[lvl] = 1; + + lvl++; + for(unsigned int i=0; imNumChildren; i++) { + if(i == node->mNumChildren - 1) { + lvlopen[lvl - 1] = 0; + } + print_hierarchy(node->mChildren[i]); + } + lvl--; +} + +#else // !defined USE_ASSIMP + +bool load_ass(Scene *scn, const char *fname) +{ + error_log("load_ass: assimp support not compiled in\n"); + return false; +} + +#endif diff -r 778ed91cb7fd -r 283cdfa7dda2 src/assload.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/assload.h Sun Aug 24 09:41:24 2014 +0300 @@ -0,0 +1,9 @@ +#ifndef ASSLOAD_H_ +#define ASSLOAD_H_ + +#include +#include "scene.h" + +bool load_ass(Scene *scn, const char *fname); + +#endif // ASSLOAD_H_ diff -r 778ed91cb7fd -r 283cdfa7dda2 src/dataset.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/dataset.h Sun Aug 24 09:41:24 2014 +0300 @@ -0,0 +1,44 @@ +/** DataSet is a generic resource database with fast O(logn) lookups by name + * it can be used for texture managers, mesh managers, sound effect managers etc + * + * The constructor takes a load function and a destructor function to be called + * when a nonexistent resource is requested and needs to be loaded, and when + * the DataSet is destroyed. The destructor is optional and can be set to null + * if not needed. + * + * Requesting a resource works by simply calling get, example: + * ---------------------------------------------------------- + * \code + * Texture *load_texture(const char *fname); + * void free_texture(Texture *tex); + * + * DataSet texman(load_texture, free_texture); + * Texture *foo = texman.get("foo.png"); + * \endcode + */ +#ifndef DATASET_H_ +#define DATASET_H_ + +#include +#include + +template +class DataSet { +protected: + mutable std::map data; + + T (*load)(const char*); + void (*destroy)(T); + +public: + DataSet(T (*load_func)(const char*), void (*destr_func)(T) = 0); + ~DataSet(); + + void clear(); + + T get(const char *name) const; +}; + +#include "dataset.inl" + +#endif // DATASET_H_ diff -r 778ed91cb7fd -r 283cdfa7dda2 src/dataset.inl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/dataset.inl Sun Aug 24 09:41:24 2014 +0300 @@ -0,0 +1,55 @@ +#include +#include + +template +DataSet::DataSet(T (*load_func)(const char*), void (*destr_func)(T)) +{ + load = load_func; + destroy = destr_func; +} + +template +DataSet::~DataSet() +{ + clear(); +} + +template +void DataSet::clear() +{ + if(destroy) { + typename std::map::iterator it = data.begin(); + while(it != data.end()) { + destroy(it++->second); + } + } + data.clear(); +} + +template +T DataSet::get(const char *name) const +{ + typename std::map::const_iterator iter = data.find(name); + if(iter != data.end()) { + return iter->second; + } + + const char *fname, *slash; + if((slash = strrchr(name, '/'))) { + fname = slash + 1; + } else { + fname = name; + } + + std::string path = fname; + if(path.empty()) { + fprintf(stderr, "can't find data file: %s\n", name); + return 0; + } + + T res = load(path.c_str()); + if(res) { + data[name] = res; + } + return res; +} diff -r 778ed91cb7fd -r 283cdfa7dda2 src/game.cc --- a/src/game.cc Sat Aug 23 12:03:29 2014 +0300 +++ b/src/game.cc Sun Aug 24 09:41:24 2014 +0300 @@ -1,4 +1,5 @@ #include "game.h" +#include "gameopt.h" #include "opengl.h" #include "camera.h" #include "texture.h" @@ -18,11 +19,18 @@ static FlyCamera cam; static Texture floor_tex; static bool keystate[256]; +static float player_height = 1.68; bool game_init() { - vr_init(); - //vr_use_module_named("null"); + if(opt.vr) { + vr_init(); + if(opt.vr_module) { + vr_use_module_named(opt.vr_module); + } + + player_height = vr_get_optf(VR_OPT_EYE_HEIGHT); + } glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); @@ -37,14 +45,16 @@ return false; } - cam.input_move(0, 0, 5); + cam.input_move(0, player_height, 0); return true; } void game_cleanup() { floor_tex.destroy(); - vr_shutdown(); + if(opt.vr) { + vr_shutdown(); + } if(fbo) { glDeleteFramebuffers(1, &fbo); @@ -88,24 +98,30 @@ void game_render() { - glBindFramebuffer(GL_FRAMEBUFFER, fbo); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + if(opt.vr) { + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glViewport(0, 0, rtwidth / 2.0, rtheight); - vr_begin(VR_EYE_LEFT); - game_render_eye(-1); - vr_end(); + glViewport(0, 0, rtwidth / 2.0, rtheight); + vr_begin(VR_EYE_LEFT); + game_render_eye(-1); + vr_end(); - glViewport(rtwidth / 2, 0, rtwidth / 2.0, rtheight); - vr_begin(VR_EYE_RIGHT); - game_render_eye(1); - vr_end(); + glViewport(rtwidth / 2, 0, rtwidth / 2.0, rtheight); + vr_begin(VR_EYE_RIGHT); + game_render_eye(1); + vr_end(); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - glViewport(0, 0, fb_width, fb_height); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glViewport(0, 0, fb_width, fb_height); - vr_output_texture(rtarg->get_texture_id(), 0, 0, (float)rtwidth / (float)rtarg->get_width(), - (float)rtheight / (float)rtarg->get_height()); + vr_output_texture(rtarg->get_texture_id(), 0, 0, (float)rtwidth / (float)rtarg->get_width(), + (float)rtheight / (float)rtarg->get_height()); + } else { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + game_render_eye(0); + } vr_swap_buffers(); } @@ -218,14 +234,11 @@ static void draw_scene() { - glMatrixMode(GL_MODELVIEW); - glTranslatef(0, -1.5, 0); - float lpos[] = {-20, 30, 10, 1}; glLightfv(GL_LIGHT0, GL_POSITION, lpos); glEnable(GL_TEXTURE_2D); - floor_tex.bind(); + bind_texture(&floor_tex); glMatrixMode(GL_TEXTURE); glScalef(8, 8, 8); @@ -251,19 +264,19 @@ glNormal3f(0, 1, 1); glVertex3f(-1, 0, 1); glVertex3f(1, 0, 1); - glVertex3f(0, 1.2, 0); + glVertex3f(0, 1.75, 0); glNormal3f(1, 1, 0); glVertex3f(1, 0, 1); glVertex3f(1, 0, -1); - glVertex3f(0, 1.2, 0); + glVertex3f(0, 1.75, 0); glNormal3f(0, 1, -1); glVertex3f(1, 0, -1); glVertex3f(-1, 0, -1); - glVertex3f(0, 1.2, 0); + glVertex3f(0, 1.75, 0); glNormal3f(-1, 1, 0); glVertex3f(-1, 0, -1); glVertex3f(-1, 0, 1); - glVertex3f(0, 1.2, 0); + glVertex3f(0, 1.75, 0); glEnd(); glPopMatrix(); diff -r 778ed91cb7fd -r 283cdfa7dda2 src/gameopt.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gameopt.cc Sun Aug 24 09:41:24 2014 +0300 @@ -0,0 +1,39 @@ +#include +#include +#include "gameopt.h" + +GameOption opt; + +bool parse_args(int argc, char **argv) +{ + int i; + + for(i=1; i +#include +#include "geom.h" +#include "logger.h" + +GeomObject::~GeomObject() +{ +} + + +Sphere::Sphere() +{ + radius = 1.0; +} + +Sphere::Sphere(const Vector3 ¢, float radius) + : center(cent) +{ + this->radius = radius; +} + +void Sphere::set_union(const GeomObject *obj1, const GeomObject *obj2) +{ + const Sphere *sph1 = dynamic_cast(obj1); + const Sphere *sph2 = dynamic_cast(obj2); + + if(!sph1 || !sph2) { + error_log("Sphere::set_union: arguments must be spheres"); + return; + } + + float dist = (sph1->center - sph2->center).length(); + float surf_dist = dist - (sph1->radius + sph2->radius); + float d1 = sph1->radius + surf_dist / 2.0; + float d2 = sph2->radius + surf_dist / 2.0; + float t = d1 / (d1 + d2); + + if(t < 0.0) t = 0.0; + if(t > 1.0) t = 1.0; + + center = sph1->center * t + sph2->center * (1.0 - t); + radius = std::max(dist * t + sph2->radius, dist * (1.0f - t) + sph1->radius); +} + +void Sphere::set_intersection(const GeomObject *obj1, const GeomObject *obj2) +{ + error_log("Sphere::intersection undefined\n"); +} + +bool Sphere::contains(const Vector3 &pt) const +{ + float dist_sq = (pt - center).length_sq(); + return dist_sq <= radius * radius; +} + +bool Sphere::intersect(const Ray &ray, HitPoint *hit) const +{ + float a = dot_product(ray.dir, ray.dir); + float b = 2.0 * ray.dir.x * (ray.origin.x - center.x) + + 2.0 * ray.dir.y * (ray.origin.y - center.y) + + 2.0 * ray.dir.z * (ray.origin.z - center.z); + float c = dot_product(ray.origin, ray.origin) + dot_product(center, center) - + 2.0 * dot_product(ray.origin, center) - radius * radius; + + float discr = b * b - 4.0 * a * c; + if(discr < 1e-4) { + return false; + } + + float sqrt_discr = sqrt(discr); + float t0 = (-b + sqrt_discr) / (2.0 * a); + float t1 = (-b - sqrt_discr) / (2.0 * a); + + if(t0 < 1e-4) + t0 = t1; + if(t1 < 1e-4) + t1 = t0; + + float t = t0 < t1 ? t0 : t1; + if(t < 1e-4) { + return false; + } + + // fill the HitPoint structure + if(hit) { + hit->obj = this; + hit->dist = t; + hit->pos = ray.origin + ray.dir * t; + hit->normal = (hit->pos - center) / radius; + } + return true; +} + + +AABox::AABox() +{ +} + +AABox::AABox(const Vector3 &vmin, const Vector3 &vmax) + : min(vmin), max(vmax) +{ +} + +void AABox::set_union(const GeomObject *obj1, const GeomObject *obj2) +{ + const AABox *box1 = dynamic_cast(obj1); + const AABox *box2 = dynamic_cast(obj2); + + if(!box1 || !box2) { + error_log("AABox::set_union: arguments must be AABoxes too\n"); + return; + } + + min.x = std::min(box1->min.x, box2->min.x); + min.y = std::min(box1->min.y, box2->min.y); + min.z = std::min(box1->min.z, box2->min.z); + + max.x = std::max(box1->max.x, box2->max.x); + max.y = std::max(box1->max.y, box2->max.y); + max.z = std::max(box1->max.z, box2->max.z); +} + +void AABox::set_intersection(const GeomObject *obj1, const GeomObject *obj2) +{ + const AABox *box1 = dynamic_cast(obj1); + const AABox *box2 = dynamic_cast(obj2); + + if(!box1 || !box2) { + error_log("AABox::set_intersection: arguments must be AABoxes too\n"); + return; + } + + for(int i=0; i<3; i++) { + min[i] = std::max(box1->min[i], box2->min[i]); + max[i] = std::min(box1->max[i], box2->max[i]); + + if(max[i] < min[i]) { + max[i] = min[i]; + } + } +} + +bool AABox::contains(const Vector3 &pt) const +{ + return pt.x >= min.x && pt.x <= max.x && + pt.y >= min.y && pt.y <= max.y && + pt.z >= min.z && pt.z <= max.z; +} + +bool AABox::intersect(const Ray &ray, HitPoint *hit) const +{ + Vector3 param[2] = {min, max}; + Vector3 inv_dir(1.0 / ray.dir.x, 1.0 / ray.dir.y, 1.0 / ray.dir.z); + int sign[3] = {inv_dir.x < 0, inv_dir.y < 0, inv_dir.z < 0}; + + float tmin = (param[sign[0]].x - ray.origin.x) * inv_dir.x; + float tmax = (param[1 - sign[0]].x - ray.origin.x) * inv_dir.x; + float tymin = (param[sign[1]].y - ray.origin.y) * inv_dir.y; + float tymax = (param[1 - sign[1]].y - ray.origin.y) * inv_dir.y; + + if(tmin > tymax || tymin > tmax) { + return false; + } + if(tymin > tmin) { + tmin = tymin; + } + if(tymax < tmax) { + tmax = tymax; + } + + float tzmin = (param[sign[2]].z - ray.origin.z) * inv_dir.z; + float tzmax = (param[1 - sign[2]].z - ray.origin.z) * inv_dir.z; + + if(tmin > tzmax || tzmin > tmax) { + return false; + } + if(tzmin > tmin) { + tmin = tzmin; + } + if(tzmax < tmax) { + tmax = tzmax; + } + + float t = tmin < 1e-4 ? tmax : tmin; + if(t >= 1e-4) { + + if(hit) { + hit->obj = this; + hit->dist = t; + hit->pos = ray.origin + ray.dir * t; + + float min_dist = FLT_MAX; + Vector3 offs = min + (max - min) / 2.0; + Vector3 local_hit = hit->pos - offs; + + static const Vector3 axis[] = { + Vector3(1, 0, 0), Vector3(0, 1, 0), Vector3(0, 0, 1) + }; + //int tcidx[][2] = {{2, 1}, {0, 2}, {0, 1}}; + + for(int i=0; i<3; i++) { + float dist = fabs((max[i] - offs[i]) - fabs(local_hit[i])); + if(dist < min_dist) { + min_dist = dist; + hit->normal = axis[i] * (local_hit[i] < 0.0 ? 1.0 : -1.0); + //hit->texcoord = Vector2(hit->pos[tcidx[i][0]], hit->pos[tcidx[i][1]]); + } + } + } + return true; + } + return false; + +} + +Plane::Plane() + : normal(0.0, 1.0, 0.0) +{ +} + +Plane::Plane(const Vector3 &p, const Vector3 &norm) + : pt(p) +{ + normal = norm.normalized(); +} + +Plane::Plane(const Vector3 &p1, const Vector3 &p2, const Vector3 &p3) + : pt(p1) +{ + normal = cross_product(p2 - p1, p3 - p1).normalized(); +} + +Plane::Plane(const Vector3 &normal, float dist) +{ + this->normal = normal.normalized(); + pt = this->normal * dist; +} + +void Plane::set_union(const GeomObject *obj1, const GeomObject *obj2) +{ + error_log("Plane::set_union undefined\n"); +} + +void Plane::set_intersection(const GeomObject *obj1, const GeomObject *obj2) +{ + error_log("Plane::set_intersection undefined\n"); +} + +bool Plane::contains(const Vector3 &pt) const +{ + return false; // TODO: maybe define containment as half-space containment? +} + +bool Plane::intersect(const Ray &ray, HitPoint *hit) const +{ + float ndotdir = dot_product(normal, ray.dir); + if(fabs(ndotdir) < 1e-4) { + return false; + } + + if(hit) { + Vector3 ptdir = pt - ray.origin; + float t = dot_product(normal, ptdir) / ndotdir; + + hit->pos = ray.origin + ray.dir * t; + hit->normal = normal; + hit->obj = this; + } + return true; +} diff -r 778ed91cb7fd -r 283cdfa7dda2 src/geom.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/geom.h Sun Aug 24 09:41:24 2014 +0300 @@ -0,0 +1,71 @@ +#ifndef GEOMOBJ_H_ +#define GEOMOBJ_H_ + +#include "vmath/vmath.h" + +class GeomObject; + +struct HitPoint { + float dist; //< parametric distance along the ray + Vector3 pos; //< position of intersection (orig + dir * dist) + Vector3 normal; //< normal at the point of intersection + const void *obj; //< pointer to the intersected object +}; + +class GeomObject { +public: + virtual ~GeomObject(); + + virtual void set_union(const GeomObject *obj1, const GeomObject *obj2) = 0; + virtual void set_intersection(const GeomObject *obj1, const GeomObject *obj2) = 0; + + virtual bool contains(const Vector3 &pt) const = 0; + virtual bool intersect(const Ray &ray, HitPoint *hit = 0) const = 0; +}; + +class Sphere : public GeomObject { +public: + Vector3 center; + float radius; + + Sphere(); + Sphere(const Vector3 ¢er, float radius); + + void set_union(const GeomObject *obj1, const GeomObject *obj2); + void set_intersection(const GeomObject *obj1, const GeomObject *obj2); + + bool contains(const Vector3 &pt) const; + bool intersect(const Ray &ray, HitPoint *hit = 0) const; +}; + +class AABox : public GeomObject { +public: + Vector3 min, max; + + AABox(); + AABox(const Vector3 &min, const Vector3 &max); + + void set_union(const GeomObject *obj1, const GeomObject *obj2); + void set_intersection(const GeomObject *obj1, const GeomObject *obj2); + + bool contains(const Vector3 &pt) const; + bool intersect(const Ray &ray, HitPoint *hit = 0) const; +}; + +class Plane : public GeomObject { +public: + Vector3 pt, normal; + + Plane(); + Plane(const Vector3 &pt, const Vector3 &normal); + Plane(const Vector3 &p1, const Vector3 &p2, const Vector3 &p3); + Plane(const Vector3 &normal, float dist); + + void set_union(const GeomObject *obj1, const GeomObject *obj2); + void set_intersection(const GeomObject *obj1, const GeomObject *obj2); + + bool contains(const Vector3 &pt) const; + bool intersect(const Ray &ray, HitPoint *hit = 0) const; +}; + +#endif // GEOMOBJ_H_ diff -r 778ed91cb7fd -r 283cdfa7dda2 src/light.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/light.cc Sun Aug 24 09:41:24 2014 +0300 @@ -0,0 +1,36 @@ +#include "light.h" + +Light::Light() +{ + cast_shadows = false; +} + +void Light::set_color(const Vector3 &color) +{ + this->color = color; +} + +const Vector3 &Light::get_color() const +{ + return color; +} + +void Light::set_attenuation(const Vector3 &att) +{ + attenuation = att; +} + +const Vector3 &Light::get_attenuation() const +{ + return attenuation; +} + +void Light::set_shadow_caster(bool s) +{ + cast_shadows = s; +} + +bool Light::is_shadow_caster() const +{ + return cast_shadows; +} diff -r 778ed91cb7fd -r 283cdfa7dda2 src/light.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/light.h Sun Aug 24 09:41:24 2014 +0300 @@ -0,0 +1,27 @@ +#ifndef GOATGFX_LIGHT_H_ +#define GOATGFX_LIGHT_H_ + +#include "xform_node.h" + + +class Light : public XFormNode { +private: + Vector3 color; + Vector3 attenuation; + + bool cast_shadows; + +public: + Light(); + + void set_color(const Vector3 &color); + const Vector3 &get_color() const; + + void set_attenuation(const Vector3 &att); + const Vector3 &get_attenuation() const; + + void set_shadow_caster(bool s); + bool is_shadow_caster() const; +}; + +#endif // GOATGFX_LIGHT_H_ diff -r 778ed91cb7fd -r 283cdfa7dda2 src/logger.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/logger.cc Sun Aug 24 09:41:24 2014 +0300 @@ -0,0 +1,118 @@ +#include +#include +#include "logger.h" + +#if defined(unix) || defined(__unix__) || defined(__APPLE__) +#include +#elif defined(WIN32) +#include +#endif + +enum { LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_FATAL, LOG_DEBUG }; + +static int typecolor(int type); + +static FILE *fp = stdout; + +static void logmsg(int type, const char *fmt, va_list ap) +{ +#if defined(unix) || defined(__unix__) || (defined(__APPLE__) && !defined(TARGET_IPHONE)) + if(isatty(fileno(fp)) && type != LOG_INFO) { + int c = typecolor(type); + fprintf(fp, "\033[%dm", c); + vfprintf(fp, fmt, ap); + fprintf(fp, "\033[0m"); + } else +#endif + { + vfprintf(fp, fmt, ap); + } + if(type == LOG_ERROR || type == LOG_FATAL || type == LOG_DEBUG) { + fflush(fp); + } + +#ifdef WIN32 + if(type == LOG_FATAL) { + static char msgbuf[1024]; + vsnprintf(msgbuf, sizeof msgbuf - 1, fmt, ap); + msgbuf[sizeof msgbuf - 1] = 0; + MessageBox(0, msgbuf, "Fatal error", MB_OK | MB_ICONSTOP); + } +#endif +} + +void info_log(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + logmsg(LOG_INFO, fmt, ap); + va_end(ap); +} + +void warning_log(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + logmsg(LOG_WARNING, fmt, ap); + va_end(ap); +} + +void error_log(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + logmsg(LOG_ERROR, fmt, ap); + va_end(ap); +} + +void fatal_log(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + logmsg(LOG_FATAL, fmt, ap); + va_end(ap); +} + +void debug_log(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + logmsg(LOG_DEBUG, fmt, ap); + va_end(ap); +} + +enum { + BLACK = 0, + RED, + GREEN, + YELLOW, + BLUE, + MAGENTA, + CYAN, + WHITE +}; + +#define ANSI_FGCOLOR(x) (30 + (x)) +#define ANSI_BGCOLOR(x) (40 + (x)) + +static int typecolor(int type) +{ + switch(type) { + case LOG_ERROR: + return ANSI_FGCOLOR(RED); + case LOG_FATAL: + return ANSI_FGCOLOR(RED); // TODO differentiate from LOG_ERROR + case LOG_WARNING: + return ANSI_FGCOLOR(YELLOW); + case LOG_DEBUG: + return ANSI_FGCOLOR(MAGENTA); + default: + break; + } + return 37; +} diff -r 778ed91cb7fd -r 283cdfa7dda2 src/logger.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/logger.h Sun Aug 24 09:41:24 2014 +0300 @@ -0,0 +1,10 @@ +#ifndef LOGGER_H_ +#define LOGGER_H_ + +void info_log(const char *fmt, ...); +void warning_log(const char *fmt, ...); +void error_log(const char *fmt, ...); +void fatal_log(const char *fmt, ...); +void debug_log(const char *fmt, ...); + +#endif // LOGGER_H_ diff -r 778ed91cb7fd -r 283cdfa7dda2 src/main.cc --- a/src/main.cc Sat Aug 23 12:03:29 2014 +0300 +++ b/src/main.cc Sun Aug 24 09:41:24 2014 +0300 @@ -2,6 +2,7 @@ #include #include "opengl.h" #include "game.h" +#include "gameopt.h" #include "vr/vr.h" static bool init(); @@ -23,8 +24,13 @@ { glutInitWindowSize(1024, 600); glutInit(&argc, argv); - glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE); - glutCreateWindow("VR Chess"); + + if(!parse_args(argc, argv)) { + return 1; + } + + glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE | (opt.stereo ? GLUT_STEREO : 0)); + glutCreateWindow("LD48 #30 - connected worlds"); glutDisplayFunc(display); glutIdleFunc(idle); @@ -53,10 +59,12 @@ return false; } - int win_xsz = vr_get_opti(VR_OPT_DISPLAY_WIDTH); - int win_ysz = vr_get_opti(VR_OPT_DISPLAY_HEIGHT); - if(win_xsz && win_ysz) { - glutReshapeWindow(win_xsz, win_ysz); + if(opt.vr) { + int win_xsz = vr_get_opti(VR_OPT_DISPLAY_WIDTH); + int win_ysz = vr_get_opti(VR_OPT_DISPLAY_HEIGHT); + if(win_xsz && win_ysz) { + glutReshapeWindow(win_xsz, win_ysz); + } } return true; } @@ -110,8 +118,8 @@ if(xoffs || yoffs) { printf("repositioning: %d,%d\n", xoffs, yoffs); glutPositionWindow(xoffs, yoffs); - fullscreen_pending = true; } + fullscreen_pending = true; } else { fullscreen_pending = false; glutPositionWindow(prev_xpos, prev_ypos); diff -r 778ed91cb7fd -r 283cdfa7dda2 src/material.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/material.cc Sun Aug 24 09:41:24 2014 +0300 @@ -0,0 +1,75 @@ +#include "opengl.h" +#include "material.h" +#include "unistate.h" + +Material::Material() + : diffuse(1, 1, 1), specular(0, 0, 0) +{ + alpha = 1.0; + shininess = 1.0; + + for(int i=0; i +#include +#include +#include +#include "opengl.h" +#include "mesh.h" +#include "xform_node.h" +#include "shader.h" +#include "logger.h" + +int Mesh::global_sdr_loc[NUM_MESH_ATTR] = { 0, 1, 2, 3, 4, 5 }; +unsigned int Mesh::intersect_mode = ISECT_DEFAULT; +float Mesh::vertex_sel_dist = 0.01; +float Mesh::vis_vecsize = 1.0; + +Mesh::Mesh() +{ + clear(); + + glGenBuffers(NUM_MESH_ATTR + 1, buffer_objects); + + for(int i=0; iname = name; +} + +const char *Mesh::get_name() const +{ + return name.c_str(); +} + +bool Mesh::has_attrib(int attr) const +{ + if(attr < 0 || attr >= NUM_MESH_ATTR) { + return false; + } + + // if neither of these is valid, then nobody has set this attribute + return vattr[attr].vbo_valid || vattr[attr].data_valid; +} + +void Mesh::clear() +{ + bones.clear(); + + for(int i=0; i= NUM_MESH_ATTR) { + error_log("%s: invalid attrib: %d\n", __FUNCTION__, attrib); + return 0; + } + + if(nverts && num != nverts) { + error_log("%s: attribute count missmatch (%d instead of %d)\n", __FUNCTION__, num, nverts); + return 0; + } + nverts = num; + + vattr[attrib].data.clear(); + vattr[attrib].nelem = nelem; + vattr[attrib].data.resize(num * nelem); + + if(data) { + memcpy(&vattr[attrib].data[0], data, num * nelem * sizeof *data); + } + + vattr[attrib].data_valid = true; + vattr[attrib].vbo_valid = false; + return &vattr[attrib].data[0]; +} + +float *Mesh::get_attrib_data(int attrib) +{ + if(attrib < 0 || attrib >= NUM_MESH_ATTR) { + error_log("%s: invalid attrib: %d\n", __FUNCTION__, attrib); + return 0; + } + + vattr[attrib].vbo_valid = false; + return (float*)((const Mesh*)this)->get_attrib_data(attrib); +} + +const float *Mesh::get_attrib_data(int attrib) const +{ + if(attrib < 0 || attrib >= NUM_MESH_ATTR) { + error_log("%s: invalid attrib: %d\n", __FUNCTION__, attrib); + return 0; + } + + if(!vattr[attrib].data_valid) { +#if GL_ES_VERSION_2_0 + error_log("%s: can't read back attrib data on CrippledGL ES\n", __FUNCTION__); + return 0; +#else + if(!vattr[attrib].vbo_valid) { + error_log("%s: unavailable attrib: %d\n", __FUNCTION__, attrib); + return 0; + } + + // local data copy is unavailable, grab the data from the vbo + Mesh *m = (Mesh*)this; + m->vattr[attrib].data.resize(nverts * vattr[attrib].nelem); + + glBindBuffer(GL_ARRAY_BUFFER, vattr[attrib].vbo); + void *data = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY); + memcpy(&m->vattr[attrib].data[0], data, nverts * vattr[attrib].nelem * sizeof(float)); + glUnmapBuffer(GL_ARRAY_BUFFER); + + vattr[attrib].data_valid = true; +#endif + } + + return &vattr[attrib].data[0]; +} + +void Mesh::set_attrib(int attrib, int idx, const Vector4 &v) +{ + float *data = get_attrib_data(attrib); + if(data) { + data += idx * vattr[attrib].nelem; + for(int i=0; iget_index_data(); +} + +const unsigned int *Mesh::get_index_data() const +{ + if(!idata_valid) { +#if GL_ES_VERSION_2_0 + error_log("%s: can't read back index data in CrippledGL ES\n", __FUNCTION__); + return 0; +#else + if(!ibo_valid) { + error_log("%s: indices unavailable\n", __FUNCTION__); + return 0; + } + + // local data copy is unavailable, gram the data from the ibo + Mesh *m = (Mesh*)this; + int nidx = nfaces * 3; + m->idata.resize(nidx); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); + void *data = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_READ_ONLY); + memcpy(&m->idata[0], data, nidx * sizeof(unsigned int)); + glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); + + idata_valid = true; +#endif + } + + return &idata[0]; +} + +void Mesh::append(const Mesh &mesh) +{ + unsigned int idxoffs = nverts; + + nverts += mesh.nverts; + nfaces += mesh.nfaces; + + for(int i=0; i= NUM_MESH_ATTR) { + return; + } + Mesh::global_sdr_loc[attr] = loc; +} + +/// static function +int Mesh::get_attrib_location(int attr) +{ + if(attr < 0 || attr >= NUM_MESH_ATTR) { + return -1; + } + return Mesh::global_sdr_loc[attr]; +} + +/// static function +void Mesh::clear_attrib_locations() +{ + for(int i=0; i= (int)bones.size()) { + return 0; + } + return bones[idx]; +} + +int Mesh::get_bones_count() const +{ + return (int)bones.size(); +} + +void Mesh::draw() const +{ + const ShaderProg *cur_sdr = get_current_shader(); +#ifdef GL_ES_VERSION_2_0 + if(!cur_sdr) { + error_log("%s: CrippledGL ES can't draw without a shader\n", __FUNCTION__); + return; + } +#endif + + ((Mesh*)this)->update_buffers(); + + if(!vattr[MESH_ATTR_VERTEX].vbo_valid) { + error_log("%s: invalid vertex buffer\n", __FUNCTION__); + return; + } + + if(cur_sdr) { + // rendering with shaders + if(global_sdr_loc[MESH_ATTR_VERTEX] == -1) { + error_log("%s: shader attribute location for vertices unset\n", __FUNCTION__); + return; + } + + for(int i=0; i= 0 && vattr[i].vbo_valid) { + glBindBuffer(GL_ARRAY_BUFFER, vattr[i].vbo); + glVertexAttribPointer(loc, vattr[i].nelem, GL_FLOAT, GL_FALSE, 0, 0); + glEnableVertexAttribArray(loc); + } + } + } else { +#ifndef GL_ES_VERSION_2_0 + // rendering with fixed-function (not available in GLES2) + glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_VERTEX].vbo); + glVertexPointer(vattr[MESH_ATTR_VERTEX].nelem, GL_FLOAT, 0, 0); + glEnableClientState(GL_VERTEX_ARRAY); + + if(vattr[MESH_ATTR_NORMAL].vbo_valid) { + glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_NORMAL].vbo); + glNormalPointer(GL_FLOAT, 0, 0); + glEnableClientState(GL_NORMAL_ARRAY); + } + if(vattr[MESH_ATTR_TEXCOORD].vbo_valid) { + glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_TEXCOORD].vbo); + glTexCoordPointer(vattr[MESH_ATTR_TEXCOORD].nelem, GL_FLOAT, 0, 0); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + } + if(vattr[MESH_ATTR_COLOR].vbo_valid) { + glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_COLOR].vbo); + glColorPointer(vattr[MESH_ATTR_COLOR].nelem, GL_FLOAT, 0, 0); + glEnableClientState(GL_COLOR_ARRAY); + } +#endif + } + glBindBuffer(GL_ARRAY_BUFFER, 0); + + if(ibo_valid) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); + glDrawElements(GL_TRIANGLES, nfaces * 3, GL_UNSIGNED_INT, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } else { + glDrawArrays(GL_TRIANGLES, 0, nverts); + } + + if(cur_sdr) { + // rendered with shaders + for(int i=0; i= 0 && vattr[i].vbo_valid) { + glDisableVertexAttribArray(loc); + } + } + } else { +#ifndef GL_ES_VERSION_2_0 + // rendered with fixed-function + glDisableClientState(GL_VERTEX_ARRAY); + if(vattr[MESH_ATTR_NORMAL].vbo_valid) { + glDisableClientState(GL_NORMAL_ARRAY); + } + if(vattr[MESH_ATTR_TEXCOORD].vbo_valid) { + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + if(vattr[MESH_ATTR_COLOR].vbo_valid) { + glDisableClientState(GL_COLOR_ARRAY); + } +#endif + } +} + +void Mesh::draw_wire() const +{ + ((Mesh*)this)->update_wire_ibo(); + + if(!vattr[MESH_ATTR_VERTEX].vbo_valid || !wire_ibo_valid) { + error_log("%s: invalid vertex buffer\n", __FUNCTION__); + return; + } + if(global_sdr_loc[MESH_ATTR_VERTEX] == -1) { + error_log("%s: shader attribute location for vertices unset\n", __FUNCTION__); + return; + } + + for(int i=0; i= 0 && vattr[i].vbo_valid) { + glBindBuffer(GL_ARRAY_BUFFER, vattr[i].vbo); + glVertexAttribPointer(loc, vattr[i].nelem, GL_FLOAT, GL_FALSE, 0, 0); + glEnableVertexAttribArray(loc); + } + } + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, wire_ibo); + glDrawElements(GL_LINES, nfaces * 6, GL_UNSIGNED_INT, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + for(int i=0; i= 0 && vattr[i].vbo_valid) { + glDisableVertexAttribArray(loc); + } + } +} + +void Mesh::draw_vertices() const +{ + ((Mesh*)this)->update_buffers(); + + if(!vattr[MESH_ATTR_VERTEX].vbo_valid) { + error_log("%s: invalid vertex buffer\n", __FUNCTION__); + return; + } + if(global_sdr_loc[MESH_ATTR_VERTEX] == -1) { + error_log("%s: shader attribute location for vertices unset\n", __FUNCTION__); + return; + } + + for(int i=0; i= 0 && vattr[i].vbo_valid) { + glBindBuffer(GL_ARRAY_BUFFER, vattr[i].vbo); + glVertexAttribPointer(loc, vattr[i].nelem, GL_FLOAT, GL_FALSE, 0, 0); + glEnableVertexAttribArray(loc); + } + } + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glDrawArrays(GL_POINTS, 0, nverts); + + for(int i=0; i= 0 && vattr[i].vbo_valid) { + glDisableVertexAttribArray(loc); + } + } +} + +void Mesh::draw_normals() const +{ +#ifdef USE_OLDGL + int vert_loc = global_sdr_loc[MESH_ATTR_VERTEX]; + Vector3 *varr = (Vector3*)get_attrib_data(MESH_ATTR_VERTEX); + Vector3 *norm = (Vector3*)get_attrib_data(MESH_ATTR_NORMAL); + + if(!varr || !norm || vert_loc < 0) { + return; + } + + glBegin(GL_LINES); + for(size_t i=0; icalc_aabb(); + } + *vmin = aabb.min; + *vmax = aabb.max; +} + +const AABox &Mesh::get_aabbox() const +{ + if(!aabb_valid) { + ((Mesh*)this)->calc_aabb(); + } + return aabb; +} + +float Mesh::get_bsphere(Vector3 *center, float *rad) const +{ + if(!bsph_valid) { + ((Mesh*)this)->calc_bsph(); + } + *center = bsph.center; + *rad = bsph.radius; + return bsph.radius; +} + +const Sphere &Mesh::get_bsphere() const +{ + if(!bsph_valid) { + ((Mesh*)this)->calc_bsph(); + } + return bsph; +} + +/// static function +void Mesh::set_intersect_mode(unsigned int mode) +{ + Mesh::intersect_mode = mode; +} + +/// static function +unsigned int Mesh::get_intersect_mode() +{ + return Mesh::intersect_mode; +} + +/// static function +void Mesh::set_vertex_select_distance(float dist) +{ + Mesh::vertex_sel_dist = dist; +} + +/// static function +float Mesh::get_vertex_select_distance() +{ + return Mesh::vertex_sel_dist; +} + +bool Mesh::intersect(const Ray &ray, HitPoint *hit) const +{ + assert((Mesh::intersect_mode & (ISECT_VERTICES | ISECT_FACE)) != (ISECT_VERTICES | ISECT_FACE)); + + const Vector3 *varr = (Vector3*)get_attrib_data(MESH_ATTR_VERTEX); + const Vector3 *narr = (Vector3*)get_attrib_data(MESH_ATTR_NORMAL); + if(!varr) { + return false; + } + const unsigned int *idxarr = get_index_data(); + + // first test with the bounding box + AABox box; + get_aabbox(&box.min, &box.max); + if(!box.intersect(ray)) { + return false; + } + + HitPoint nearest_hit; + nearest_hit.dist = FLT_MAX; + nearest_hit.obj = 0; + + if(Mesh::intersect_mode & ISECT_VERTICES) { + // we asked for "intersections" with the vertices of the mesh + long nearest_vidx = -1; + float thres_sq = Mesh::vertex_sel_dist * Mesh::vertex_sel_dist; + + for(unsigned int i=0; i 0) { + continue; + } + + // project the vertex onto the ray line + float t = dot_product(varr[i] - ray.origin, ray.dir); + Vector3 vproj = ray.origin + ray.dir * t; + + float dist_sq = (vproj - varr[i]).length_sq(); + if(dist_sq < thres_sq) { + if(!hit) { + return true; + } + if(t < nearest_hit.dist) { + nearest_hit.dist = t; + nearest_vidx = i; + } + } + } + + if(nearest_vidx != -1) { + hitvert = varr[nearest_vidx]; + nearest_hit.obj = &hitvert; + } + + } else { + // regular intersection test with polygons + + for(unsigned int i=0; i 0) { + continue; + } + + HitPoint fhit; + if(face.intersect(ray, hit ? &fhit : 0)) { + if(!hit) { + return true; + } + if(fhit.dist < nearest_hit.dist) { + nearest_hit = fhit; + hitface = face; + } + } + } + } + + if(nearest_hit.obj) { + if(hit) { + *hit = nearest_hit; + + // if we are interested in the mesh and not the faces set obj to this + if(Mesh::intersect_mode & ISECT_FACE) { + hit->obj = &hitface; + } else if(Mesh::intersect_mode & ISECT_VERTICES) { + hit->obj = &hitvert; + } else { + hit->obj = this; + } + } + return true; + } + return false; +} + + +// ------ private member functions ------ + +void Mesh::calc_aabb() +{ + // the cast is to force calling the const version which doesn't invalidate + if(!((const Mesh*)this)->get_attrib_data(MESH_ATTR_VERTEX)) { + return; + } + + aabb.min = Vector3(FLT_MAX, FLT_MAX, FLT_MAX); + aabb.max = -aabb.min; + + for(unsigned int i=0; i aabb.max[j]) { + aabb.max[j] = v[j]; + } + } + } + aabb_valid = true; +} + +void Mesh::calc_bsph() +{ + // the cast is to force calling the const version which doesn't invalidate + if(!((const Mesh*)this)->get_attrib_data(MESH_ATTR_VERTEX)) { + return; + } + + Vector3 v; + bsph.center = Vector3(0, 0, 0); + + // first find the center + for(unsigned int i=0; i bsph.radius) { + bsph.radius = dist_sq; + } + } + bsph.radius = sqrt(bsph.radius); + + bsph_valid = true; +} + +void Mesh::update_buffers() +{ + for(int i=0; iget_index_data(); + + for(unsigned int i=0; icalc_normal(); + } + return normal; +} + +void Triangle::transform(const Matrix4x4 &xform) +{ + v[0].transform(xform); + v[1].transform(xform); + v[2].transform(xform); + normal_valid = false; +} + +void Triangle::draw() const +{ + Vector3 n[3]; + n[0] = get_normal(); + n[1] = get_normal(); + n[2] = get_normal(); + + int vloc = Mesh::get_attrib_location(MESH_ATTR_VERTEX); + int nloc = Mesh::get_attrib_location(MESH_ATTR_NORMAL); + + glEnableVertexAttribArray(vloc); + glVertexAttribPointer(vloc, 3, GL_FLOAT, GL_FALSE, 0, &v[0].x); + glVertexAttribPointer(nloc, 3, GL_FLOAT, GL_FALSE, 0, &n[0].x); + + glDrawArrays(GL_TRIANGLES, 0, 3); + + glDisableVertexAttribArray(vloc); + glDisableVertexAttribArray(nloc); + CHECKGLERR; +} + +void Triangle::draw_wire() const +{ + static const int idxarr[] = {0, 1, 1, 2, 2, 0}; + int vloc = Mesh::get_attrib_location(MESH_ATTR_VERTEX); + + glEnableVertexAttribArray(vloc); + glVertexAttribPointer(vloc, 3, GL_FLOAT, GL_FALSE, 0, &v[0].x); + + glDrawElements(GL_LINES, 6, GL_UNSIGNED_INT, idxarr); + + glDisableVertexAttribArray(vloc); + CHECKGLERR; +} + +Vector3 Triangle::calc_barycentric(const Vector3 &pos) const +{ + Vector3 norm = get_normal(); + + float area_sq = fabs(dot_product(cross_product(v[1] - v[0], v[2] - v[0]), norm)); + if(area_sq < 1e-5) { + return Vector3(0, 0, 0); + } + + float asq0 = fabs(dot_product(cross_product(v[1] - pos, v[2] - pos), norm)); + float asq1 = fabs(dot_product(cross_product(v[2] - pos, v[0] - pos), norm)); + float asq2 = fabs(dot_product(cross_product(v[0] - pos, v[1] - pos), norm)); + + return Vector3(asq0 / area_sq, asq1 / area_sq, asq2 / area_sq); +} + +bool Triangle::intersect(const Ray &ray, HitPoint *hit) const +{ + Vector3 normal = get_normal(); + + float ndotdir = dot_product(ray.dir, normal); + if(fabs(ndotdir) < 1e-4) { + return false; + } + + Vector3 vertdir = v[0] - ray.origin; + float t = dot_product(normal, vertdir) / ndotdir; + + Vector3 pos = ray.origin + ray.dir * t; + Vector3 bary = calc_barycentric(pos); + + if(bary.x + bary.y + bary.z > 1.00001) { + return false; + } + + if(hit) { + hit->dist = t; + hit->pos = ray.origin + ray.dir * t; + hit->normal = normal; + hit->obj = this; + } + return true; +} diff -r 778ed91cb7fd -r 283cdfa7dda2 src/mesh.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mesh.h Sun Aug 24 09:41:24 2014 +0300 @@ -0,0 +1,210 @@ +#ifndef MESH_H_ +#define MESH_H_ + +#include +#include +#include +#include "geom.h" + +enum { + MESH_ATTR_VERTEX, + MESH_ATTR_NORMAL, + MESH_ATTR_TANGENT, + MESH_ATTR_TEXCOORD, + MESH_ATTR_COLOR, + MESH_ATTR_BONEWEIGHTS, + MESH_ATTR_BONEIDX, + + NUM_MESH_ATTR +}; + +// intersection mode flags +enum { + ISECT_DEFAULT = 0, // default (whole mesh, all intersections) + ISECT_FRONT = 1, // front-faces only + ISECT_FACE = 2, // return intersected face pointer instead of mesh + ISECT_VERTICES = 4 // return (?) TODO +}; + +class XFormNode; + + +class Triangle { +public: + Vector3 v[3]; + Vector3 normal; + bool normal_valid; + int id; + + Triangle(); + Triangle(const Vector3 &v0, const Vector3 &v1, const Vector3 &v2); + Triangle(int n, const Vector3 *varr, const unsigned int *idxarr = 0); + + /// calculate normal (quite expensive) + void calc_normal(); + const Vector3 &get_normal() const; + + void transform(const Matrix4x4 &xform); + + void draw() const; + void draw_wire() const; + + /// calculate barycentric coordinates of a point + Vector3 calc_barycentric(const Vector3 &pos) const; + + bool intersect(const Ray &ray, HitPoint *hit = 0) const; +}; + + +class Mesh { +private: + std::string name; + unsigned int nverts, nfaces; + + // current value for each attribute for the immedate mode + // interface. + Vector4 cur_val[NUM_MESH_ATTR]; + + unsigned int buffer_objects[NUM_MESH_ATTR + 1]; + + // vertex attribute data and buffer objects + struct { + int nelem; // number of elements per attribute range: [1, 4] + std::vector data; + unsigned int vbo; + mutable bool vbo_valid; // if this is false, the vbo needs updating from the data + mutable bool data_valid; // if this is false, the data needs to be pulled from the vbo + //int sdr_loc; + } vattr[NUM_MESH_ATTR]; + + static int global_sdr_loc[NUM_MESH_ATTR]; + + std::vector bones; // bones affecting this mesh + + // index data and buffer object + std::vector idata; + unsigned int ibo; + mutable bool ibo_valid; + mutable bool idata_valid; + + // index buffer object for wireframe rendering (constructed on demand) + unsigned int wire_ibo; + mutable bool wire_ibo_valid; + + // axis-aligned bounding box + mutable AABox aabb; + mutable bool aabb_valid; + + // bounding sphere + mutable Sphere bsph; + mutable bool bsph_valid; + + // keeps the last intersected face + mutable Triangle hitface; + // keeps the last intersected vertex position + mutable Vector3 hitvert; + + void calc_aabb(); + void calc_bsph(); + + static unsigned int intersect_mode; + static float vertex_sel_dist; + + static float vis_vecsize; + + /// update the VBOs after data has changed (invalid vbo/ibo) + void update_buffers(); + /// construct/update the wireframe index buffer (called from draw_wire). + void update_wire_ibo(); + + +public: + Mesh(); + ~Mesh(); + + void set_name(const char *name); + const char *get_name() const; + + bool has_attrib(int attr) const; + + // clears everything about this mesh, and returns to the newly constructed state + void clear(); + + // access the vertex attribute data + // if vdata == 0, space is just allocated + float *set_attrib_data(int attrib, int nelem, unsigned int num, const float *vdata = 0); // invalidates vbo + float *get_attrib_data(int attrib); // invalidates vbo + const float *get_attrib_data(int attrib) const; + + // simple access to any particular attribute + void set_attrib(int attrib, int idx, const Vector4 &v); // invalidates vbo + Vector4 get_attrib(int attrib, int idx) const; + + // ... same for index data + unsigned int *set_index_data(int num, const unsigned int *indices = 0); // invalidates ibo + unsigned int *get_index_data(); // invalidates ibo + const unsigned int *get_index_data() const; + + void append(const Mesh &mesh); + + // immediate-mode style mesh construction interface + void vertex(float x, float y, float z); + void normal(float nx, float ny, float nz); + void tangent(float tx, float ty, float tz); + void texcoord(float u, float v, float w); + void boneweights(float w1, float w2, float w3, float w4); + void boneidx(int idx1, int idx2, int idx3, int idx4); + + /* apply a transformation to the vertices and its inverse-transpose + * to the normals and tangents. + */ + void apply_xform(const Matrix4x4 &xform); + void apply_xform(const Matrix4x4 &xform, const Matrix4x4 &dir_xform); + + // adds a bone and returns its index + int add_bone(XFormNode *bone); + const XFormNode *get_bone(int idx) const; + int get_bones_count() const; + + // access the shader attribute locations + static void set_attrib_location(int attr, int loc); + static int get_attrib_location(int attr); + static void clear_attrib_locations(); + + static void set_vis_vecsize(float sz); + static float get_vis_vecsize(); + + void draw() const; + void draw_wire() const; + void draw_vertices() const; + void draw_normals() const; + void draw_tangents() const; + + /** get the bounding box in local space. The result will be cached, and subsequent + * calls will return the same box. The cache gets invalidated by any functions that can affect + * the vertex data (non-const variant of get_attrib_data(MESH_ATTR_VERTEX, ...) included). + * @{ */ + void get_aabbox(Vector3 *vmin, Vector3 *vmax) const; + const AABox &get_aabbox() const; + /// @} + + /** get the bounding sphere in local space. The result will be cached, and subsequent + * calls will return the same box. The cache gets invalidated by any functions that can affect + * the vertex data (non-const variant of get_attrib_data(MESH_ATTR_VERTEX, ...) included). + * @{ */ + float get_bsphere(Vector3 *center, float *rad) const; + const Sphere &get_bsphere() const; + + static void set_intersect_mode(unsigned int mode); + static unsigned int get_intersect_mode(); + static void set_vertex_select_distance(float dist); + static float get_vertex_select_distance(); + + /** Find the intersection between the mesh and a ray. + * XXX Brute force at the moment, not intended to be used for anything other than picking in tools. + * If you intend to use it in a speed-critical part of the code, you'll *have* to optimize it! + */ + bool intersect(const Ray &ray, HitPoint *hit = 0) const; +}; + +#endif // MESH_H_ diff -r 778ed91cb7fd -r 283cdfa7dda2 src/object.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/object.cc Sun Aug 24 09:41:24 2014 +0300 @@ -0,0 +1,285 @@ +#include +#include "object.h" +#include "opengl.h" +#include "unistate.h" +#include "logger.h" + + +static void destroy_all_rec(XFormNode *node); +static void get_all_meshes_rec(XFormNode *node, std::list *reslist); + +DrawMode Object::draw_mode = DRAW_DEFAULT; + +Object::Object() +{ + mesh = 0; +} + +void Object::destroy_all() +{ + destroy_all_rec(this); +} + +static void destroy_all_rec(XFormNode *node) +{ + if(!node) { + return; + } + + for(int i=0; iget_children_count(); i++) { + destroy_all_rec(node->get_child(i)); + } + delete node; +} + +void Object::set_mesh(Mesh *m) +{ + mesh = m; +} + +Mesh *Object::get_mesh() +{ + return mesh; +} + +const Mesh *Object::get_mesh() const +{ + return mesh; +} + +std::list Object::get_all_meshes() +{ + std::list meshes; + get_all_meshes_rec(this, &meshes); + return meshes; +} + +std::list Object::get_all_meshes() const +{ + std::list meshes; + get_all_meshes_rec((Object*)this, (std::list*)&meshes); + return meshes; +} + +static void get_all_meshes_rec(XFormNode *node, std::list *reslist) +{ + if(!node) { + return; + } + + Object *obj = dynamic_cast(node); + if(obj) { + Mesh *mesh = obj->get_mesh(); + if(mesh) { + reslist->push_back(mesh); + } + } + + for(int i=0; iget_children_count(); i++) { + get_all_meshes_rec(node->get_child(i), reslist); + } +} + +AABox Object::get_aabbox() const +{ + AABox box; + + if(mesh) { + box = mesh->get_aabbox(); + } else { + box.min = Vector3(FLT_MAX, FLT_MAX, FLT_MAX); + box.max = -box.min; + } + + int num_children = get_children_count(); + for(int i=0; i(get_child(i)); + if(obj) { + AABox child_box = obj->get_aabbox(); + box.set_union(&box, &child_box); + } + } + return box; +} + +Sphere Object::get_bsphere() const +{ + Sphere sph; + bool valid = false; + + if(mesh) { + sph = mesh->get_bsphere(); + valid = true; + } else { + sph.radius = 0.0; + } + + int num_children = get_children_count(); + for(int i=0; i(get_child(i)); + if(obj) { + Sphere child_sph = obj->get_bsphere(); + if(valid) { + sph.set_union(&sph, &child_sph); + } else { + sph = child_sph; + valid = true; + } + } + } + + return sph; +} + +/*static const char *attr_name[] = { + "attr_vertex", + "attr_normal", + "attr_tangent", + "attr_texcoord", + "attr_boneweights", + "attr_boneidx" +};*/ + +void Object::draw(long msec) const +{ + Matrix4x4 xform; + get_xform(msec, &xform); + + set_world_matrix(xform); + + if(mesh) { + /*unsigned int prog = sdrprog; + + if(mesh->get_bones_count() > 0) { + prog = sdrprog_skin; + } + + glUseProgram(prog); + // get all the attribute locations + for(int i=0; idraw_wire(); + break; + + case DRAW_VERTICES: + mesh->draw_vertices(); + break; + + case DRAW_DEFAULT: + default: + mesh->draw(); + } + } + + int num_children = get_children_count(); + for(int i=0; i(get_child(i)); + if(obj) { + obj->draw(msec); + } + } +} + + +bool Object::intersect(const Ray &inray, HitPoint *hit) const +{ + Ray ray = inray; + Matrix4x4 xform, inv_xform; + get_xform(ray.time, &xform/*, &inv_xform*/); + ray.transform(xform.inverse()); // TODO find out what's wrong with get_xform's inv_xform and use that + + HitPoint nearest_hit; + nearest_hit.dist = FLT_MAX; + nearest_hit.obj = 0; + + if(mesh) { + if(mesh->intersect(ray, hit ? &nearest_hit : 0)) { + if(!hit) { + return true; + } + + if(Mesh::get_intersect_mode() & ISECT_FACE) { + Triangle *face = (Triangle*)nearest_hit.obj; + face->transform(xform); + } else if(Mesh::get_intersect_mode() & ISECT_VERTICES) { + Vector3 *v = (Vector3*)nearest_hit.obj; + v->transform(xform); + } else { + nearest_hit.obj = this; + } + } + } + + int num_children = get_children_count(); + for(int i=0; i(get_child(i)); + + HitPoint chit; + if(obj && obj->intersect(inray, hit ? &chit : 0)) { + if(!hit) { + return true; + } + + if(chit.dist < nearest_hit.dist) { + nearest_hit = chit; + } + } + } + + if(nearest_hit.obj) { + if(hit) { + *hit = nearest_hit; + } + return true; + } + return false; +} + + +bool Object::setup_bones(long msec) const +{ + int num_bones; + if(!mesh || !(num_bones = mesh->get_bones_count())) { + return false; + } + + /*char uniname[32]; + + for(int i=0; iget_bone(i); + + Matrix4x4 xform; + bone->get_xform(msec, &xform); + + xform = xform * bone->get_bone_matrix(); + + sprintf(uniname, "bone_xform[%d]", i); + int loc = glGetUniformLocation(sdrprog_skin, uniname); + if(loc == -1) { + return false; + } + glUniformMatrix4fv(loc, 1, GL_TRUE, xform[0]); + }*/ + return true; +} + +DrawMode Object::set_draw_mode(DrawMode mode) +{ + DrawMode prev = Object::draw_mode; + Object::draw_mode = mode; + return prev; +} diff -r 778ed91cb7fd -r 283cdfa7dda2 src/object.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/object.h Sun Aug 24 09:41:24 2014 +0300 @@ -0,0 +1,51 @@ +#ifndef OBJECT_H_ +#define OBJECT_H_ + +#include +#include "xform_node.h" +#include "mesh.h" +#include "material.h" + +enum DrawMode { + DRAW_DEFAULT, + DRAW_WIREFRAME, + DRAW_VERTICES +}; + + +class Object : public XFormNode { +private: + Mesh *mesh; ///< no ownership, just keeping the pointer around + static DrawMode draw_mode; + + bool setup_bones(long msec) const; + +public: + Material material; + + Object(); + + /// destroy this object and all the hierarchy of objects hanging below it + void destroy_all(); + + void set_mesh(Mesh *m); + Mesh *get_mesh(); + const Mesh *get_mesh() const; + + /// get all the meshes of the subtree rooted in this object @{ + std::list get_all_meshes(); + std::list get_all_meshes() const; + /// @} + + AABox get_aabbox() const; + Sphere get_bsphere() const; + + void draw(long msec = 0) const; + + bool intersect(const Ray &ray, HitPoint *hit = 0) const; + + /// this is mostly for tools, to allow drawing only the wireframe or vertices + static DrawMode set_draw_mode(DrawMode mode); +}; + +#endif // OBJECT_H_ diff -r 778ed91cb7fd -r 283cdfa7dda2 src/scene.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/scene.cc Sun Aug 24 09:41:24 2014 +0300 @@ -0,0 +1,34 @@ +#include "scene.h" +#include "opengl.h" + +Scene::~Scene() +{ + destroy(); +} + +void Scene::destroy() +{ + for(size_t i=0; idraw(msec); + } + + // if there are no objects in the scene, just draw the meshes instead + if(objects.empty()) { + for(size_t i=0; idraw(); + } + } +} diff -r 778ed91cb7fd -r 283cdfa7dda2 src/scene.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/scene.h Sun Aug 24 09:41:24 2014 +0300 @@ -0,0 +1,25 @@ +#ifndef SCENE_H_ +#define SCENE_H_ + +#include +#include "object.h" +#include "light.h" +#include "camera.h" + +class Scene { +public: + std::vector objects; + std::vector meshes; + std::vector lights; + std::vector cameras; + + // nodes can be objects, lights, cameras, or just dummy nodes + std::vector nodes; + + ~Scene(); + void destroy(); + + void draw(long msec = 0) const; +}; + +#endif // SCENE_H_ diff -r 778ed91cb7fd -r 283cdfa7dda2 src/shader.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/shader.cc Sun Aug 24 09:41:24 2014 +0300 @@ -0,0 +1,671 @@ +#include +#include +#include +#include +#include "opengl.h" +#include "shader.h" +#include "mesh.h" +#include "logger.h" +#include "unistate.h" + +#ifdef _MSC_VER +#include +#else +#include +#endif + +#ifdef __GLEW_H__ +#define HAVE_GEOMETRY_SHADER +#define HAVE_TESSELATION_SHADER +#endif + +static std::string read_source(const char *fname); +static void bind_standard_attr(const ShaderProg *prog); + +ShaderProg *ShaderProg::current; + +void bind_shader(const ShaderProg *sdr) +{ + if(sdr) { + sdr->bind(); + } else { +#ifndef GL_ES_VERSION_2_0 + glUseProgram(0); + ShaderProg::current = 0; +#endif + } +} + +const ShaderProg *get_current_shader() +{ + return ShaderProg::current; +} + +Shader::Shader() +{ + sdr = type = 0; +} + +Shader::~Shader() +{ + destroy(); +} + +unsigned int Shader::get_id() const +{ + return sdr; +} + +void Shader::set_name(const char *name) +{ + this->name = std::string(name); +} + +const char *Shader::get_name() const +{ + return name.c_str(); +} + +bool Shader::create(const char *src, unsigned int type) +{ +#if !GL_ES_VERSION_2_0 + const char *src_arr[] = {src}; +#else + const char *src_arr[] = { "precision mediump float; ", src }; +#endif + + // keep a copy of the source code + this->src = std::string(src); + + if(!sdr) { + sdr = glCreateShader(type); + } + + info_log("compiling shader: %s... ", name.c_str()); + + glShaderSource(sdr, sizeof src_arr / sizeof *src_arr, src_arr, 0); + glCompileShader(sdr); + + int status; + glGetShaderiv(sdr, GL_COMPILE_STATUS, &status); + + info_log(status ? "success\n" : "failed\n"); + + int info_len; + glGetShaderiv(sdr, GL_INFO_LOG_LENGTH, &info_len); + if(info_len > 1) { + char *buf = (char*)alloca(info_len); + glGetShaderInfoLog(sdr, info_len, 0, buf); + buf[info_len - 1] = 0; + + if(status) { + info_log("%s\n", buf); + } else { + error_log("%s\n", buf); + } + } + + return status == GL_TRUE; +} + +void Shader::destroy() +{ + if(sdr) { + glDeleteShader(sdr); + } + sdr = type = 0; + + src.clear(); + name.clear(); +} + +static std::string read_source(const char *fname) +{ + FILE *fp; + + if(!(fp = fopen(fname, "rb"))) { + error_log("failed to load shader: %s: %s\n", fname, strerror(errno)); + return std::string(); + } + + fseek(fp, 0, SEEK_END); + long sz = ftell(fp); + rewind(fp); + + char *src = (char*)alloca(sz + 1); + if(fread(src, 1, sz, fp) < (size_t)sz) { + error_log("failed to load shader: %s: %s\n", fname, strerror(errno)); + fclose(fp); + delete [] src; + return std::string(); + } + src[sz] = 0; + fclose(fp); + + return std::string(src); +} + +bool Shader::load(const char *fname, unsigned int type) +{ + std::string src = read_source(fname); + if(src.empty()) { + return false; + } + set_name(fname); + return create(src.c_str(), type); +} + +// ---- shader program ---- +ShaderProg::ShaderProg() +{ + prog = 0; + must_link = true; +} + +ShaderProg::~ShaderProg() +{ + destroy(); +} + +unsigned int ShaderProg::get_id() const +{ + return prog; +} + +bool ShaderProg::create(const char *src, unsigned int type, ...) +{ + va_list ap; + + va_start(ap, type); + bool res = create(src, type, ap); + va_end(ap); + + return res; +} + +bool ShaderProg::create(const char *src, unsigned int type, va_list ap) +{ + destroy(); + prog = glCreateProgram(); + + while(src) { + Shader *sdr = new Shader; + if(!sdr->create(src, type)) { + va_end(ap); + return false; + } + add_shader(sdr); + src = va_arg(ap, const char*); + type = va_arg(ap, unsigned int); + } + + return link(); +} + +bool ShaderProg::create(const char *vsrc, const char *psrc) +{ + return create(VSDR(vsrc), PSDR(psrc), 0); +} + +bool ShaderProg::create(Shader *sdr, ...) +{ + va_list ap; + + va_start(ap, sdr); + bool res = create(sdr, ap); + va_end(ap); + + return res; +} + +bool ShaderProg::create(Shader *sdr, va_list ap) +{ + destroy(); + prog = glCreateProgram(); + + while(sdr) { + add_shader(sdr); + sdr = va_arg(ap, Shader*); + } + return link(); +} + +bool ShaderProg::create(Shader *vsdr, Shader *psdr) +{ + return create(vsdr, psdr, 0); +} + +void ShaderProg::destroy() +{ + if(prog) { + glDeleteProgram(prog); + } + prog = 0; + + shaders.clear(); + // don't actually destroy the shaders, let the ShaderSet own them +} + +bool ShaderProg::load(const char *fname, unsigned int type, ...) +{ + va_list ap; + va_start(ap, type); + bool res = load(fname, type, ap); + va_end(ap); + + return res; +} + +bool ShaderProg::load(const char *fname, unsigned int type, va_list ap) +{ + destroy(); + prog = glCreateProgram(); + + while(fname) { + Shader *sdr = new Shader; + if(!sdr->load(fname, type)) { + delete sdr; + return false; + } + add_shader(sdr); + + if((fname = va_arg(ap, const char*))) { + type = va_arg(ap, unsigned int); + } + } + + return link(); +} + +bool ShaderProg::load(const char *vfname, const char *pfname) +{ + return load(VSDR(vfname), PSDR(pfname), 0); +} + +void ShaderProg::add_shader(Shader *sdr) +{ + glAttachShader(prog, sdr->get_id()); +} + +bool ShaderProg::link() const +{ + bind_standard_attr(this); + + CHECKGLERR; + info_log("linking program ... "); + glLinkProgram(prog); + + int status; + glGetProgramiv(prog, GL_LINK_STATUS, &status); + + info_log(status ? "success\n" : "failed\n"); + + int info_len; + glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &info_len); + if(info_len > 1) { + char *buf = (char*)alloca(info_len); + glGetProgramInfoLog(prog, info_len, 0, buf); + buf[info_len - 1] = 0; + + if(status) { + info_log("%s\n", buf); + } else { + error_log("%s\n", buf); + } + } + + if(status) { + must_link = false; + cache_state_uniforms(); + return true; + } + return false; +} + +void ShaderProg::bind() const +{ + CHECKGLERR; + if(must_link) { + if(!link()) { + return; + } + } + CHECKGLERR; + glUseProgram(prog); + ShaderProg::current = (ShaderProg*)this; + + setup_state_uniforms(); +} + + +int ShaderProg::get_attrib_location(const char *name) const +{ + glUseProgram(prog); + return glGetAttribLocation(prog, name); +} + +void ShaderProg::set_attrib_location(const char *name, int loc) const +{ + glBindAttribLocation(prog, loc, name); + must_link = true; +} + +int ShaderProg::get_uniform_location(const char *name) const +{ + glUseProgram(prog); + return glGetUniformLocation(prog, name); +} + +bool ShaderProg::set_uniform(int loc, int val) const +{ + glUseProgram(prog); + if(loc >= 0) { + glUniform1i(loc, val); + return true; + } + return false; +} + +bool ShaderProg::set_uniform(int loc, float val) const +{ + glUseProgram(prog); + if(loc >= 0) { + glUniform1f(loc, val); + return true; + } + return false; +} + +bool ShaderProg::set_uniform(int loc, const Vector2 &v) const +{ + glUseProgram(prog); + if(loc >= 0) { + glUniform2f(loc, v.x, v.y); + return true; + } + return false; +} + +bool ShaderProg::set_uniform(int loc, const Vector3 &v) const +{ + glUseProgram(prog); + if(loc >= 0) { + glUniform3f(loc, v.x, v.y, v.z); + return true; + } + return false; +} + +bool ShaderProg::set_uniform(int loc, const Vector4 &v) const +{ + glUseProgram(prog); + if(loc >= 0) { + glUniform4f(loc, v.x, v.y, v.z, v.w); + return true; + } + return false; +} + +bool ShaderProg::set_uniform(int loc, const Matrix3x3 &m) const +{ + glUseProgram(prog); + if(loc >= 0) { + glUniformMatrix3fv(loc, 1, GL_TRUE, m[0]); + return true; + } + return false; +} + +bool ShaderProg::set_uniform(int loc, const Matrix4x4 &m) const +{ + glUseProgram(prog); + if(loc >= 0) { + glUniformMatrix4fv(loc, 1, GL_TRUE, m[0]); + return true; + } + return false; +} + + +bool ShaderProg::set_uniform(const char *name, int val) const +{ + return set_uniform(get_uniform_location(name), val); +} + +bool ShaderProg::set_uniform(const char *name, float val) const +{ + return set_uniform(get_uniform_location(name), val); +} + +bool ShaderProg::set_uniform(const char *name, const Vector2 &v) const +{ + return set_uniform(get_uniform_location(name), v); +} + +bool ShaderProg::set_uniform(const char *name, const Vector3 &v) const +{ + return set_uniform(get_uniform_location(name), v); +} + +bool ShaderProg::set_uniform(const char *name, const Vector4 &v) const +{ + return set_uniform(get_uniform_location(name), v); +} + +bool ShaderProg::set_uniform(const char *name, const Matrix3x3 &m) const +{ + return set_uniform(get_uniform_location(name), m); +} + +bool ShaderProg::set_uniform(const char *name, const Matrix4x4 &m) const +{ + return set_uniform(get_uniform_location(name), m); +} + +static StType unist_type(GLenum type) +{ + switch(type) { + case GL_FLOAT: + return ST_FLOAT; + case GL_FLOAT_VEC2: + return ST_FLOAT2; + case GL_FLOAT_VEC3: + return ST_FLOAT3; + case GL_FLOAT_VEC4: + return ST_FLOAT4; + case GL_INT: + case GL_SAMPLER_2D: + case GL_SAMPLER_CUBE: +#if !GL_ES_VERSION_2_0 + case GL_SAMPLER_1D: + case GL_SAMPLER_3D: + case GL_SAMPLER_1D_SHADOW: + case GL_SAMPLER_2D_SHADOW: +#endif + return ST_INT; + case GL_INT_VEC2: + return ST_INT2; + case GL_INT_VEC3: + return ST_INT3; + case GL_INT_VEC4: + return ST_INT4; + case GL_FLOAT_MAT3: + return ST_MATRIX3; + case GL_FLOAT_MAT4: + return ST_MATRIX4; + default: + break; + } + return ST_UNKNOWN; +} + +void ShaderProg::cache_state_uniforms() const +{ + if(!glIsProgram(prog)) { + return; + } + + int num_uni; + glGetProgramiv(prog, GL_ACTIVE_UNIFORMS, &num_uni); + + char name[256]; + for(int i=0; iload(fname, type)) { + delete sdr; + return 0; + } + return sdr; +} + +static Shader *load_vertex_shader(const char *fname) +{ + return load_shader(fname, GL_VERTEX_SHADER); +} + +static Shader *load_pixel_shader(const char *fname) +{ + return load_shader(fname, GL_FRAGMENT_SHADER); +} + +#ifdef HAVE_GEOMETRY_SHADER +static Shader *load_geom_shader(const char *fname) +{ + return load_shader(fname, GL_GEOMETRY_SHADER); +} +#endif + +#ifdef HAVE_TESSELATION_SHADER +static Shader *load_tc_shader(const char *fname) +{ + return load_shader(fname, GL_TESS_CONTROL_SHADER); +} + +static Shader *load_te_shader(const char *fname) +{ + return load_shader(fname, GL_TESS_EVALUATION_SHADER); +} +#endif + +static void destroy_shader(Shader *sdr) +{ + delete sdr; +} + +ShaderSet::ShaderSet(unsigned int type) + : DataSet(0, destroy_shader) +{ + this->type = type; + + switch(type) { + case GL_VERTEX_SHADER: + load = load_vertex_shader; + break; + + case GL_FRAGMENT_SHADER: + load = load_pixel_shader; + break; + +#ifdef HAVE_GEOMETRY_SHADER + case GL_GEOMETRY_SHADER: + load = load_geom_shader; + break; +#endif + +#ifdef HAVE_TESSELATION_SHADER + case GL_TESS_CONTROL_SHADER: + load = load_tc_shader; + break; + + case GL_TESS_EVALUATION_SHADER: + load = load_te_shader; + break; +#endif + + default: + error_log("ShaderSet constructed with invalid shader type!\n"); + } +} + +static struct { const char *name; int loc; } attr_loc[] = { + {"attr_vertex", MESH_ATTR_VERTEX}, + {"attr_normal", MESH_ATTR_NORMAL}, + {"attr_tangent", MESH_ATTR_TANGENT}, + {"attr_texcoord", MESH_ATTR_TEXCOORD}, + {"attr_color", MESH_ATTR_COLOR}, + {"attr_boneweights", MESH_ATTR_BONEWEIGHTS}, + {"attr_boneidx", MESH_ATTR_BONEIDX} +}; + +static void bind_standard_attr(const ShaderProg *prog) +{ + // we must link once to find out which are the active attributes + glLinkProgram(prog->get_id()); + + int num_attr; + glGetProgramiv(prog->get_id(), GL_ACTIVE_ATTRIBUTES, &num_attr); + + char name[256]; + for(int i=0; iget_id(), i, sizeof name - 1, 0, &sz, &type, name); + + for(int j=0; j<(int)(sizeof attr_loc / sizeof *attr_loc); j++) { + if(strcmp(name, attr_loc[j].name) == 0) { + prog->set_attrib_location(name, attr_loc[j].loc); + } + } + } +} + + +/* +static const char *strtype(unsigned int type) +{ + switch(type) { + case GL_VERTEX_SHADER: + return "vertex"; + case GL_FRAGMENT_SHADER: + return "fragment"; +#ifdef HAVE_GEOMETRY_SHADER + case GL_GEOMETRY_SHADER: + return "geometry"; +#endif +#ifdef HAVE_TESSELATION_SHADER + case GL_TESS_CONTROL_SHADER: + return "tesselation control"; + case GL_TESS_EVALUATION_SHADER: + return "tesselation evaluation"; +#endif + default: + break; + } + return ""; +} +*/ diff -r 778ed91cb7fd -r 283cdfa7dda2 src/shader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/shader.h Sun Aug 24 09:41:24 2014 +0300 @@ -0,0 +1,150 @@ +#ifndef SHADER_H_ +#define SHADER_H_ + +#include +#include +#include "vmath/vmath.h" +#include "opengl.h" +#include "dataset.h" + +class ShaderProg; + + +void bind_shader(const ShaderProg *sdr); +const ShaderProg *get_current_shader(); + + +class Shader { +private: + unsigned int sdr; + unsigned int type; + std::string name, src; + +public: + Shader(); + ~Shader(); + + unsigned int get_id() const; + + void set_name(const char *name); + const char *get_name() const; + + bool create(const char *src, unsigned int type); + void destroy(); + + bool load(const char *fname, unsigned int type); + + + // these functions are only meant to be used by the ShaderSet + static Shader *create_shader(); + static bool load_shader(Shader *sdr, const char *fname); + static bool done_shader(Shader *sdr, unsigned int type); + static void destroy_shader(Shader *sdr); +}; + +#define VSDR(s) s, GL_VERTEX_SHADER +#define FSDR(s) s, GL_FRAGMENT_SHADER +#define PSDR(s) FSDR(s) +#define GSDR(s) s, GL_GEOMETRY_SHADER +#define TCSDR(s) s, GL_TESS_CONTROL_SHADER +#define TESDR(s) s, GL_TESS_EVALUATION_SHADER + +class ShaderProg { +private: + unsigned int prog; + mutable bool must_link; + std::vector shaders; + + struct StateLocCache { int sidx, loc; }; + /** a cache of all st_ prefixed uniform locations and their corresponding + * index in the global uniform state vector (see unistate.h) + */ + mutable std::vector stloc_cache; + + void cache_state_uniforms() const; + void setup_state_uniforms() const; + +public: + static ShaderProg *current; + + ShaderProg(); + ~ShaderProg(); + + /// returns the OpenGL object id for this shader program + unsigned int get_id() const; + + /** takes a series of shaders, and constructs a program object by linking + * them together. Terminate with a null pointer (don't use 0!) */ + bool create(Shader *sdr, ...); + /// same as above, but with a va_list instead of variable arguments. + bool create(Shader *sdr, va_list ap); + /** takes two shaders (vertex and pixel) and constructs a program object by + * linking them together. Either one can be null. */ + bool create(Shader *vsdr, Shader *psdr); + + /** takes a series of shader source/shader type pairs and constructs a program + * object by linking them together. Terminate with a null pointer (don't use 0!) + * You can use the VSDR, PSDR, GSDR, TCSDR, TESDR convenience macros for passing + * the pairs. + * Example: create(VSDR(vsrc0), VSDR(vsrc1), PSDR(psrc), NULL); + */ + bool create(const char *src, unsigned int type, ...); + /// same as above, but with a va_list instead of variable arguments. + bool create(const char *src, unsigned int type, va_list ap); + /** takes two shaders source strings (vertex and pixel) and constructs + * a program object by linking them together. Either one can be null. */ + bool create(const char *vsrc, const char *psrc); + + void destroy(); + + /** takes a series of shader filename/shader type pairs, loads the shaders and + * constructs a program object by linking them together. Terminate with a null + * pointer (don't use 0!). You can use the VSDR, PSDR, GSDR, TCSDR, TESDR convenience + * macros for passing the pairs. + * Example: load(VSDR("vsdr1.glsl"), VSDR("vsdr2.glsl"), PSDR("pixel.glsl"), NULL); + */ + bool load(const char *fname, unsigned int type, ...); + /// same as above, but with a va_list instead of variable arguments. + bool load(const char *fname, unsigned int type, va_list ap); + /** takes the filenames of two shader files (vertex and pixel), loads them and + * constructs a program object by linking them together. Either one can be null */ + bool load(const char *vsrc, const char *psrc); + + void add_shader(Shader *sdr); + bool link() const; + + void bind() const; + + int get_attrib_location(const char *name) const; + void set_attrib_location(const char *name, int loc) const; + + int get_uniform_location(const char *name) const; + + bool set_uniform(int loc, int val) const; + bool set_uniform(int loc, float val) const; + bool set_uniform(int loc, const Vector2 &v) const; + bool set_uniform(int loc, const Vector3 &v) const; + bool set_uniform(int loc, const Vector4 &v) const; + bool set_uniform(int loc, const Matrix3x3 &m) const; + bool set_uniform(int loc, const Matrix4x4 &m) const; + + bool set_uniform(const char *name, int val) const; + bool set_uniform(const char *name, float val) const; + bool set_uniform(const char *name, const Vector2 &v) const; + bool set_uniform(const char *name, const Vector3 &v) const; + bool set_uniform(const char *name, const Vector4 &v) const; + bool set_uniform(const char *name, const Matrix3x3 &m) const; + bool set_uniform(const char *name, const Matrix4x4 &m) const; + + friend void setup_unistate(const ShaderProg*); +}; + +class ShaderSet : public DataSet { +private: + unsigned int type; + +public: + ShaderSet(unsigned int type); +}; + +#endif // SHADER_H_ diff -r 778ed91cb7fd -r 283cdfa7dda2 src/texture.cc --- a/src/texture.cc Sat Aug 23 12:03:29 2014 +0300 +++ b/src/texture.cc Sun Aug 24 09:41:24 2014 +0300 @@ -64,18 +64,16 @@ return img; } +unsigned int Texture::get_type() const +{ + return type; +} + unsigned int Texture::get_texture_id() const { return tex; } -void Texture::bind(int tunit) const -{ - glActiveTextureARB(GL_TEXTURE0_ARB + tunit); - glBindTexture(type, tex); - glActiveTextureARB(GL_TEXTURE0_ARB); -} - bool Texture::load(const char *fname) { Image image; @@ -86,6 +84,26 @@ return true; } +void bind_texture(const Texture *tex, int tunit) +{ + static unsigned int cur_tex_type[8] = { + GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D, + GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D + }; + + glActiveTextureARB(GL_TEXTURE0_ARB + tunit); + if(tex) { + glBindTexture(tex->get_type(), tex->get_texture_id()); + glEnable(tex->get_type()); + } else { + glDisable(cur_tex_type[tunit]); + } + glActiveTextureARB(GL_TEXTURE0_ARB); + + cur_tex_type[tunit] = tex->get_type(); +} + + unsigned int next_pow2(unsigned int x) { x -= 1; diff -r 778ed91cb7fd -r 283cdfa7dda2 src/texture.h --- a/src/texture.h Sat Aug 23 12:03:29 2014 +0300 +++ b/src/texture.h Sun Aug 24 09:41:24 2014 +0300 @@ -23,12 +23,14 @@ Image &get_image(); const Image &get_image() const; + unsigned int get_type() const; unsigned int get_texture_id() const; - void bind(int tunit = 0) const; bool load(const char *fname); }; +void bind_texture(const Texture *tex, int unit = 0); + unsigned int next_pow2(unsigned int x); #endif // TEXTURE_H_ diff -r 778ed91cb7fd -r 283cdfa7dda2 src/unistate.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/unistate.cc Sun Aug 24 09:41:24 2014 +0300 @@ -0,0 +1,678 @@ +#include +#include +#include "unistate.h" +#include "shader.h" +#include "logger.h" + +struct StateItem { + StType type; + + union { + int ival[4]; + float fval[16]; + }; + int transpose; // for matrices +}; + +static const char *typestr(StType type); +static int type_nelem(StType type); +static StType float_type(int elem); +static StType int_type(int elem); + +std::vector state; +std::map stateidx; + + +int add_unistate(const char *name, StType type) +{ + static const float ident3[] = {1, 0, 0, 0, 1, 0, 0, 0, 1}; + static const float ident4[] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}; + + if(stateidx.find(name) != stateidx.end()) { + return stateidx[name]; + } + + StateItem sitem; + memset(&sitem, 0, sizeof sitem); + sitem.type = type; + + // initialize to a reasonable default value + switch(type) { + case ST_MATRIX3: + memcpy(sitem.fval, ident3, sizeof ident3); + break; + + case ST_MATRIX4: + memcpy(sitem.fval, ident4, sizeof ident4); + break; + + default: + break; // in all other cases leave it zero (see memset above) + } + + int sidx = state.size(); + state.push_back(sitem); + stateidx[name] = sidx; + + debug_log("adding uniform state [%d]: %s %s\n", sidx, typestr(sitem.type), name); + + return sidx; +} + +int get_unistate_index(const char *name) +{ + std::map::const_iterator it = stateidx.find(name); + if(it != stateidx.end()) { + return it->second; + } + return -1; +} + +#define CHECK_INDEX(i) \ + if(i < 0 || i >= (int)state.size()) return + +#define CHECK_COUNT(count, type) \ + do { \ + int max_elem = type_nelem(type); \ + if(!(count) || (count) > max_elem) { \ + count = max_elem; \ + } \ + } while(0) + +void set_unistate(int sidx, const int *val, int count) +{ + CHECK_INDEX(sidx); + CHECK_COUNT(count, state[sidx].type); + + memcpy(state[sidx].ival, val, count * sizeof *state[sidx].ival); +} + +void set_unistate(int sidx, const float *val, int count) +{ + CHECK_INDEX(sidx); + CHECK_COUNT(count, state[sidx].type); + + memcpy(state[sidx].fval, val, count * sizeof *state[sidx].fval); + state[sidx].transpose = 0; +} + +void get_unistate(int sidx, int *val, int count) +{ + CHECK_INDEX(sidx); + CHECK_COUNT(count, state[sidx].type); + + memcpy(val, state[sidx].ival, count * sizeof *val); +} + +void get_unistate(int sidx, float *val, int count) +{ + CHECK_INDEX(sidx); + CHECK_COUNT(count, state[sidx].type); + + memcpy(val, state[sidx].fval, count * sizeof *val); +} + +void set_unistate(int sidx, int val) +{ + set_unistate(sidx, &val, 1); +} + +void set_unistate(int sidx, float val) +{ + set_unistate(sidx, &val, 1); +} + +void set_unistate(int sidx, const Vector2 &vec) +{ + set_unistate(sidx, &vec.x, 2); +} + +void set_unistate(int sidx, const Vector3 &vec) +{ + set_unistate(sidx, &vec.x, 3); +} + +void set_unistate(int sidx, const Vector4 &vec) +{ + set_unistate(sidx, &vec.x, 4); +} + +void set_unistate(int sidx, const Matrix3x3 &mat) +{ + set_unistate(sidx, mat[0], 9); + state[sidx].transpose = 1; +} + +void set_unistate(int sidx, const Matrix4x4 &mat) +{ + set_unistate(sidx, mat[0], 16); + state[sidx].transpose = 1; +} + + +int set_unistate(const char *name, int *val, int count) +{ + int sidx = get_unistate_index(name); + if(sidx < 0) { + StType type = int_type(count); + if(type == ST_UNKNOWN) { + error_log("invalid element count (%d) while setting previously unknown unistate item \"%s\"\n", + count, name); + return -1; + } + + sidx = add_unistate(name, type); + } + set_unistate(sidx, val); + return sidx; +} + +int set_unistate(const char *name, float *val, int count) +{ + int sidx = get_unistate_index(name); + if(sidx < 0) { + StType type = float_type(count); + if(type == ST_UNKNOWN) { + error_log("invalid element count (%d) while setting previously unknown unistate item \"%s\"\n", + count, name); + return -1; + } + + sidx = add_unistate(name, type); + } + set_unistate(sidx, val); + return sidx; +} + +int set_unistate(const char *name, int val) +{ + int sidx = get_unistate_index(name); + if(sidx < 0) { + sidx = add_unistate(name, ST_INT); + } + set_unistate(sidx, val); + return sidx; +} + +int set_unistate(const char *name, float val) +{ + int sidx = get_unistate_index(name); + if(sidx < 0) { + sidx = add_unistate(name, ST_FLOAT); + } + set_unistate(sidx, val); + return sidx; +} + +int set_unistate(const char *name, const Vector2 &vec) +{ + int sidx = get_unistate_index(name); + if(sidx < 0) { + sidx = add_unistate(name, ST_FLOAT2); + } + set_unistate(sidx, vec); + return sidx; +} + +int set_unistate(const char *name, const Vector3 &vec) +{ + int sidx = get_unistate_index(name); + if(sidx < 0) { + sidx = add_unistate(name, ST_FLOAT3); + } + set_unistate(sidx, vec); + return sidx; +} + +int set_unistate(const char *name, const Vector4 &vec) +{ + int sidx = get_unistate_index(name); + if(sidx < 0) { + sidx = add_unistate(name, ST_FLOAT4); + } + set_unistate(sidx, vec); + return sidx; +} + +int set_unistate(const char *name, const Matrix3x3 &mat) +{ + int sidx = get_unistate_index(name); + if(sidx < 0) { + sidx = add_unistate(name, ST_MATRIX3); + } + set_unistate(sidx, mat); + return sidx; +} + +int set_unistate(const char *name, const Matrix4x4 &mat) +{ + int sidx = get_unistate_index(name); + if(sidx < 0) { + sidx = add_unistate(name, ST_MATRIX4); + } + set_unistate(sidx, mat); + return sidx; +} + + +int get_unistate_int(int sidx) +{ + int val = 0; + get_unistate(sidx, &val, 1); + return val; +} + +float get_unistate_float(int sidx) +{ + float val = 0.0f; + get_unistate(sidx, &val, 1); + return val; +} + +Vector2 get_unistate_vec2(int sidx) +{ + float val[2] = {0.0f, 0.0f}; + get_unistate(sidx, val, 2); + return Vector2(val[0], val[1]); +} + +Vector3 get_unistate_vec3(int sidx) +{ + float val[3] = {0.0f, 0.0f, 0.0f}; + get_unistate(sidx, val, 3); + return Vector3(val[0], val[1], val[2]); +} + +Vector4 get_unistate_vec4(int sidx) +{ + float val[4] = {0.0f, 0.0f, 0.0f}; + get_unistate(sidx, val, 4); + return Vector4(val[0], val[1], val[2], val[3]); +} + +Matrix3x3 get_unistate_mat3(int sidx) +{ + Matrix3x3 res; + get_unistate(sidx, res.m[0], 9); + return res; +} + +Matrix4x4 get_unistate_mat4(int sidx) +{ + Matrix4x4 res; + get_unistate(sidx, res.m[0], 16); + return res; +} + + +int get_unistate_int(const char *name) +{ + int sidx = get_unistate_index(name); + if(sidx == -1) { + return 0; + } + return get_unistate_int(sidx); +} + +float get_unistate_float(const char *name) +{ + int sidx = get_unistate_index(name); + if(sidx == -1) { + return 0.0f; + } + return get_unistate_float(sidx); +} + +Vector2 get_unistate_vec2(const char *name) +{ + int sidx = get_unistate_index(name); + if(sidx == -1) { + return Vector2(); + } + return get_unistate_vec2(sidx); +} + +Vector3 get_unistate_vec3(const char *name) +{ + int sidx = get_unistate_index(name); + if(sidx == -1) { + return Vector3(); + } + return get_unistate_vec3(sidx); +} + +Vector4 get_unistate_vec4(const char *name) +{ + int sidx = get_unistate_index(name); + if(sidx == -1) { + return Vector4(); + } + return get_unistate_vec4(sidx); +} + +Matrix3x3 get_unistate_mat3(const char *name) +{ + int sidx = get_unistate_index(name); + if(sidx == -1) { + return Matrix3x3(); + } + return get_unistate_mat3(sidx); +} + +Matrix4x4 get_unistate_mat4(const char *name) +{ + int sidx = get_unistate_index(name); + if(sidx == -1) { + return Matrix4x4(); + } + return get_unistate_mat4(sidx); +} + + +void setup_unistate(const ShaderProg *sdr) +{ + if(!sdr) { + if(!(sdr = ShaderProg::current)) { + return; + } + } + + sdr->setup_state_uniforms(); +} + +bool setup_unistate(int sidx, const ShaderProg *sdr, int loc) +{ + if(loc < 0 || sidx < 0 || sidx >= (int)state.size()) { + return false; + } + + CHECKGLERR; + glUseProgram(sdr->get_id()); + CHECKGLERR; + + switch(state[sidx].type) { + case ST_INT: + glUniform1iv(loc, 1, state[sidx].ival); + break; + case ST_INT2: + glUniform2iv(loc, 1, state[sidx].ival); + break; + case ST_INT3: + glUniform3iv(loc, 1, state[sidx].ival); + break; + case ST_INT4: + glUniform4iv(loc, 1, state[sidx].ival); + break; + + case ST_FLOAT: + glUniform1fv(loc, 1, state[sidx].fval); + break; + case ST_FLOAT2: + glUniform2fv(loc, 1, state[sidx].fval); + break; + case ST_FLOAT3: + glUniform3fv(loc, 1, state[sidx].fval); + break; + case ST_FLOAT4: + glUniform4fv(loc, 1, state[sidx].fval); + break; + + case ST_MATRIX3: +#ifdef GL_ES_VERSION_2_0 + { + float tmat[9], *ptr = tmat; + for(int i=0; i<3; i++) { + for(int j=0; j<3; j++) { + *ptr++ = state[sidx].fval[j * 3 + i]; + } + } + glUniformMatrix3fv(loc, 1, GL_FALSE, tmat); + } +#else + glUniformMatrix3fv(loc, 1, state[sidx].transpose, state[sidx].fval); +#endif + break; + + case ST_MATRIX4: +#ifdef GL_ES_VERSION_2_0 + { + float tmat[16], *ptr = tmat; + for(int i=0; i<4; i++) { + for(int j=0; j<4; j++) { + *ptr++ = state[sidx].fval[j * 4 + i]; + } + } + glUniformMatrix4fv(loc, 1, GL_FALSE, tmat); + } +#else + glUniformMatrix4fv(loc, 1, state[sidx].transpose, state[sidx].fval); +#endif + break; + + default: + return false; + } + + CHECKGLERR; + return true; +} + +bool setup_unistate(const char *name, const ShaderProg *sdr) +{ + int loc = sdr->get_uniform_location(name); + if(loc == -1) { + return false; + } + return setup_unistate(get_unistate_index(name), sdr, loc); +} + +void set_world_matrix(const Matrix4x4 &mat) +{ + static int sidx = -1, sidx_transp, sidx_mat3; + + if(sidx == -1) { + sidx = add_unistate("st_world_matrix", ST_MATRIX4); + sidx_mat3 = add_unistate("st_world_matrix3", ST_MATRIX3); + sidx_transp = add_unistate("st_world_matrix_transpose", ST_MATRIX4); + } + + set_unistate(sidx, mat); + set_unistate(sidx_mat3, Matrix3x3(mat)); + set_unistate(sidx_transp, mat[0]); // by using the float* variant, we unset the transpose flag +} + +void set_view_matrix(const Matrix4x4 &mat) +{ + static int sidx = -1, sidx_transp, sidx_mat3; + + if(sidx == -1) { + sidx = add_unistate("st_view_matrix", ST_MATRIX4); + sidx_mat3 = add_unistate("st_view_matrix3", ST_MATRIX3); + sidx_transp = add_unistate("st_view_matrix_transpose", ST_MATRIX4); + } + + set_unistate(sidx, mat); + set_unistate(sidx_mat3, Matrix3x3(mat)); + set_unistate(sidx_transp, mat[0]); // by using the float* variant, we unset the transpose flag +} + +void set_projection_matrix(const Matrix4x4 &mat) +{ + static int sidx = -1; + + if(sidx == -1) { + sidx = add_unistate("st_proj_matrix", ST_MATRIX4); + } + + set_unistate(sidx, mat); +} + +void set_texture_matrix(const Matrix4x4 &mat) +{ + static int sidx = -1; + + if(sidx == -1) { + sidx = add_unistate("st_tex_matrix", ST_MATRIX4); + } + + set_unistate(sidx, mat); +} + +Matrix4x4 get_world_matrix() +{ + static int sidx = -1; + + if(sidx == -1) { + if((sidx = get_unistate_index("st_world_matrix")) == -1) { + return Matrix4x4(); + } + } + return get_unistate_mat4(sidx); +} + +Matrix4x4 get_view_matrix() +{ + static int sidx = -1; + + if(sidx == -1) { + if((sidx = get_unistate_index("st_view_matrix")) == -1) { + return Matrix4x4(); + } + } + return get_unistate_mat4(sidx); +} + +Matrix4x4 get_projection_matrix() +{ + static int sidx = -1; + + if(sidx == -1) { + if((sidx = get_unistate_index("st_proj_matrix")) == -1) { + return Matrix4x4(); + } + } + return get_unistate_mat4(sidx); +} + +Matrix4x4 get_texture_matrix() +{ + static int sidx = -1; + + if(sidx == -1) { + if((sidx = get_unistate_index("st_tex_matrix")) == -1) { + return Matrix4x4(); + } + } + return get_unistate_mat4(sidx); +} + +void setup_gl_matrices() +{ +#ifdef USE_OLDGL + Matrix4x4 modelview = get_world_matrix() * get_view_matrix(); + Matrix4x4 proj = get_projection_matrix(); + Matrix4x4 tex = get_texture_matrix(); + + glMatrixMode(GL_TEXTURE); + glLoadTransposeMatrixf(tex[0]); + glMatrixMode(GL_PROJECTION); + glLoadTransposeMatrixf(proj[0]); + glMatrixMode(GL_MODELVIEW); + glLoadTransposeMatrixf(modelview[0]); +#endif +} + +static const char *typestr(StType type) +{ + switch(type) { + case ST_INT: + return "int"; + case ST_INT2: + return "ivec2"; + case ST_INT3: + return "ivec3"; + case ST_INT4: + return "ivec4"; + case ST_FLOAT: + return "float"; + case ST_FLOAT2: + return "vec2"; + case ST_FLOAT3: + return "vec3"; + case ST_FLOAT4: + return "vec4"; + case ST_MATRIX3: + return "mat3"; + case ST_MATRIX4: + return "mat4"; + + default: + break; + } + return ""; +} + +static int type_nelem(StType type) +{ + switch(type) { + case ST_INT: + case ST_FLOAT: + return 1; + case ST_INT2: + case ST_FLOAT2: + return 2; + case ST_INT3: + case ST_FLOAT3: + return 3; + case ST_INT4: + case ST_FLOAT4: + return 4; + case ST_MATRIX3: + return 9; + case ST_MATRIX4: + return 16; + + default: + break; + } + + return 0; +} + +static StType float_type(int elem) +{ + switch(elem) { + case 1: + return ST_FLOAT; + case 2: + return ST_FLOAT2; + case 3: + return ST_FLOAT3; + case 4: + return ST_FLOAT4; + case 9: + return ST_MATRIX3; + case 16: + return ST_MATRIX4; + default: + break; + } + return ST_UNKNOWN; +} + +static StType int_type(int elem) +{ + switch(elem) { + case 1: + return ST_INT; + case 2: + return ST_INT2; + case 3: + return ST_INT3; + case 4: + return ST_INT4; + default: + break; + } + return ST_UNKNOWN; +} diff -r 778ed91cb7fd -r 283cdfa7dda2 src/unistate.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/unistate.h Sun Aug 24 09:41:24 2014 +0300 @@ -0,0 +1,104 @@ +#ifndef UNISTATE_H_ +#define UNISTATE_H_ + +#include "vmath/vmath.h" + +class ShaderProg; + +enum StType { + ST_UNKNOWN, + ST_INT, ST_INT2, ST_INT3, ST_INT4, + ST_FLOAT, ST_FLOAT2, ST_FLOAT3, ST_FLOAT4, + ST_MATRIX3, ST_MATRIX4 +}; + +int add_unistate(const char *name, StType type); +int get_unistate_index(const char *name); + +/** set the uniform state identified by \param sidx by copying + * a number of elements from \param val. If \param count is 0 + * then it's automatically set based on the type of this state item. + * @{ */ +void set_unistate(int sidx, const int *val, int count = 0); +void set_unistate(int sidx, const float *val, int count = 0); +/// @} + +/** get the uniform state identified by \param sidx by copying + * a number of elements into \param val. If \param count is 0 + * then it's automatically set based on the type of this state item. + * @{ */ +void get_unistate(int sidx, int *val, int count = 0); +void get_unistate(int sidx, float *val, int count = 0); +/// @} + +/// convenience versions of set_unistate @{ +void set_unistate(int sidx, int val); +void set_unistate(int sidx, float val); +void set_unistate(int sidx, const Vector2 &vec); +void set_unistate(int sidx, const Vector3 &vec); +void set_unistate(int sidx, const Vector4 &vec); +void set_unistate(int sidx, const Matrix3x3 &mat); +void set_unistate(int sidx, const Matrix4x4 &mat); +/// @} + +/** convenience functions for setting the uniform state by name. + * if the name cannot be found in the current set of uniform state + * items, a new one is created with a type derived from the variant + * of the function that was called (which might not be what you want). + * The index of the state item is returned. + * @{ */ +int set_unistate(const char *name, int *val, int count = 0); +int set_unistate(const char *name, float *val, int count = 0); +int set_unistate(const char *name, int val); +int set_unistate(const char *name, float val); +int set_unistate(const char *name, const Vector2 &vec); +int set_unistate(const char *name, const Vector3 &vec); +int set_unistate(const char *name, const Vector4 &vec); +int set_unistate(const char *name, const Matrix3x3 &mat); +int set_unistate(const char *name, const Matrix4x4 &mat); +/// @} + +/// convenience versions of get_unistate @{ +int get_unistate_int(int sidx); +float get_unistate_float(int sidx); +Vector2 get_unistate_vec2(int sidx); +Vector3 get_unistate_vec3(int sidx); +Vector4 get_unistate_vec4(int sidx); +Matrix3x3 get_unistate_mat3(int sidx); +Matrix4x4 get_unistate_mat4(int sidx); +/// @} + +/// convenience versions of get_unistate for getting the uniform state by name @{ +int get_unistate_int(const char *name); +float get_unistate_float(const char *name); +Vector2 get_unistate_vec2(const char *name); +Vector3 get_unistate_vec3(const char *name); +Vector4 get_unistate_vec4(const char *name); +Matrix3x3 get_unistate_mat3(const char *name); +Matrix4x4 get_unistate_mat4(const char *name); +/// @} + +/** Prepare for rendering by setting up all the state uniforms in the shader sdr. + * If sdr is null, then use the "current" shader as per ShaderProg::current + */ +void setup_unistate(const ShaderProg *sdr = 0); + +bool setup_unistate(int sidx, const ShaderProg *sdr, int loc); +bool setup_unistate(const char *name, const ShaderProg *sdr); + +// special functions for setting the rendering pipeline matrices +void set_world_matrix(const Matrix4x4 &mat); +void set_view_matrix(const Matrix4x4 &mat); +void set_projection_matrix(const Matrix4x4 &mat); +void set_texture_matrix(const Matrix4x4 &mat); + +Matrix4x4 get_world_matrix(); +Matrix4x4 get_view_matrix(); +Matrix4x4 get_projection_matrix(); +Matrix4x4 get_texture_matrix(); + +void setup_gl_matrices(); // this shouldn't be needed in the final code + +// TODO should do a matrix stack at some point ... + +#endif // UNISTATE_H_ diff -r 778ed91cb7fd -r 283cdfa7dda2 src/xform_node.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xform_node.cc Sun Aug 24 09:41:24 2014 +0300 @@ -0,0 +1,461 @@ +#include +#include +#include "xform_node.h" +#include "anim/anim.h" +#include "anim/track.h" + +static inline anm_interpolator track_interpolator(Interp in); +static inline anm_extrapolator track_extrapolator(Extrap ex); + +XFormNode::XFormNode() +{ + anm = new anm_node; + anm_init_node(anm); + parent = 0; + + // TODO read them from anm to get the correct initial values + interp = INTERP_LINEAR; + extrap = EXTRAP_EXTEND; +} + +XFormNode::~XFormNode() +{ + anm_destroy_node(anm); + delete anm; +} + +void XFormNode::set_name(const char *name) +{ + anm_set_node_name(anm, name); +} + +const char *XFormNode::get_name() const +{ + return anm_get_node_name(anm); +} + +void XFormNode::set_interpolator(Interp in) +{ + anm_set_interpolator(anm, track_interpolator(in)); + interp = in; +} + +Interp XFormNode::get_interpolator() const +{ + return interp; +} + +void XFormNode::set_extrapolator(Extrap ex) +{ + anm_set_extrapolator(anm, track_extrapolator(ex)); + extrap = ex; +} + +Extrap XFormNode::get_extrapolator() const +{ + return extrap; +} + +XFormNode *XFormNode::get_parent() +{ + return parent; +} + +const XFormNode *XFormNode::get_parent() const +{ + return parent; +} + +void XFormNode::add_child(XFormNode *child) +{ + children.push_back(child); + anm_link_node(anm, child->anm); + child->parent = this; +} + +void XFormNode::remove_child(XFormNode *child) +{ + std::vector::iterator it; + it = std::find(children.begin(), children.end(), child); + if(it != children.end()) { + children.erase(it); + anm_unlink_node(anm, child->anm); + + if(child->parent == this) { + child->parent = 0; + } + } +} + +int XFormNode::get_children_count() const +{ + return (int)children.size(); +} + +XFormNode *XFormNode::get_child(int idx) +{ + if(idx >= 0 && idx < get_children_count()) { + return children[idx]; + } + return 0; +} + +const XFormNode *XFormNode::get_child(int idx) const +{ + if(idx >= 0 && idx < get_children_count()) { + return children[idx]; + } + return 0; +} + + +void XFormNode::use_animation(int idx) +{ + if(idx >= 0) { + anm_use_animation(anm, idx); + } +} + +void XFormNode::use_animation(const char *name) +{ + anm_use_animation(anm, anm_find_animation(anm, name)); +} + +void XFormNode::use_animation(int aidx, int bidx, float t) +{ + anm_use_animations(anm, aidx, bidx, t); +} + +void XFormNode::use_animation(const char *aname, const char *bname, float t) +{ + int aidx = anm_find_animation(anm, aname); + int bidx = anm_find_animation(anm, bname); + + if(aidx == -1) { + use_animation(bidx); + } + if(bidx == -1) { + use_animation(aidx); + } + anm_use_animations(anm, aidx, bidx, t); +} + +int XFormNode::get_active_animation_index(int which) const +{ + return anm_get_active_animation_index(anm, which); +} + +float XFormNode::get_active_animation_mix() const +{ + return anm_get_active_animation_mix(anm); +} + +int XFormNode::get_animation_count() const +{ + return anm_get_animation_count(anm); +} + +void XFormNode::add_animation(const char *name) +{ + int idx = get_animation_count(); + + anm_add_animation(anm); + use_animation(idx); + + if(name) { + set_animation_name(name); + } +} + +void XFormNode::set_animation_name(const char *name) +{ + anm_set_active_animation_name(anm, name); +} + +const char *XFormNode::get_animation_name() const +{ + return anm_get_active_animation_name(anm); +} + + + +void XFormNode::set_position(const Vector3 &pos, long tmsec) +{ + anm_set_position(anm, v3_cons(pos.x, pos.y, pos.z), ANM_MSEC2TM(tmsec)); +} + +Vector3 XFormNode::get_node_position(long tmsec) const +{ + vec3_t p = anm_get_node_position(anm, ANM_MSEC2TM(tmsec)); + return Vector3(p.x, p.y, p.z); +} + +void XFormNode::set_rotation(const Quaternion &quat, long tmsec) +{ + anm_set_rotation(anm, quat_cons(quat.s, quat.v.x, quat.v.y, quat.v.z), ANM_MSEC2TM(tmsec)); +} + +Quaternion XFormNode::get_node_rotation(long tmsec) const +{ + quat_t q = anm_get_node_rotation(anm, ANM_MSEC2TM(tmsec)); + return Quaternion(q.w, q.x, q.y, q.z); +} + +void XFormNode::set_scaling(const Vector3 &pos, long tmsec) +{ + anm_set_scaling(anm, v3_cons(pos.x, pos.y, pos.z), ANM_MSEC2TM(tmsec)); +} + +Vector3 XFormNode::get_node_scaling(long tmsec) const +{ + vec3_t s = anm_get_node_scaling(anm, ANM_MSEC2TM(tmsec)); + return Vector3(s.x, s.y, s.z); +} + +// these take hierarchy into account +Vector3 XFormNode::get_position(long tmsec) const +{ + vec3_t v = anm_get_position(anm, ANM_MSEC2TM(tmsec)); + return Vector3(v.x, v.y, v.z); +} + +Quaternion XFormNode::get_rotation(long tmsec) const +{ + quat_t q = anm_get_rotation(anm, tmsec); + return Quaternion(q.w, q.x, q.y, q.z); +} + +Vector3 XFormNode::get_scaling(long tmsec) const +{ + vec3_t v = anm_get_scaling(anm, ANM_MSEC2TM(tmsec)); + return Vector3(v.x, v.y, v.z); +} + +void XFormNode::set_pivot(const Vector3 &pivot) +{ + anm_set_pivot(anm, v3_cons(pivot.x, pivot.y, pivot.z)); +} + +Vector3 XFormNode::get_pivot() const +{ + vec3_t p = anm_get_pivot(anm); + return Vector3(p.x, p.y, p.z); +} + +void XFormNode::set_local_matrix(const Matrix4x4 &mat) +{ + local_matrix = mat; +} + +const Matrix4x4 &XFormNode::get_local_matrix() const +{ + return local_matrix; +} + +void XFormNode::set_bone_matrix(const Matrix4x4 &bmat) +{ + bone_matrix = bmat; +} + +const Matrix4x4 &XFormNode::get_bone_matrix() const +{ + return bone_matrix; +} + +#define FOO + +void XFormNode::get_node_xform(long tmsec, Matrix4x4 *mat, Matrix4x4 *inv_mat) const +{ + anm_time_t tm = ANM_MSEC2TM(tmsec); + + if(mat) { + anm_get_node_matrix(anm, (scalar_t(*)[4])mat, tm); +#ifdef FOO + *mat = local_matrix * *mat; +#else + *mat = *mat * local_matrix; +#endif + } + if(inv_mat) { + anm_get_inv_matrix(anm, (scalar_t(*)[4])inv_mat, tm); + } +} + +void XFormNode::get_xform(long tmsec, Matrix4x4 *mat, Matrix4x4 *inv_mat) const +{ + anm_time_t tm = ANM_MSEC2TM(tmsec); + + if(mat) { + anm_get_matrix(anm, (scalar_t(*)[4])mat, tm); +#ifdef FOO + *mat = local_matrix * *mat; +#else + *mat = *mat * local_matrix; +#endif + } + if(inv_mat) { + anm_get_inv_matrix(anm, (scalar_t(*)[4])inv_mat, tm); + } +} + + +// ---- Track ---- + +Track::Track() +{ + trk = new anm_track; + anm_init_track(trk); +} + +Track::~Track() +{ + anm_destroy_track(trk); + delete trk; +} + +Track::Track(const Track &rhs) +{ + trk = new anm_track; + anm_init_track(trk); + anm_copy_track(trk, rhs.trk); + interp = rhs.interp; + extrap = rhs.extrap; +} + +Track &Track::operator =(const Track &rhs) +{ + if(&rhs == this) { + return *this; + } + + anm_copy_track(trk, rhs.trk); + interp = rhs.interp; + extrap = rhs.extrap; + return *this; +} + + +void Track::set_interpolator(Interp in) +{ + anm_set_track_interpolator(trk, track_interpolator(in)); + interp = in; +} + +Interp Track::get_interpolator() const +{ + return interp; +} + +void Track::set_extrapolator(Extrap ex) +{ + anm_set_track_extrapolator(trk, track_extrapolator(ex)); + extrap = ex; +} + +Extrap Track::get_extrapolator() const +{ + return extrap; +} + +void Track::set_default(double def) +{ + anm_set_track_default(trk, def); +} + +void Track::set_value(float val, long tmsec) +{ + anm_set_value(trk, ANM_MSEC2TM(tmsec), val); +} + +float Track::get_value(long tmsec) const +{ + return anm_get_value(trk, ANM_MSEC2TM(tmsec)); +} + +float Track::operator ()(long tmsec) const +{ + return anm_get_value(trk, ANM_MSEC2TM(tmsec)); +} + + +// ---- Track3 ---- + +void Track3::set_interpolator(Interp in) +{ + for(int i=0; i<3; i++) { + track[i].set_interpolator(in); + } +} + +Interp Track3::get_interpolator() const +{ + return track[0].get_interpolator(); +} + +void Track3::set_extrapolator(Extrap ex) +{ + for(int i=0; i<3; i++) { + track[i].set_extrapolator(ex); + } +} + +Extrap Track3::get_extrapolator() const +{ + return track[0].get_extrapolator(); +} + +void Track3::set_default(const Vector3 &def) +{ + for(int i=0; i<3; i++) { + track[i].set_default(def[i]); + } +} + +void Track3::set_value(const Vector3 &val, long tmsec) +{ + for(int i=0; i<3; i++) { + track[i].set_value(val[i], tmsec); + } +} + +Vector3 Track3::get_value(long tmsec) const +{ + return Vector3(track[0](tmsec), track[1](tmsec), track[2](tmsec)); +} + +Vector3 Track3::operator ()(long tmsec) const +{ + return Vector3(track[0](tmsec), track[1](tmsec), track[2](tmsec)); +} + + +static inline anm_interpolator track_interpolator(Interp in) +{ + switch(in) { + case INTERP_STEP: + return ANM_INTERP_STEP; + case INTERP_LINEAR: + return ANM_INTERP_LINEAR; + case INTERP_CUBIC: + return ANM_INTERP_CUBIC; + } + + assert(0); + return ANM_INTERP_STEP; +} + +static inline anm_extrapolator track_extrapolator(Extrap ex) +{ + switch(ex) { + case EXTRAP_EXTEND: + return ANM_EXTRAP_EXTEND; + case EXTRAP_CLAMP: + return ANM_EXTRAP_CLAMP; + case EXTRAP_REPEAT: + return ANM_EXTRAP_REPEAT; + } + + assert(0); + return ANM_EXTRAP_EXTEND; +} + diff -r 778ed91cb7fd -r 283cdfa7dda2 src/xform_node.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xform_node.h Sun Aug 24 09:41:24 2014 +0300 @@ -0,0 +1,154 @@ +#ifndef GOATGFX_XFORM_NODE_H_ +#define GOATGFX_XFORM_NODE_H_ + +#include +#include "vmath/vector.h" +#include "vmath/quat.h" +#include "vmath/matrix.h" + + +struct anm_node; +struct anm_track; + +enum Interp { INTERP_STEP, INTERP_LINEAR, INTERP_CUBIC }; +enum Extrap { EXTRAP_EXTEND, EXTRAP_CLAMP, EXTRAP_REPEAT }; + +// NOTE: all time arguments are milliseconds + +class XFormNode { +private: + struct anm_node *anm; + std::vector children; + XFormNode *parent; + + Interp interp; + Extrap extrap; + + Matrix4x4 local_matrix; + Matrix4x4 bone_matrix; + + XFormNode(const XFormNode &node) {} + XFormNode &operator =(const XFormNode &node) { return *this; } + +public: + XFormNode(); + virtual ~XFormNode(); + + virtual void set_name(const char *name); + virtual const char *get_name() const; + + virtual void set_interpolator(Interp in); + virtual Interp get_interpolator() const; + virtual void set_extrapolator(Extrap ex); + virtual Extrap get_extrapolator() const; + + virtual XFormNode *get_parent(); + virtual const XFormNode *get_parent() const; + + // children management + virtual void add_child(XFormNode *child); + virtual void remove_child(XFormNode *child); + + virtual int get_children_count() const; + virtual XFormNode *get_child(int idx); + virtual const XFormNode *get_child(int idx) const; + + + virtual void use_animation(int idx); + virtual void use_animation(const char *name); + virtual void use_animation(int aidx, int bidx, float t); + virtual void use_animation(const char *aname, const char *bname, float t); + + virtual int get_active_animation_index(int which = 0) const; + virtual float get_active_animation_mix() const; + + virtual int get_animation_count() const; + + // add a new empty animation slot (recursive) + virtual void add_animation(const char *name = 0); + + // set/get the current animation name (set is recursive) + virtual void set_animation_name(const char *name); + virtual const char *get_animation_name() const; + + + virtual void set_position(const Vector3 &pos, long tmsec = 0); + virtual Vector3 get_node_position(long tmsec = 0) const; + + virtual void set_rotation(const Quaternion &quat, long tmsec = 0); + virtual Quaternion get_node_rotation(long tmsec = 0) const; + + virtual void set_scaling(const Vector3 &pos, long tmsec = 0); + virtual Vector3 get_node_scaling(long tmsec = 0) const; + + // these take hierarchy into account + virtual Vector3 get_position(long tmsec = 0) const; + virtual Quaternion get_rotation(long tmsec = 0) const; + virtual Vector3 get_scaling(long tmsec = 0) const; + + virtual void set_pivot(const Vector3 &pivot); + virtual Vector3 get_pivot() const; + + // the local matrix is concatenated with the regular node/anim matrix + virtual void set_local_matrix(const Matrix4x4 &mat); + virtual const Matrix4x4 &get_local_matrix() const; + + // for bone nodes, the transformation of the bone in bind position + virtual void set_bone_matrix(const Matrix4x4 &bmat); + virtual const Matrix4x4 &get_bone_matrix() const; + + // node transformation alone + virtual void get_node_xform(long tmsec, Matrix4x4 *mat, Matrix4x4 *inv_mat = 0) const; + + // node transformation taking hierarchy into account + virtual void get_xform(long tmsec, Matrix4x4 *mat, Matrix4x4 *inv_mat = 0) const; +}; + + +class Track { +private: + struct anm_track *trk; + Interp interp; + Extrap extrap; + +public: + Track(); + ~Track(); + + Track(const Track &trk); + Track &operator =(const Track &trk); + + void set_interpolator(Interp in); + Interp get_interpolator() const; + void set_extrapolator(Extrap ex); + Extrap get_extrapolator() const; + + void set_default(double def); + + void set_value(float val, long tmsec = 0); + float get_value(long tmsec = 0) const; + + // the same as get_value + float operator ()(long tmsec = 0) const; +}; + +class Track3 { +private: + Track track[3]; + +public: + void set_interpolator(Interp in); + Interp get_interpolator() const; + void set_extrapolator(Extrap ex); + Extrap get_extrapolator() const; + + void set_default(const Vector3 &def); + + void set_value(const Vector3 &val, long tmsec = 0); + Vector3 get_value(long tmsec = 0) const; + + // the same as get_value + Vector3 operator ()(long tmsec = 0) const; +}; + +#endif /* GOATGFX_XFORM_NODE_H_ */ diff -r 778ed91cb7fd -r 283cdfa7dda2 vrchess.sln --- a/vrchess.sln Sat Aug 23 12:03:29 2014 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,20 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vrchess", "vrchess.vcxproj", "{714906B6-FD4C-4ECC-990C-247FA46B8801}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {714906B6-FD4C-4ECC-990C-247FA46B8801}.Debug|Win32.ActiveCfg = Debug|Win32 - {714906B6-FD4C-4ECC-990C-247FA46B8801}.Debug|Win32.Build.0 = Debug|Win32 - {714906B6-FD4C-4ECC-990C-247FA46B8801}.Release|Win32.ActiveCfg = Release|Win32 - {714906B6-FD4C-4ECC-990C-247FA46B8801}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff -r 778ed91cb7fd -r 283cdfa7dda2 vrchess.vcxproj --- a/vrchess.vcxproj Sat Aug 23 12:03:29 2014 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,117 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {714906B6-FD4C-4ECC-990C-247FA46B8801} - Win32Proj - vrchess - - - - Application - true - v120 - MultiByte - - - Application - false - v120 - false - MultiByte - - - - - - - - - - - - - true - - - false - - - - - - Level3 - Disabled - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions);USE_LIBOVR - 4996;4244;4305 - $(SolutionDir)\src - - - Console - true - opengl32.lib;freeglutd.lib;glew32.lib;libvmath.lib;libimago2.lib;jpeglib.lib;libpng.lib;zlib.lib;libovrd.lib;ws2_32.lib;%(AdditionalDependencies) - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);USE_LIBOVR - 4996;4244;4305 - $(SolutionDir)\src - - - Console - true - true - true - opengl32.lib;freeglut.lib;glew32.lib;libvmath.lib;libimago2.lib;jpeglib.lib;libpng.lib;zlib.lib;libovr.lib;ws2_32.lib;%(AdditionalDependencies) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff -r 778ed91cb7fd -r 283cdfa7dda2 vrchess.vcxproj.filters --- a/vrchess.vcxproj.filters Sat Aug 23 12:03:29 2014 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,94 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;h;inl - - - {e881ab02-1a45-43f6-a15d-ee7f77256a1e} - - - - - src - - - src - - - src - - - src - - - src - - - src - - - src - - - src\vr - - - src\vr - - - src\vr - - - src\vr - - - src\vr - - - src\vr - - - src\vr - - - src\vr - - - - - src - - - src - - - src - - - src - - - src - - - src - - - src\vr - - - src\vr - - - src\vr - - - src\vr - - - src\vr - - - \ No newline at end of file