# HG changeset patch # User John Tsiombikas # Date 1384399629 -7200 # Node ID 1873dfd13f2d363fb9c3a27f7148b42a75fb2159 initial commit diff -r 000000000000 -r 1873dfd13f2d .hgignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,7 @@ +\.o$ +\.d$ +\.swp$ +\.a$ +\.so\. +\.so$ +\.dylib$ diff -r 000000000000 -r 1873dfd13f2d Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,80 @@ +PREFIX = /usr/local + +csrc = $(wildcard src/*.c) +ccsrc = $(wildcard src/*.cc) +obj = $(csrc:.c=.o) $(ccsrc:.cc=.o) +dep = $(obj:.o=.d) +name = goat3dgfx +lib_a = lib$(name).a + +so_major = 0 +so_minor = 1 + +ifeq ($(shell uname -r), Darwin) + libgl = -framework OpenGL -framework GLUT -lGLEW + + lib_so = lib$(name).dylib + shared = -dynamiclib +else + libgl = -lGL -lGLU -lglut -lGLEW + + lib_so = lib$(name).so.$(so_major).$(so_minor) + soname = lib$(name).so.$(so_major) + devlink = lib$(name).so + shared = -shared -Wl,-soname=$(soname) + pic = -fPIC +endif + + +CFLAGS = -pedantic $(warn) $(dbg) $(pic) $(opt) $(inc) $(libs_cflags) +CXXFLAGS = $(CFLAGS) +LDFLAGS = $(libgl) $(libs_ldflags) + +.PHONY: all +all: $(lib_so) $(lib_a) + +$(lib_a): $(obj) + $(AR) rcs $@ $(obj) + +$(lib_so): $(obj) + $(CXX) $(shared) -o $@ $(obj) $(LDFLAGS) + +-include $(dep) + +%.d: %.c + @$(CPP) $(CFLAGS) $< -MM -MT $(@:.d=.o) >$@ + +%.d: %.cc + @$(CPP) $(CXXFLAGS) $< -MM -MT $(@:.d=.o) >$@ + +.PHONY: clean +clean: + rm -f $(obj) $(lib_a) $(lib_so) + +.PHONY: cleandep +cleandep: + rm -f $(dep) + +.PHONY: install +install: $(lib_so) $(lib_a) + mkdir -p $(DESTDIR)$(PREFIX)/lib $(DESTDIR)$(PREFIX)/include/goat3dgfx + cp src/*.h src/*.inl $(DESTDIR)$(PREFIX)/include/goat3dgfx/ + cp $(lib_a) $(DESTDIR)$(PREFIX)/lib/$(lib_a) + cp $(lib_so) $(DESTDIR)$(PREFIX)/lib/$(lib_so) + [ -n "$(devlink)" ] && \ + cd $(DESTDIR)$(PREFIX)/lib && \ + rm -f $(soname) $(devlink) && \ + ln -s $(lib_so) $(soname) && \ + ln -s $(soname) $(devlink) || \ + true + +.PHONY: uninstall +uninstall: + rm -f $(DESTDIR)$(PREFIX)/include/goat3dgfx/* + rmdir $(DESTDIR)$(PREFIX)/include/goat3dgfx + rm -f $(DESTDIR)$(PREFIX)/lib/$(lib_so) + rm -f $(DESTDIR)$(PREFIX)/lib/$(lib_a) + [ -n "$(devlink)" ] && \ + rm -f $(DESTDIR)$(PREFIX)/lib/$(soname) && \ + rm -f $(DESTDIR)$(PREFIX)/lib/$(devlink) || \ + true diff -r 000000000000 -r 1873dfd13f2d src/3dschunks.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/3dschunks.h Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,160 @@ +#ifndef _3DSCHUNKS_H_ +#define _3DSCHUNKS_H_ + +enum ChunkID { + Chunk_Color_Float3 = 0x0010, // o Floating point color + Chunk_Color_Byte3 = 0x0011, // o 24bit color + Chunk_Color_GammaByte3 = 0x0012, // o 24bit gamma corrected + Chunk_Color_GammaFloat3 = 0x0013, // o Floating point gamma corrected + Chunk_PercentInt = 0x0030, // o Percent Chunk int 0 - 100 + Chunk_PercentFloat = 0x0031, // o Percent Chunk float 0 - 1 + + Chunk_3DSMain = 0x4D4D, // + Root Chunk + Chunk_Main_3DSVersion = 0x0002, // - 3DS Version + Chunk_Main_3DEditor = 0x3D3D, // + 3D Editor Chunk + Chunk_Edit_Unit = 0x0100, // - Unit + Chunk_Edit_BGBitmap = 0x1100, // - Background Bitmap + Chunk_Edit_UseBGBitmap = 0x1101, // - Use Background Bitmap + Chunk_Edit_BGColor = 0x1200, // - Background Color + Chunk_Edit_UseBGColor = 0x1201, // - Use Background Color + Chunk_Edit_GradColor = 0x1300, // - Background Gradient + Chunk_Edit_UseGradColor = 0x1301, // - Use Gradient Color + Chunk_Edit_ShadowMapBias = 0x1400, // - Shadow map bias + Chunk_Edit_ShadowMapSize = 0x1420, // - Shadow map size + Chunk_Edit_ShadowMapSampleRange = 0x1450, // - Shadow map sample range + Chunk_Edit_RaytraceBias = 0x1460, // - Raytrace bias + Chunk_Edit_UseRaytrace = 0x1470, // - Use Raytrace + Chunk_Edit_AmbientColor = 0x2100, // - Ambient Color + Chunk_Edit_Fog = 0x2200, // + Fog + Chunk_Fog_FogColor = 0x2210, // - Fog Color + Chunk_Edit_UseFog = 0x2201, // - Use Fog + Chunk_Edit_DistanceQue = 0x2300, // + Distance que + Chunk_Dist_DimBackground = 0x2310, // - Dim Background + Chunk_Edit_UseDistanceQue = 0x2301, // - Use distance que + Chunk_Edit_LayeredFogOptions = 0x2302, // - Layered fog options + Chunk_Edit_UseLayeredFog = 0x2303, // - Use Layered Fog + Chunk_Edit_MeshVersion = 0x3D3E, // - Mesh Version + + Chunk_Edit_Object = 0x4000, // + Object + Chunk_Obj_Hidden = 0x4010, // - Hidden + Chunk_Obj_DontCastShadows = 0x4012, // - Object doesn't cast shadows + Chunk_Obj_MatteObject = 0x4013, // - Matte + Chunk_Obj_ExternalProcessOn = 0x4015, // - External Process on (?) + Chunk_Obj_DontReceiveShadows = 0x4017, // - doesn't reseive shadows + Chunk_Obj_TriMesh = 0x4100, // + TriMesh + Chunk_TriMesh_VertexList = 0x4110, // - Vertex List + Chunk_TriMesh_FaceDesc = 0x4120, // + Faces description + Chunk_Face_Material = 0x4130, // - Face Materials* + Chunk_TriMesh_TexCoords = 0x4140, // - Texture Coordinates + Chunk_TriMesh_SmoothingGroup = 0x4150, // - Smoothing group + Chunk_TriMesh_WorldTransform = 0x4160, // - Position and Orientation + Chunk_TriMesh_Color = 0x4165, // - Object color + Chunk_TriMesh_ExternalProcessName = 0x4181, // - External Process name (?) + Chunk_TriMesh_ExternalProcessParams = 0x4182, // - External Process parameters (?) + + Chunk_Obj_Light = 0x4600, // + Light + Chunk_Light_SpotLight = 0x4610, // + SpotLight + Chunk_Spot_Raytrace = 0x4627, // - Raytrace + Chunk_Spot_CastShadows = 0x4630, // - Light casts shadows + Chunk_Spot_ShadowMap = 0x4641, // - Shadow Map + Chunk_Spot_ShowCone = 0x4650, // - Show Cone + Chunk_Spot_Rectangular = 0x4651, // - Rectangular shaped spotlight + Chunk_Spot_OverShoot = 0x4652, // - Overshoot + Chunk_Spot_ProjMap = 0x4653, // - Projector Map + Chunk_Spot_Roll = 0x4656, // - Roll around dir + Chunk_Spot_RaytraceBias = 0x4658, // - Raytrace Bias + Chunk_Light_Off = 0x4620, // - Light is disabled + Chunk_Light_Attenuation = 0x4625, // - Attenuation enabled + Chunk_Light_AttenuationStart = 0x4659, // - Attenuation Start Range + Chunk_Light_AttenuationEnd = 0x465A, // - Attenuation End Range + Chunk_Light_Intensity = 0x465B, // - Light Intensity + + Chunk_Obj_Camera = 0x4700, // - Camera + Chunk_Edit_ViewSettings = 0x7001, // - View Settings + Chunk_Edit_ViewDesc2 = 0x7011, // - View Description 2 + Chunk_Edit_ViewDesc1 = 0x7012, // - View Description 1 + Chunk_Edit_MeshWindows = 0x7020, // - Mesh Windows (?) + + Chunk_Edit_Material = 0xAFFF, // + Material Block + Chunk_Mat_Name = 0xA000, // - Material Name + Chunk_Mat_AmbientColor = 0xA010, // - Ambient Color + Chunk_Mat_DiffuseColor = 0xA020, // - Diffuse Color + Chunk_Mat_SpecularColor = 0xA030, // - Specular Color + Chunk_Mat_Specular = 0xA040, // - Shininness (Specular Power) + Chunk_Mat_SpecularIntensity = 0xA041, // - Shininness Strength (specular intensity) + Chunk_Mat_Transparency = 0xA050, // - Transparency (alpha) + Chunk_Mat_TransparencyFalloff = 0xA052, // - Transparency Falloff + Chunk_Mat_ReflectionBlur = 0xA053, // - Reflection Blur + Chunk_Mat_TwoSided = 0xA081, // - Two Sided + Chunk_Mat_AddTransparency = 0xA083, // - ? + Chunk_Mat_SelfIllumination = 0xA084, // - Self Illumination (emissive) + Chunk_Mat_Wireframe = 0xA085, // - Render in wireframe + Chunk_Mat_WireframeThickness = 0xA087, // - Wire thickness + Chunk_Mat_FaceMapping = 0xA088, // - Apply maps to faces seperatly (ignore uv) + Chunk_Mat_InTranc = 0xA08A, // ? + Chunk_Mat_Soften = 0xA08C, // - Soft Shading + Chunk_Mat_WireUnits = 0xA08E, // - Wire units (?) + Chunk_Mat_RenderType = 0xA100, // - Render Type + Chunk_Mat_BumpMapPercent = 0xA252, // - Bump map intensity + Chunk_Mat_TextureMap = 0xA200, // + Texture Map + Chunk_Mat_TextureMap2 = 0xA33A, // + Texture Map 2 + Chunk_Mat_OpacityMap = 0xA210, // + Opacity Map + Chunk_Mat_BumpMap = 0xA230, // + Bump Map + Chunk_Mat_SpecularMap = 0xA33C, // + Specular Intensity map + Chunk_Mat_SpecularColorMap = 0xA204, // + Specular color (texture) map + Chunk_Mat_SelfIlluminationMap = 0xA33D, // + Self Illumination Map + Chunk_Mat_ReflectionMap = 0xA220, // + Reflection Map + Chunk_Mat_TextureMask = 0xA33E, // - Texture Mask + Chunk_Mat_Texture2Mask = 0xA340, // - Texture 2 Mask + Chunk_Mat_OpacityMask = 0xA342, // - Opacity Mask + Chunk_Mat_BumpMask = 0xA344, // - Bump Mask + Chunk_Mat_SpecularMask = 0xA346, // - Specular Mask + Chunk_Mat_SpecularColorMask = 0xA348, // - Specular color mask + Chunk_Mat_SelfIlluminationMask = 0xA34A, // - Self Illumination mask + Chunk_Mat_ReflectionMask = 0xA34C, // - Reflection mask + + // map subchunks // ----------------------- + Chunk_Map_FileName = 0xA300, // - Filename + Chunk_Map_Params = 0xA351, // - Parameters + Chunk_Map_BlurPercent = 0xA353, // - Blur ammount + Chunk_Map_VScale = 0xA354, // - Texture V Scale + Chunk_Map_UScale = 0xA356, // - Texture U Scale + Chunk_Map_UOffset = 0xA358, // - Texture U Offset + Chunk_MAP_VOffset = 0xA35A, // - Texture V Offset + Chunk_Map_RotationAngle = 0xA35C, // - Texture Rotation Angle + Chunk_Map_RGBLumAlphaTint1 = 0xA360, // - RGB Luminance Alpha Tint 1 + Chunk_Map_RGBLumAlphaTint2 = 0xA362, // - RGB Luminance Alpha Tint 2 + Chunk_Map_RGBTintR = 0xA364, // - RGB Tint R + Chunk_Map_RGBTintG = 0xA366, // - RGB Tint G + Chunk_Map_RGBTintB = 0xA368, // - RGB Tint B + // map subchunks end // ----------------------- + + Chunk_Main_Keyframer = 0xB000, // + Keyframer Chunk + Chunk_Key_AmbientInfo = 0xB001, // - Ambient animation info + Chunk_Key_MeshInfo = 0xB002, // - Mesh animation info + Chunk_Key_CameraInfo = 0xB003, // - Camera animation info + Chunk_Key_CameraTargetInfo = 0xB004, // - Camera Target animation info + Chunk_Key_OmniLightInfo = 0xB005, // - Omni Light animation info + Chunk_Key_SpotLightTargetInfo = 0xB006, // - Spotlight target animation info + Chunk_Key_SpotLightInfo = 0xB007, // - Spotlight animation info + Chunk_Key_Frames = 0xB008, // - Animation Frames + + // animation information subchunks // ----------------------- + Chunk_Info_Object = 0xB010, // - Object information + Chunk_Info_ObjectPivot = 0xB013, // - Object Pivot + Chunk_Info_ObjectMorphAngle = 0xB015, // - Object Morph Angle + Chunk_Info_PositionTrack = 0xB020, // - Position Track + Chunk_Info_RotationTrack = 0xB021, // - Rotation Track + Chunk_Info_ScaleTrack = 0xB022, // - Scaling Track + Chunk_Info_FOVTrack = 0xB023, // - FOV Track + Chunk_Info_RollTrack = 0xB024, // - Roll Track + Chunk_Info_ColorTrack = 0xB025, // - Color Track + Chunk_Info_MorphTrack = 0xB026, // - Morph Track + Chunk_Info_HotSpotTrack = 0xB027, // - HotSpot Track + Chunk_Info_FalloffTrack = 0xB028, // - Falloff Track + Chunk_Info_HideTrack = 0xB029, // - Hide Track + Chunk_Info_HierarchyPosition = 0xB030 // - Hierarchy Position +}; + +#endif // _3DSCHUNKS_H_ + diff -r 000000000000 -r 1873dfd13f2d src/assload.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/assload.cc Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,437 @@ +#include +#include "assload.h" +#include "logger.h" +#include "datapath.h" + +#ifdef USE_ASSIMP + +#include +#include +#include "assimp/cimport.h" +#include "assimp/scene.h" +#include "assimp/postprocess.h" +#include "texman.h" +#include "material.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 000000000000 -r 1873dfd13f2d src/assload.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/assload.h Thu Nov 14 05:27:09 2013 +0200 @@ -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 000000000000 -r 1873dfd13f2d src/curve.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/curve.cc Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,316 @@ +#include +#include +#include "curve.h" +#include "opengl.h" +#include "shader.h" +#include "logger.h" + +#define DEF_THICKNESS 0.075 +#define DEF_SEGM_SUB 3 +#define DEF_RING_SUB 6 + +Curve::Curve() +{ + thickness = DEF_THICKNESS; + mesh_valid = false; + lengths_valid = false; + + bbox_valid = false; + + segm_subdiv = DEF_SEGM_SUB; + ring_subdiv = DEF_RING_SUB; +} + +Curve::Curve(const Vector3 *points, int num_points) +{ + thickness = DEF_THICKNESS; + mesh_valid = false; + lengths_valid = false; + + bbox_valid = false; + + segm_subdiv = DEF_SEGM_SUB; + ring_subdiv = DEF_RING_SUB; + + for(int i=0; iname = name; +} + +const char *Curve::get_name() const +{ + return name.c_str(); +} + +bool Curve::empty() const +{ + return cv.empty(); +} + +void Curve::set_thickness(float thickness) +{ + this->thickness = thickness; +} + +void Curve::set_subdiv(int seg, int ring) +{ + if(seg < 1) seg = 1; + if(ring < 3) ring = 3; + + segm_subdiv = seg; + ring_subdiv = ring; +} + +void Curve::clear() +{ + mesh_valid = false; + lengths_valid = false; + bbox_valid = false; + cv.clear(); +} + +void Curve::add_point(const Vector3 &pt) +{ + cv.push_back(pt); + mesh_valid = false; + lengths_valid = false; + bbox_valid = false; +} + +Vector3 &Curve::get_point(int idx) +{ + mesh_valid = false; + lengths_valid = false; + bbox_valid = false; + return cv[idx]; +} + +const Vector3 &Curve::get_point(int idx) const +{ + return cv[idx]; +} + +int Curve::get_count() const +{ + return (int)cv.size(); +} + +Vector3 &Curve::operator[] (int idx) +{ + return get_point(idx); +} + +const Vector3 &Curve::operator[] (int idx) const +{ + return get_point(idx); +} + +void Curve::get_bbox(Vector3 *bbmin, Vector3 *bbmax) const +{ + if(!bbox_valid) { + this->bbmin = Vector3(FLT_MAX, FLT_MAX, FLT_MAX); + this->bbmax = -this->bbmin; + + for(size_t i=0; ibbmin[j]) { + this->bbmin[j] = cv[i][j]; + } + if(cv[i][j] > this->bbmax[j]) { + this->bbmax[j] = cv[i][j]; + } + } + } + bbox_valid = true; + } + + if(bbmin) *bbmin = this->bbmin; + if(bbmax) *bbmax = this->bbmax; +} + +void Curve::normalize() +{ + get_bbox(0, 0); // force validation of the bounding box + + float len = (bbmax - bbmin).length() * 0.5; + if(len == 0.0) { + return; + } + + for(size_t i=0; i= 1.0) { + return cv.back(); + } + + t = reparametrize(t); + + int numcv = (int)cv.size(); + int idx0 = t * (numcv - 1); + int idx1 = idx0 + 1; + + int idx_prev = idx0 <= 0 ? idx0 : idx0 - 1; + int idx_next = idx1 >= numcv - 1 ? idx1 : idx1 + 1; + + float dt = 1.0 / (float)(numcv - 1); + + float t0 = (float)idx0 * dt; + float t1 = (float)idx1 * dt; + + t = (t - t0) / (t1 - t0); + if(t < 0.0) t = 0.0; + if(t > 1.0) t = 1.0; + + //return catmull_rom_spline(cv[idx_prev], cv[idx0], cv[idx1], cv[idx_next], t); + return bspline(cv[idx_prev], cv[idx0], cv[idx1], cv[idx_next], t); +} + +Vector3 Curve::operator() (float t) const +{ + return get_pos(t); +} + +void Curve::draw() const +{ + update_mesh(); + if(!mesh_valid) { + return; + } + + mesh.draw(); +} + + +float Curve::reparametrize(float t) const +{ + calc_cvlengths(); + return t; // TODO +} + +void Curve::calc_cvlengths() const +{ + if(lengths_valid || cv.empty()) { + return; + } + + length.clear(); + length.resize(cv.size()); + + length[0] = 0; + for(size_t i=1; i +#include +#include "vmath/vmath.h" +#include "mesh.h" + +class Curve { +private: + std::string name; + + std::vector cv; + float thickness; + + int segm_subdiv, ring_subdiv; + + /// normalized arc-lengths of each control vertex from the beginning + mutable std::vector length; + mutable bool lengths_valid; + + mutable Vector3 bbmin, bbmax; + mutable bool bbox_valid; + + mutable Mesh mesh; + mutable bool mesh_valid; + + float reparametrize(float t) const; + + void calc_cvlengths() const; + void update_mesh() const; + +public: + Curve(); + Curve(const Vector3 *points, int num_points); + Curve(const Vector2 *points, int num_points); + + void set_name(const char *name); + const char *get_name() const; + + bool empty() const; + + void set_thickness(float thickness); + void set_subdiv(int seg, int ring); + + void clear(); + void add_point(const Vector3 &pt); + Vector3 &get_point(int idx); + const Vector3 &get_point(int idx) const; + int get_count() const; + + Vector3 &operator[] (int idx); + const Vector3 &operator[] (int idx) const; + + void get_bbox(Vector3 *bbmin, Vector3 *bbmax) const; + void normalize(); + + Vector3 get_pos(float t) const; + Vector3 operator() (float t) const; + + void draw() const; +}; + +#endif // CURVE_H_ diff -r 000000000000 -r 1873dfd13f2d src/curveload.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/curveload.cc Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,195 @@ +#include +#include +#include +#include "curveload.h" +#include "3dschunks.h" +#include "logger.h" + +static uint32_t read_counter; + +struct ChunkHeader { + ChunkID id; + uint32_t size; +}; + +#define HEADER_SIZE 6 + +// local function prototypes +static uint8_t read_byte(FILE *file); +static uint16_t read_int16(FILE *file); +static uint32_t read_int32(FILE *file); +static float read_float(FILE *file); +static Vector3 read_vector(FILE *file, bool flip_yz = false); +static std::string read_string(FILE *file); +static ChunkHeader read_chunk_header(FILE *file); +static void skip_chunk(FILE *file, const ChunkHeader &chunk); +static Curve *read_curve(FILE *file, const ChunkHeader &ch); + +static bool eof; + +bool load_curves(Scene *scn, const char *fname) +{ + FILE *file = fopen(fname, "rb"); + if(!file) { + error_log("failed to open curves scene file: %s\n", fname); + return false; + } + eof = false; + + ChunkHeader chunk; + + chunk = read_chunk_header(file); + if(chunk.id != Chunk_3DSMain) { + fclose(file); + return false; + } + + while(!eof) { + chunk = read_chunk_header(file); + + Curve *curve; + + switch(chunk.id) { + case Chunk_Main_3DEditor: + break; // dont skip + + case Chunk_Edit_Object: + if((curve = read_curve(file, chunk))) { + scn->curves.push_back(curve); + } + break; + + default: + skip_chunk(file, chunk); + } + } + + fclose(file); + return true; +} + +static uint8_t read_byte(FILE *fp) { + uint8_t v; + if(fread(&v, 1, 1, fp) <= 0) { + eof = true; + return 0; + } + read_counter++; + return v; +} + +static uint16_t read_int16(FILE *fp) { + uint16_t v; + if(fread(&v, 2, 1, fp) <= 0) { + eof = true; + return 0; + } + read_counter += 2; + return v; +} + +static uint32_t read_int32(FILE *fp) { + uint32_t v; + if(fread(&v, 4, 1, fp) <= 0) { + eof = true; + return 0; + } + read_counter += 4; + return v; +} + +static float read_float(FILE *fp) +{ + int32_t tmp = read_int32(fp); + return *((float*)&tmp); +} + +static Vector3 read_vector(FILE *file, bool flip_yz) +{ + Vector3 vector; + vector.x = read_float(file); + if(!flip_yz) vector.y = read_float(file); + vector.z = read_float(file); + if(flip_yz) vector.y = read_float(file); + return vector; +} + +static std::string read_string(FILE *file) +{ + std::string str; + char c; + while((c = (char)read_byte(file))) { + str.push_back(c); + } + read_counter++; + + return str; +} + +static ChunkHeader read_chunk_header(FILE *file) +{ + ChunkHeader chunk; + chunk.id = (ChunkID)read_int16(file); + chunk.size = read_int32(file); + return chunk; +} + +static void skip_chunk(FILE *file, const ChunkHeader &chunk) +{ + if(eof) return; + fseek(file, chunk.size - HEADER_SIZE, SEEK_CUR); + read_counter += chunk.size - HEADER_SIZE; +} + +static Curve *read_curve(FILE *file, const ChunkHeader &ch) +{ + read_counter = HEADER_SIZE; // reset the global read counter + + std::string name = read_string(file); + + ChunkHeader chunk; + chunk = read_chunk_header(file); + if(chunk.id == Chunk_Obj_TriMesh) { + // object is a trimesh... load it + Vector3 *varray = 0; + uint32_t vertex_count = 0; + + uint32_t obj_chunk_size = ch.size; + + while(read_counter < obj_chunk_size) { // make sure we only read subchunks of this object chunk + chunk = read_chunk_header(file); + + switch(chunk.id) { + case Chunk_TriMesh_VertexList: + vertex_count = (uint32_t)read_int16(file); + varray = new Vector3[vertex_count]; + + for(uint32_t i=0; iset_name(name.c_str()); + for(uint32_t i=0; iadd_point(varray[i]); + } + return curve; + } + } + + return 0; +} diff -r 000000000000 -r 1873dfd13f2d src/curveload.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/curveload.h Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,8 @@ +#ifndef CURVELOAD_H_ +#define CURVELOAD_H_ + +#include "scene.h" + +bool load_curves(Scene *scn, const char *fname); + +#endif // CURVELOAD_H_ diff -r 000000000000 -r 1873dfd13f2d src/datapath.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/datapath.cc Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,67 @@ +#include +#include +#include +#include "logger.h" +#include "datapath.h" + +static std::set paths; + +void add_data_path(const char *path) +{ + paths.insert(path); +} + +#ifndef TARGET_IPHONE +std::string datafile_path(const char *fname) +{ + std::string res; + if(!fname) { + return res; + } + + std::set::const_iterator it = paths.begin(); + while(it != paths.end()) { + const std::string &path = *it++; + res = path + "/" + std::string(fname); + FILE *fp = fopen(res.c_str(), "r"); + if(fp) { + fclose(fp); + return res; + } + } + + // It's not found. Return the name itself just in case it's right here + return std::string(fname); +} +#else +#include + +std::string datafile_path(const char *fname) +{ + std::string res; + if(!fname) { + return res; + } + + CFBundleRef bundle; + CFURLRef url; + CFStringRef cfname; + + cfname = CFStringCreateWithCString(0, fname, kCFStringEncodingASCII); + + bundle = CFBundleGetMainBundle(); + if(!(url = CFBundleCopyResourceURL(bundle, cfname, 0, 0))) { + CFRelease(cfname); + return fname; + } + CFRelease(cfname); + + char path[1024]; + if(!CFURLGetFileSystemRepresentation(url, 1, (unsigned char*)path, sizeof path)) { + CFRelease(url); + return fname; + } + CFRelease(url); + return std::string(path); +} +#endif diff -r 000000000000 -r 1873dfd13f2d src/datapath.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/datapath.h Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,10 @@ +#ifndef DATAPATH_H_ +#define DATAPATH_H_ + +#include + +void add_data_path(const char *path); + +std::string datafile_path(const char *fname); + +#endif // DATAPATH_H_ diff -r 000000000000 -r 1873dfd13f2d src/dataset.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/dataset.h Thu Nov 14 05:27:09 2013 +0200 @@ -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 000000000000 -r 1873dfd13f2d src/dataset.inl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/dataset.inl Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,56 @@ +#include +#include +#include "datapath.h" + +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 = datafile_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 000000000000 -r 1873dfd13f2d src/geom.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/geom.cc Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,252 @@ +#include +#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::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::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::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 000000000000 -r 1873dfd13f2d src/geom.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/geom.h Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,67 @@ +#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 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 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 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 intersect(const Ray &ray, HitPoint *hit = 0) const; +}; + +#endif // GEOMOBJ_H_ diff -r 000000000000 -r 1873dfd13f2d src/gfxutil.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gfxutil.cc Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,64 @@ +#include +#include "gfxutil.h" +#include "mesh.h" +#include "meshgen.h" +#include "texture.h" +#include "shader.h" +#include "sdrman.h" +#include "opengl.h" +#include "logger.h" + +void draw_rect(const Vector3 &v1, const Vector3 &v2, Texture2D *tex, ShaderProg *sdr) +{ + static ShaderProg *defsdr; + + if(!defsdr) { + if(!(defsdr = get_sdrprog("defpost.v.glsl", "defpost.p.glsl"))) { + static bool didlog; + if(!didlog) { + error_log("draw_rect: failed to load default shader\n"); + didlog = true; + } + if(!sdr) { + return; + } + } + } + + + if(tex) { + set_texture(tex); + } + if(sdr) { + sdr->bind(); + } else { + defsdr->bind(); + + assert(defsdr->get_attrib_location("attr_vertex") == MESH_ATTR_VERTEX); + assert(defsdr->get_attrib_location("attr_texcoord") == MESH_ATTR_TEXCOORD); + } + + + float varr[] = { + v1.x, v1.y, v1.z, v2.x, v1.y, v2.z, + v2.x, v2.y, v2.z, v1.x, v2.y, v1.z + }; + static const float tarr[] = { 0, 0, 1, 0, 1, 1, 0, 1 }; + static const unsigned int idxarr[] = {0, 1, 2, 0, 2, 3}; + + // disable VBOs if they are enabled + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + glEnableVertexAttribArray(MESH_ATTR_VERTEX); + glVertexAttribPointer(MESH_ATTR_VERTEX, 3, GL_FLOAT, GL_FALSE, 0, varr); + glEnableVertexAttribArray(MESH_ATTR_TEXCOORD); + glVertexAttribPointer(MESH_ATTR_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0, tarr); + + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, idxarr); + + glDisableVertexAttribArray(MESH_ATTR_VERTEX); + glDisableVertexAttribArray(MESH_ATTR_TEXCOORD); + + set_texture(0); +} diff -r 000000000000 -r 1873dfd13f2d src/gfxutil.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gfxutil.h Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,11 @@ +#ifndef GFXUTIL_H_ +#define GFXUTIL_H_ + +#include + +class Texture2D; +class ShaderProg; + +void draw_rect(const Vector3 &v1, const Vector3 &v2, Texture2D *tex = 0, ShaderProg *sdr = 0); + +#endif // GFXUTIL_H_ diff -r 000000000000 -r 1873dfd13f2d src/image.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/image.cc Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,192 @@ +#include +#include "imago2.h" +#include "image.h" +#include "logger.h" + + +static int pixel_elements(Image::Format fmt); +static int elem_size(Image::Format fmt); +static int pixel_size(Image::Format fmt); + +Image::Image() +{ + fmt = FMT_RGBA; + width = height = 0; + pixels = 0; +} + +Image::~Image() +{ + delete [] (char*)pixels; +} + +int Image::get_width() const +{ + return width; +} + +int Image::get_height() const +{ + return height; +} + +Image::Format Image::get_format() const +{ + return fmt; +} + +bool Image::create(int x, int y, Format fmt) +{ + width = x; + height = y; + this->fmt = fmt; + + try { + pixels = new char[x * y * pixel_size(fmt)]; + } + catch(...) { + return false; + } + return true; +} + +bool Image::set_pixels(int x, int y, void *pixels, Format fmt) +{ + if(!create(x, y, fmt)) { + return false; + } + memcpy(this->pixels, pixels, x * y * pixel_size(fmt)); + return true; +} + +void *Image::get_pixels() const +{ + return pixels; +} + +bool Image::load(const char *fname) +{ + struct img_pixmap pixmap; + + img_init(&pixmap); + if(img_load(&pixmap, fname) == -1) { + return false; + } + + Format fmt; + switch(pixmap.fmt) { + case IMG_FMT_GREY8: + fmt = FMT_GREY; + break; + case IMG_FMT_RGB24: + fmt = FMT_RGB; + break; + case IMG_FMT_RGBA32: + fmt = FMT_RGBA; + break; + case IMG_FMT_GREYF: + fmt = FMT_GREY_FLOAT; + break; + case IMG_FMT_RGBF: + fmt = FMT_RGB_FLOAT; + break; + case IMG_FMT_RGBAF: + fmt = FMT_RGBA_FLOAT; + break; + default: + img_destroy(&pixmap); + return false; + } + + if(!set_pixels(pixmap.width, pixmap.height, pixmap.pixels, fmt)) { + img_destroy(&pixmap); + return false; + } + img_destroy(&pixmap); + return true; +} + +bool Image::save(const char *fname) const +{ + struct img_pixmap pixmap; + + img_init(&pixmap); + + switch(fmt) { + case FMT_GREY: + pixmap.fmt = IMG_FMT_GREY8; + break; + case FMT_GREY_FLOAT: + pixmap.fmt = IMG_FMT_GREYF; + break; + case FMT_RGB: + pixmap.fmt = IMG_FMT_RGB24; + break; + case FMT_RGB_FLOAT: + pixmap.fmt = IMG_FMT_RGBF; + break; + case FMT_RGBA: + pixmap.fmt = IMG_FMT_RGBA32; + break; + case FMT_RGBA_FLOAT: + pixmap.fmt = IMG_FMT_RGBAF; + break; + default: + return false; + } + + pixmap.width = width; + pixmap.height = height; + pixmap.pixels = pixels; + pixmap.pixelsz = pixel_size(fmt); + + if(img_save(&pixmap, fname) == -1) { + return false; + } + return true; +} + +static int pixel_elements(Image::Format fmt) +{ + switch(fmt) { + case Image::FMT_GREY: + case Image::FMT_GREY_FLOAT: + return 1; + + case Image::FMT_RGB: + case Image::FMT_RGB_FLOAT: + return 3; + + case Image::FMT_RGBA: + case Image::FMT_RGBA_FLOAT: + return 4; + + default: + break; + } + return 0; +} + +static int elem_size(Image::Format fmt) +{ + switch(fmt) { + case Image::FMT_GREY: + case Image::FMT_RGB: + case Image::FMT_RGBA: + return 1; + + case Image::FMT_GREY_FLOAT: + case Image::FMT_RGB_FLOAT: + case Image::FMT_RGBA_FLOAT: + return sizeof(float); + + default: + break; + } + return 0; +} + +static int pixel_size(Image::Format fmt) +{ + return elem_size(fmt) * pixel_elements(fmt); +} diff -r 000000000000 -r 1873dfd13f2d src/image.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/image.h Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,38 @@ +#ifndef IMAGE_H_ +#define IMAGE_H_ + + +class Image { +public: + enum Format { + FMT_GREY, + FMT_RGB, + FMT_RGBA, + FMT_GREY_FLOAT, + FMT_RGB_FLOAT, + FMT_RGBA_FLOAT + }; + +private: + Format fmt; + int width, height; + void *pixels; + +public: + Image(); + ~Image(); + + int get_width() const; + int get_height() const; + + Format get_format() const; + + bool create(int x, int y, Format fmt = FMT_RGBA); + bool set_pixels(int x, int y, void *pixels, Format fmt = FMT_RGBA); + void *get_pixels() const; + + bool load(const char *fname); + bool save(const char *fname) const; +}; + +#endif // IMAGE_H_ diff -r 000000000000 -r 1873dfd13f2d src/logger.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/logger.cc Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,96 @@ +#include +#include +#include "logger.h" + +#if defined(unix) || defined(__unix__) || defined(__APPLE__) +#include +#endif + +enum { LOG_INFO, LOG_WARNING, LOG_ERROR, 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_DEBUG) { + fflush(fp); + } +} + +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 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_WARNING: + return ANSI_FGCOLOR(YELLOW); + case LOG_DEBUG: + return ANSI_FGCOLOR(MAGENTA); + default: + break; + } + return 37; +} diff -r 000000000000 -r 1873dfd13f2d src/logger.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/logger.h Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,9 @@ +#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 debug_log(const char *fmt, ...); + +#endif // LOGGER_H_ diff -r 000000000000 -r 1873dfd13f2d src/material.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/material.cc Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,65 @@ +#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; ibind(tex_unit); + + if(tex_named_idx[i]) { + set_unistate(tex_named_idx[i], tex_unit); + } + set_unistate(st_tex_idx[i], tex_unit); + tex_unit++; + } + } +} diff -r 000000000000 -r 1873dfd13f2d src/material.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/material.h Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,29 @@ +#ifndef MATERIAL_H_ +#define MATERIAL_H_ + +#include "vmath/vmath.h" +#include "texture.h" + +enum { + TEX_DIFFUSE, + TEX_SPECULAR, + TEX_NORMAL, + TEX_MISC, + + MAX_MTL_TEXTURES +}; + +class Material { +public: + Vector3 diffuse, specular; + float alpha; + float shininess; + + Texture *tex[MAX_MTL_TEXTURES]; + + Material(); + + void setup(bool use_textures = true) const; +}; + +#endif // MATERIAL_H_ diff -r 000000000000 -r 1873dfd13f2d src/mesh.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mesh.cc Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,971 @@ +#include +#include +#include +#include +#include "opengl.h" +#include "mesh.h" +#include "xform_node.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 +{ + ((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); + + 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); + } + + for(int i=0; i= 0 && vattr[i].vbo_valid) { + glDisableVertexAttribArray(loc); + } + } +} + +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 000000000000 -r 1873dfd13f2d src/mesh.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mesh.h Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,211 @@ +#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 000000000000 -r 1873dfd13f2d src/meshgen.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/meshgen.cc Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,373 @@ +#include "meshgen.h" +#include "mesh.h" +#include "logger.h" + +// -------- sphere -------- + +#define SURAD(u) ((u) * 2.0 * M_PI) +#define SVRAD(v) ((v) * M_PI) + +static Vector3 sphvec(float theta, float phi) +{ + return Vector3(sin(theta) * sin(phi), + cos(phi), + cos(theta) * sin(phi)); +} + +void gen_sphere(Mesh *mesh, float rad, int usub, int vsub, float urange, float vrange) +{ + if(usub < 4) usub = 4; + if(vsub < 2) vsub = 2; + + int uverts = usub + 1; + int vverts = vsub + 1; + + int num_verts = uverts * vverts; + int num_quads = usub * vsub; + int num_tri = num_quads * 2; + + mesh->clear(); + Vector3 *varr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0); + Vector3 *narr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0); + Vector3 *tarr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0); + Vector2 *uvarr = (Vector2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0); + unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0); + + float du = urange / (float)(uverts - 1); + float dv = vrange / (float)(vverts - 1); + + float u = 0.0; + for(int i=0; iclear(); + Vector3 *varr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0); + Vector3 *narr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0); + Vector3 *tarr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0); + Vector2 *uvarr = (Vector2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0); + unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0); + + float du = urange / (float)(uverts - 1); + float dv = vrange / (float)(vverts - 1); + + float u = 0.0; + for(int i=0; iclear(); + Vector3 *varr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0); + Vector3 *narr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0); + Vector3 *tarr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0); + Vector2 *uvarr = (Vector2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0); + unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0); + + float du = urange / (float)(uverts - 1); + float dv = vrange / (float)(vverts - 1); + + float u = 0.0; + for(int i=0; iclear(); + + int uverts = usub + 1; + int vverts = vsub + 1; + int num_verts = uverts * vverts; + + int num_quads = usub * vsub; + int num_tri = num_quads * 2; + + Vector3 *varr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0); + Vector3 *narr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0); + Vector3 *tarr = (Vector3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0); + Vector2 *uvarr = (Vector2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0); + unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0); + + float du = 1.0 / (float)usub; + float dv = 1.0 / (float)vsub; + + float u = 0.0; + for(int i=0; i +#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 000000000000 -r 1873dfd13f2d src/object.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/object.h Thu Nov 14 05:27:09 2013 +0200 @@ -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 000000000000 -r 1873dfd13f2d src/opengl.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/opengl.cc Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,29 @@ +#include "opengl.h" + +void init_opengl() +{ +#ifdef __GLEW_H__ + glewInit(); +#endif +} + +const char *strglerr(int err) +{ + static const char *errnames[] = { + "GL_INVALID_ENUM", + "GL_INVALID_VALUE", + "GL_INVALID_OPERATION", + "GL_STACK_OVERFLOW", + "GL_STACK_UNDERFLOW", + "GL_OUT_OF_MEMORY", + "GL_INVALID_FRAMEBUFFER_OPERATION" + }; + + if(!err) { + return "GL_NO_ERROR"; + } + if(err < GL_INVALID_ENUM || err > GL_OUT_OF_MEMORY) { + return ""; + } + return errnames[err - GL_INVALID_ENUM]; +} diff -r 000000000000 -r 1873dfd13f2d src/opengl.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/opengl.h Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,71 @@ +#ifndef OPENGL_H_ +#define OPENGL_H_ + +#include + +#ifdef __APPLE__ +#include "TargetConditionals.h" + +#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR +/* iOS */ +#include +#include + +#define GL_CLAMP GL_CLAMP_TO_EDGE +#define GL_DEPTH24_STENCIL8 GL_DEPTH24_STENCIL8_OES + +#undef USE_OLDGL + +#define GL_WRITE_ONLY GL_WRITE_ONLY_OES +#define glMapBuffer glMapBufferOES +#define glUnmapBuffer glUnmapBufferOES + +#else +/* MacOS X */ +#include +#include + +#define USE_OLDGL +#endif + +#else +/* UNIX or Windows */ +#include +#include + +#define USE_OLDGL +#endif + +#ifndef GL_RGB16F +#define GL_RGB16F 0x881b +#endif +#ifndef GL_RGBA16F +#define GL_RGBA16F 0x881a +#endif +#ifndef GL_RGB32F +#define GL_RGB32F 0x8815 +#endif +#ifndef GL_RGBA32F +#define GL_RGBA32F 0x8814 +#endif +#ifndef GL_LUMINANCE16F +#define GL_LUMINANCE16F 0x881e +#endif +#ifndef GL_LUMINANCE32F +#define GL_LUMINANCE32F 0x8818 +#endif + +#define CHECKGLERR \ + do { \ + int err = glGetError(); \ + if(err) { \ + fprintf(stderr, "%s:%d: OpenGL error 0x%x: %s\n", __FILE__, __LINE__, err, strglerr(err)); \ + abort(); \ + } \ + } while(0) + +void init_opengl(); + +const char *strglerr(int err); + +#endif // OPENGL_H_ diff -r 000000000000 -r 1873dfd13f2d src/psyspp.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/psyspp.cc Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,459 @@ +#include +#include "psyspp.h" +#include "sdrman.h" +#include "logger.h" +#include "mesh.h" // just for the attrib enums +#include "unistate.h" +#include "datapath.h" +#include "texman.h" +#include "texgen.h" + +static void pdraw_start(const psys_emitter *em, void *cls); +static void pdraw(const psys_emitter *em, const psys_particle *part, void *cls); +static void pdraw_end(const psys_emitter *em, void *cls); + +static unsigned int psys_load_texture(const char *fname, void *cls); + +ParticleSystemAttributes::ParticleSystemAttributes() +{ + tex = 0; + own_psattr = true; + psattr = new psys_attributes; + psys_init_attr(psattr); +} + +ParticleSystemAttributes::ParticleSystemAttributes(psys_attributes *psattr) +{ + tex = 0; + own_psattr = false; + this->psattr = psattr; +} + +ParticleSystemAttributes::~ParticleSystemAttributes() +{ + if(own_psattr) { + psys_destroy_attr(psattr); + delete psattr; + } +} + +ParticleSystemAttributes::ParticleSystemAttributes(const ParticleSystemAttributes &rhs) +{ + own_psattr = true; + tex = rhs.tex; + psattr = new psys_attributes; + psys_init_attr(psattr); + + psys_copy_attr(psattr, rhs.psattr); +} + +ParticleSystemAttributes &ParticleSystemAttributes::operator =(const ParticleSystemAttributes &rhs) +{ + if(&rhs != this) { + tex = rhs.tex; + psys_copy_attr(psattr, rhs.psattr); + } + return *this; +} + +bool ParticleSystemAttributes::load(const char *fname) +{ + psys_texture_loader(psys_load_texture, 0, this); + return psys_load_attr(psattr, datafile_path(fname).c_str()) != -1; +} + +bool ParticleSystemAttributes::load(FILE *fp) +{ + psys_texture_loader(psys_load_texture, 0, this); + return psys_load_attr_stream(psattr, fp) != -1; +} + +bool ParticleSystemAttributes::save(const char *fname) const +{ + return psys_save_attr(psattr, fname) != -1; +} + +bool ParticleSystemAttributes::save(FILE *fp) const +{ + return psys_save_attr_stream(psattr, fp) != -1; +} + +void ParticleSystemAttributes::set_part_color(const Vector3 &color, float t) +{ + psys_set_value3(&psattr->part_attr.color, (anm_time_t)(t * 1000.0), v3_cons(color.x, color.y, color.z)); +} + +void ParticleSystemAttributes::set_part_alpha(float alpha, float t) +{ + psys_set_value(&psattr->part_attr.alpha, (anm_time_t)(t * 1000.0), alpha); +} + +void ParticleSystemAttributes::set_part_scale(float size, float t) +{ + psys_set_value(&psattr->part_attr.size, (anm_time_t)(t * 1000.0), size); +} + + +// emmiter attributes +void ParticleSystemAttributes::set_texture(Texture *tex) +{ + this->tex = tex; + psattr->tex = tex->get_id(); +} + +Texture *ParticleSystemAttributes::get_texture() const +{ + return tex; +} + +void ParticleSystemAttributes::set_spawn_range(const Vector3 &range, long tm) +{ + psys_set_value3(&psattr->spawn_range, ANM_MSEC2TM(tm), v3_cons(range.x, range.y, range.z)); +} + +void ParticleSystemAttributes::set_spawn_rate(float rate, long tm) +{ + psys_set_value(&psattr->rate, ANM_MSEC2TM(tm), rate); +} + +float ParticleSystemAttributes::get_spawn_rate(long tm) const +{ + return psys_get_value(&psattr->rate, ANM_MSEC2TM(tm)); +} + +void ParticleSystemAttributes::set_life(float life, float range, long tm) +{ + psys_set_anm_rnd(&psattr->life, ANM_MSEC2TM(tm), life, range); +} + +void ParticleSystemAttributes::set_size(float sz, float range, long tm) +{ + psys_set_anm_rnd(&psattr->size, ANM_MSEC2TM(tm), sz, range); +} + +void ParticleSystemAttributes::set_spawn_dir(const Vector3 &dir, const Vector3 &range, long tm) +{ + psys_set_anm_rnd3(&psattr->dir, ANM_MSEC2TM(tm), v3_cons(dir.x, dir.y, dir.z), v3_cons(range.x, range.y, range.z)); +} + +void ParticleSystemAttributes::set_gravity(const Vector3 &grav, long tm) +{ + psys_set_value3(&psattr->grav, ANM_MSEC2TM(tm), v3_cons(grav.x, grav.y, grav.z)); +} + +void ParticleSystemAttributes::set_drag(float drag) +{ + psattr->drag = drag; +} + +void ParticleSystemAttributes::set_particle_limit(int lim) +{ + psattr->max_particles = lim; +} + + +// ---- ParticleSystem ---- + +ParticleSystem::ParticleSystem() + : attr(&psys.attr) +{ + psys_init(&psys); + psys_draw_func(&psys, pdraw, pdraw_start, pdraw_end, (void*)this); + start_time = LONG_MIN; + last_upd_time = LONG_MIN; +} + +ParticleSystem::~ParticleSystem() +{ + psys_destroy(&psys); +} + +void ParticleSystem::set_start_time(long tm) +{ + start_time = tm; +} + +bool ParticleSystem::is_active() const +{ + float rate = attr.get_spawn_rate(last_upd_time); + return psys.pcount > 0 || last_upd_time == 0;// || rate > 0.0; +} + +ParticleSystemAttributes *ParticleSystem::get_attr() +{ + return &attr; +} + +const ParticleSystemAttributes *ParticleSystem::get_attr() const +{ + return &attr; +} + +void ParticleSystem::set_attr(const ParticleSystemAttributes &pattr) +{ + attr = pattr; +} + +bool ParticleSystem::load(const char *fname) +{ + psys_texture_loader(psys_load_texture, 0, &attr); + return attr.load(fname); +} + +bool ParticleSystem::save(const char *fname) const +{ + return attr.save(fname); +} + +void ParticleSystem::update(long tm) +{ + if(start_time > LONG_MIN) { + tm -= start_time; + } + + Matrix4x4 xform; + get_xform(tm, &xform); + + Vector3 pos = Vector3(0, 0, 0).transformed(xform); + + psys_set_pos(&psys, v3_cons(pos.x, pos.y, pos.z), 0); + psys_update(&psys, (double)tm / 1000.0); + + last_upd_time = tm; +} + +void ParticleSystem::draw() const +{ + psys_draw(&psys); +} + +// ---- particle drawing ---- +struct PVertex { + Vector4 color; + Vector3 pos; + Vector2 texcoord; +}; + + +#define USE_VBO +#define USE_IBO + + +#define MAX_DRAW_PART 256 +#define MAX_PVERTS (MAX_DRAW_PART * 4) +static PVertex *pvarr, *pvptr; + +// double-buffered vbo set, write on one, while drawing from the other + +#ifdef USE_VBO +static unsigned int vbo[2]; +static int cur_buf; // current write vbo +#endif +static int num_buffered; // number of particles bufferd, will flush when >= MAX_DRAW_PART + +// ok so the index array is constant, regardless of the particle system +// so this is a static index buffer created in init_particle_draw which should +// be called once. +#define MAX_PVIDX (MAX_DRAW_PART * 6) +#ifdef USE_IBO +static unsigned int ibo; +#endif +unsigned int *pvidx; + +static ShaderProg *psdr; // particle shader +static Texture2D *blank_tex; + +static inline void init_particle_draw() +{ + static bool done_init; + if(done_init) { + return; // once + } + + pvidx = new unsigned int[MAX_PVIDX]; + unsigned int *ptr = pvidx; + + static const unsigned int idxoffs[] = { 0, 1, 2, 0, 2, 3 }; + + for(int i=0; iset_image(*img); + delete img; + + done_init = true; +} + +static void pdraw_flush() +{ +#ifdef USE_VBO + // assuming vbo[cur_buf] is bound + glUnmapBuffer(GL_ARRAY_BUFFER); +#endif + + // draw from the bound buffer 6 indices per particle +#ifdef USE_IBO + glDrawElements(GL_TRIANGLES, num_buffered * 6, GL_UNSIGNED_INT, 0); +#else + glDrawElements(GL_TRIANGLES, num_buffered * 6, GL_UNSIGNED_INT, pvidx); +#endif + num_buffered = 0; + +#ifdef USE_VBO + // map the next buffer (write buffer) while the previous is drawing + cur_buf = (cur_buf + 1) & 1; + glBindBuffer(GL_ARRAY_BUFFER, vbo[cur_buf]); + pvarr = (PVertex*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); +#endif + pvptr = pvarr; +} + +static void pdraw_start(const psys_emitter *em, void *cls) +{ + ParticleSystem *ps = (ParticleSystem*)cls; + + init_particle_draw(); + + num_buffered = 0; + +#ifdef USE_IBO + // bind the particle index buffer which is static + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); +#endif + +#ifdef USE_VBO + // map the current write buffer + glBindBuffer(GL_ARRAY_BUFFER, vbo[cur_buf]); + pvarr = (PVertex*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); +#endif + pvptr = pvarr; + + Texture *tex = ps->get_attr()->get_texture(); + if(tex) { + tex->bind(); + } else { + blank_tex->bind(); + } + psdr->bind(); + + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + glDepthMask(0); + + glEnableVertexAttribArray(MESH_ATTR_VERTEX); + glEnableVertexAttribArray(MESH_ATTR_COLOR); + glEnableVertexAttribArray(MESH_ATTR_TEXCOORD); + +#ifdef USE_VBO + glVertexAttribPointer(MESH_ATTR_VERTEX, 3, GL_FLOAT, GL_FALSE, sizeof *pvarr, (void*)((char*)&pvarr->pos - (char*)pvarr)); + glVertexAttribPointer(MESH_ATTR_COLOR, 4, GL_FLOAT, GL_TRUE, sizeof *pvarr, (void*)((char*)&pvarr->color - (char*)pvarr)); + glVertexAttribPointer(MESH_ATTR_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof *pvarr, (void*)((char*)&pvarr->texcoord - (char*)pvarr)); +#else + glVertexAttribPointer(MESH_ATTR_VERTEX, 3, GL_FLOAT, GL_FALSE, sizeof *pvarr, (void*)&pvarr->pos); + glVertexAttribPointer(MESH_ATTR_COLOR, 4, GL_FLOAT, GL_TRUE, sizeof *pvarr, (void*)&pvarr->color); + glVertexAttribPointer(MESH_ATTR_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof *pvarr, (void*)&pvarr->texcoord); +#endif +} + +static void pdraw(const psys_emitter *em, const psys_particle *part, void *cls) +{ + ParticleSystem *ps = (ParticleSystem*)cls; + + static const Vector3 pv[] = { + Vector3(-0.5, -0.5, 0), + Vector3(0.5, -0.5, 0), + Vector3(0.5, 0.5, 0), + Vector3(-0.5, 0.5, 0) + }; + static const Vector2 tex[] = { + Vector2(0, 0), Vector2(0, 1), Vector2(1, 1), Vector2(1, 0) + }; + Vector4 color = Vector4(part->color.x, part->color.y, part->color.z, part->alpha); + + for(int i=0; i<4; i++) { + pvptr->color = color; + pvptr->pos = pv[i] * part->size + part->pos; + pvptr->texcoord = tex[i]; + pvptr++; + } + // XXX we don't need billboarding for this game, so don't bother + + // if we reached the maximum number of buffered particles, draw them + if(++num_buffered >= MAX_DRAW_PART) { + pdraw_flush(); // this will reset the counter + } +} + +static void pdraw_end(const psys_emitter *em, void *cls) +{ + // if we have leftover particles buffered, draw them before returning + if(num_buffered) { + pdraw_flush(); + } + + // cleanup + glDisableVertexAttribArray(MESH_ATTR_VERTEX); + glDisableVertexAttribArray(MESH_ATTR_COLOR); + glDisableVertexAttribArray(MESH_ATTR_TEXCOORD); + +#ifdef USE_VBO + glUnmapBuffer(GL_ARRAY_BUFFER); + glBindBuffer(GL_ARRAY_BUFFER, 0); +#endif +#ifdef USE_IBO + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +#endif + + glDepthMask(1); + glDisable(GL_BLEND); + + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); +} + +static unsigned int psys_load_texture(const char *fname, void *cls) +{ + ParticleSystemAttributes *attr = (ParticleSystemAttributes*)cls; + + Texture *tex = texset.get(fname); + if(tex) { + attr->set_texture(tex); + return tex->get_id(); + } + return 0; +} diff -r 000000000000 -r 1873dfd13f2d src/psyspp.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/psyspp.h Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,79 @@ +#ifndef PSYSPP_H_ +#define PSYSPP_H_ + +#include "xform_node.h" +#include "texture.h" +#include + +class ParticleSystemAttributes { +private: + Texture *tex; + psys_attributes *psattr; + bool own_psattr; + +public: + ParticleSystemAttributes(); + ParticleSystemAttributes(psys_attributes *psattr); + ~ParticleSystemAttributes(); + + ParticleSystemAttributes(const ParticleSystemAttributes &rhs); + ParticleSystemAttributes &operator =(const ParticleSystemAttributes &rhs); + + bool load(const char *fname); + bool load(FILE *fp); + bool save(const char *fname) const; + bool save(FILE *fp) const; + + // particle attributes + void set_part_color(const Vector3 &color, float t = 0); + void set_part_alpha(float alpha, float t = 0); + void set_part_scale(float size, float t = 0); + + // emmiter attributes + void set_texture(Texture *tex); + Texture *get_texture() const; + + void set_spawn_range(const Vector3 &range, long tm = 0); + + void set_spawn_rate(float rate, long tm = 0); + float get_spawn_rate(long tm = 0) const; + + void set_life(float life, float range = 0.0, long tm = 0); + void set_size(float sz, float range = 0.0, long tm = 0); + void set_spawn_dir(const Vector3 &dir, const Vector3 &range = Vector3(0, 0, 0), long tm = 0); + + void set_gravity(const Vector3 &grav, long tm = 0); + void set_drag(float drag); + + void set_particle_limit(int lim); +}; + +class ParticleSystem : public XFormNode { +private: + psys_emitter psys; + ParticleSystemAttributes attr; + + long start_time; // subtracted from all time calculations + long last_upd_time; + +public: + ParticleSystem(); + ~ParticleSystem(); + + void set_start_time(long tm); + + bool is_active() const; + + ParticleSystemAttributes *get_attr(); + const ParticleSystemAttributes *get_attr() const; + + void set_attr(const ParticleSystemAttributes &pattr); + + bool load(const char *fname); + bool save(const char *fname) const; + + void update(long tm); + void draw() const; +}; + +#endif // PSYSPP_H_ diff -r 000000000000 -r 1873dfd13f2d src/rtarg.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/rtarg.cc Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,259 @@ +#include +#include "rtarg.h" +#include "texture.h" +#include "logger.h" + +RenderTarget::RenderTarget() +{ + width = height = 0; + fbo = 0; + rbuf_zstencil = 0; + color_tex = 0; + tex_face = 0; + tex_targ = 0; +} + +RenderTarget::~RenderTarget() +{ + cleanup(); +} + +bool RenderTarget::create(unsigned int fmt) +{ + int vp[4]; + glGetIntegerv(GL_VIEWPORT, vp); + + return create(vp[2] - vp[0], vp[3] - vp[1], fmt); +} + +bool RenderTarget::create(int width, int height, unsigned int fmt) +{ + debug_log("RenderTarget::create(%d, %d)\n", width, height); + cleanup(); + + tex_targ = GL_TEXTURE_2D; + this->width = width; + this->height = height; + int tex_width = next_pow2(width); + int tex_height = next_pow2(height); + + CHECKGLERR; + color_tex = new Texture2D; + color_tex->create(tex_width, tex_height, fmt); + CHECKGLERR; + tex_face = 0; + + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + glBindTexture(GL_TEXTURE_2D, color_tex->get_id()); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color_tex->get_id(), 0); + glBindTexture(GL_TEXTURE_2D, 0); + + glGenRenderbuffers(1, &rbuf_zstencil); + glBindRenderbuffer(GL_RENDERBUFFER, rbuf_zstencil); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, tex_width, tex_height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbuf_zstencil); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbuf_zstencil); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + CHECKGLERR; + return true; +} + +bool RenderTarget::create(Texture *tex, int face) +{ + debug_log("RenderTarget::create(tex{%d, %d}, face:%d)\n", tex->get_size(0), + tex->get_size(1), face); + + tex_targ = GL_TEXTURE_2D; + if(dynamic_cast(tex)) { + if(face >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) { + tex_targ = face; + } else if(face >= 0 && face < 6) { + tex_targ = GL_TEXTURE_CUBE_MAP_POSITIVE_X + face; + } else { + error_log("invalid face (%d) passed to RenderTarget::create(TextureCube*, int)\n", face); + return false; + } + } + + cleanup(); + + width = tex->get_size(0); + height = tex->get_size(1); + + color_tex = tex; + + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + glBindTexture(tex_targ, color_tex->get_id()); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex_targ, color_tex->get_id(), 0); + glBindTexture(tex_targ, 0); + + glGenRenderbuffers(1, &rbuf_zstencil); + glBindRenderbuffer(GL_RENDERBUFFER, rbuf_zstencil); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbuf_zstencil); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbuf_zstencil); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + CHECKGLERR; + return true; +} + +void RenderTarget::cleanup() +{ + delete color_tex; + color_tex = 0; + + if(fbo) { + glDeleteFramebuffers(1, &fbo); + } + if(rbuf_zstencil) { + glDeleteRenderbuffers(1, &rbuf_zstencil); + } + + fbo = rbuf_zstencil = 0; + width = height = 0; + tex_face = 0; + tex_targ = 0; +} + +bool RenderTarget::resize(int width, int height) +{ + this->width = width; + this->height = height; + int tex_width = next_pow2(width); + int tex_height = next_pow2(height); + + debug_log("resizing render target (fbo %u): %dx%d [%dx%d]\n", fbo, width, height, tex_width, tex_height); + + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + color_tex->create(tex_width, tex_height, color_tex->get_format()); + color_tex->bind(); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex_targ, color_tex->get_id(), 0); + glBindTexture(tex_targ, 0); + + glBindRenderbuffer(GL_RENDERBUFFER, rbuf_zstencil); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, tex_width, tex_height); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + return true; +} + +int RenderTarget::get_width() const +{ + return width; +} + +int RenderTarget::get_height() const +{ + return height; +} + +Texture *RenderTarget::get_texture() const +{ + return color_tex; +} + +Matrix4x4 RenderTarget::get_texture_matrix() const +{ + float sx = (float)width / (float)color_tex->get_size(0); + float sy = (float)height / (float)color_tex->get_size(1); + + // counting on RVO to optimize this away + return Matrix4x4(sx, 0, 0, 0, + 0, sy, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); +} + + +static const char *fbstname[] = { + "GL_FRAMEBUFFER_COMPLETE", + "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT", + "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT", + "no such fbo error", + "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS", + "GL_FRAMEBUFFER_INCOMPLETE_FORMATS", + "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER", + "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER", + "GL_FRAMEBUFFER_UNSUPPORTED" +}; + +bool RenderTarget::check() const +{ + bool res = true; + +#ifndef GL_ES_VERSION_2_0 + int prev_fb = 0; + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &prev_fb); +#endif + + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + int status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if(status != GL_FRAMEBUFFER_COMPLETE) { + error_log("RenderTarget::check: incomplete FBO %u: %s\n", fbo, + fbstname[status - GL_FRAMEBUFFER_COMPLETE]); + res = false; + goto end; + } + +end: +#ifndef GL_ES_VERSION_2_0 + glBindFramebuffer(GL_FRAMEBUFFER, prev_fb); +#endif + return res; +} + +void RenderTarget::bind() const +{ + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + glViewport(0, 0, width, height); +} + +struct Viewport { int vp[4]; }; +static std::stack vpstack; + +void set_render_target(const RenderTarget *rtarg) +{ + Viewport vp; + + if(rtarg) { + glGetIntegerv(GL_VIEWPORT, vp.vp); + vpstack.push(vp); + + rtarg->bind(); + } else { +#ifdef GL_ES_VERSION_2_0 + extern unsigned int default_fbo; + glBindFramebuffer(GL_FRAMEBUFFER, default_fbo); +#else + glBindFramebuffer(GL_FRAMEBUFFER, 0); +#endif + + if(vpstack.empty()) { + return; + } + vp = vpstack.top(); + vpstack.pop(); + glViewport(vp.vp[0], vp.vp[1], vp.vp[2], vp.vp[3]); + } +} + +int next_pow2(int x) +{ + x--; + x = (x >> 1) | x; + x = (x >> 2) | x; + x = (x >> 4) | x; + x = (x >> 8) | x; + x = (x >> 16) | x; + return x + 1; +} + diff -r 000000000000 -r 1873dfd13f2d src/rtarg.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/rtarg.h Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,49 @@ +#ifndef RTARG_H_ +#define RTARG_H_ + +#include "vmath/vmath.h" +#include "opengl.h" + +class Texture; + +class RenderTarget { +private: + int width, height; + + unsigned int fbo; + Texture *color_tex; + unsigned int tex_targ; + int tex_face; + unsigned int rbuf_zstencil; + +public: + RenderTarget(); + ~RenderTarget(); + + bool create(unsigned int fmt = GL_RGBA); + bool create(int width, int height, unsigned int fmt = GL_RGBA); + bool create(Texture *tex, int face = 0); + + void cleanup(); + + bool resize(int width, int height); + + int get_width() const; + int get_height() const; + + Texture *get_texture() const; + + /** calculates a texture matrix to map the full texture space + * onto the part of the texture occupied by the render target + */ + Matrix4x4 get_texture_matrix() const; + + bool check() const; + void bind() const; +}; + +void set_render_target(const RenderTarget *rtarg); + +int next_pow2(int x); + +#endif // RTARG_H_ diff -r 000000000000 -r 1873dfd13f2d src/scene.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/scene.cc Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,39 @@ +#include "scene.h" +#include "opengl.h" + +Scene::~Scene() +{ + destroy(); +} + +void Scene::destroy() +{ + for(size_t i=0; idraw(msec); + } + + if(objects.empty()) { + printf("rendering meshes directly\n"); + for(size_t i=0; idraw(); + } + } +} diff -r 000000000000 -r 1873dfd13f2d src/scene.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/scene.h Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,20 @@ +#ifndef SCENE_H_ +#define SCENE_H_ + +#include +#include "object.h" +#include "curve.h" + +class Scene { +public: + std::vector objects; + std::vector meshes; + std::vector curves; + + ~Scene(); + void destroy(); + + void draw(long msec = 0) const; +}; + +#endif // SCENE_H_ diff -r 000000000000 -r 1873dfd13f2d src/sdrman.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/sdrman.cc Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,27 @@ +#include "sdrman.h" + +ShaderSet vsdrset(GL_VERTEX_SHADER); +ShaderSet psdrset(GL_FRAGMENT_SHADER); + +ShaderProg *get_sdrprog(const char *vname, const char *pname) +{ + Shader *vsdr = vname ? vsdrset.get(vname) : 0; + Shader *psdr = pname ? psdrset.get(pname) : 0; + + if(vname && !vsdr) { + return 0; + } + if(pname && !psdr) { + return 0; + } + if(!vsdr && !psdr) { + return 0; + } + + ShaderProg *prog = new ShaderProg; + if(!prog->create(vsdr, psdr)) { + delete prog; + return 0; + } + return prog; +} diff -r 000000000000 -r 1873dfd13f2d src/sdrman.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/sdrman.h Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,11 @@ +#ifndef SDRMAN_H_ +#define SDRMAN_H_ + +#include "shader.h" + +extern ShaderSet vsdrset; +extern ShaderSet psdrset; + +ShaderProg *get_sdrprog(const char *vname, const char *pname); + +#endif // SDRMAN_H_ diff -r 000000000000 -r 1873dfd13f2d src/shader.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/shader.cc Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,641 @@ +#include +#include +#include +#include +#include "opengl.h" +#include "shader.h" +#include "logger.h" +#include "unistate.h" +#include "mesh.h" + +#ifdef _MSC_VER +#include +#else +#include +#endif + +#ifdef __GLEW_H__ +#define HAVE_GEOMETRY_SHADER +#define HAVE_TESSELATION_SHADER +#endif + +static void bind_standard_attr(const ShaderProg *prog); +static const char *strtype(unsigned int type); + +ShaderProg *ShaderProg::current; + +Shader::Shader() +{ + sdr = type = 0; + name = 0; +} + +Shader::~Shader() +{ + destroy(); +} + +unsigned int Shader::get_id() const +{ + return sdr; +} + +void Shader::set_name(const char *name) +{ + delete [] this->name; + this->name = new char[strlen(name) + 1]; + strcpy(this->name, name); +} + +const char *Shader::get_name() const +{ + return name; +} + +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 + + if(!sdr) { + sdr = glCreateShader(type); + } + + info_log("compiling shader: %s... ", name ? name : ""); + + 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; + + delete [] name; + name = 0; +} + +bool Shader::load(const char *fname, unsigned int type) +{ + FILE *fp; + + if(!(fp = fopen(fname, "rb"))) { + error_log("failed to load %s shader: %s: %s\n", strtype(type), fname, strerror(errno)); + return false; + } + + 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 %s shader: %s: %s\n", strtype(type), fname, strerror(errno)); + fclose(fp); + return false; + } + src[sz] = 0; + fclose(fp); + + set_name(fname); + return create(src, 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; jset_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 000000000000 -r 1873dfd13f2d src/shader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/shader.h Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,135 @@ +#ifndef SHADER_H_ +#define SHADER_H_ + +#include +#include "vmath/vmath.h" +#include "opengl.h" +#include "dataset.h" + +class Shader { +private: + unsigned int sdr; + unsigned int type; + char *name; + +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); +}; + +#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 000000000000 -r 1873dfd13f2d src/texgen.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/texgen.cc Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,137 @@ +#include "texgen.h" + +static void intcolor(const Vector4 &color, int *icol); + +Image *texgen_solid(int xsz, int ysz, const Vector4 &color) +{ + Image *img = new Image; + if(!img->create(xsz, ysz, Image::FMT_RGBA)) { + delete img; + return 0; + } + + int col[4]; + intcolor(color, col); + + unsigned char *pix = (unsigned char*)img->get_pixels(); + for(int i=0; icreate(xsz, ysz, Image::FMT_RGBA)) { + delete img; + return 0; + } + + int c1[4], c2[4]; + intcolor(col1, c1); + intcolor(col2, c2); + + int udiv = xsz / usub; + int vdiv = ysz / vsub; + + unsigned char *pix = (unsigned char*)img->get_pixels(); + for(int i=0; icreate(xsz, ysz, Image::FMT_RGBA)) { + delete img; + return 0; + } + + unsigned char *pix = (unsigned char*)img->get_pixels(); + for(int i=0; ioctaves) * 0.5 + 0.5; + return lerp(arg->col1, arg->col2, noise); +} + +static Vector4 fbm_abs_eval(float x, float y, void *cls) +{ + NoiseArg *arg = (NoiseArg*)cls; + + float noise = turbulence2(x, y, arg->octaves) * 0.5 + 0.5; + return lerp(arg->col1, arg->col2, noise); +} + + +Image *texgen_fbm(int xsz, int ysz, float usize, float vsize, int octaves, const Vector4 &col1, const Vector4 &col2) +{ + NoiseArg arg = {octaves, col1, col2}; + if(arg.octaves < 1) { + arg.octaves = 1; + } + + return texgen(xsz, ysz, usize, vsize, fbm_eval, &arg); +} + +Image *texgen_fbm_abs(int xsz, int ysz, float usize, float vsize, int octaves, const Vector4 &col1, const Vector4 &col2) +{ + NoiseArg arg = {octaves, col1, col2}; + if(arg.octaves < 1) { + arg.octaves = 1; + } + + return texgen(xsz, ysz, usize, vsize, fbm_abs_eval, &arg); +} + +static inline void intcolor(const Vector4 &color, int *icol) +{ + for(int i=0; i<4; i++) { + icol[i] = std::max(std::min((int)(color[i] * 255.0), 255), 0); + } +} diff -r 000000000000 -r 1873dfd13f2d src/texgen.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/texgen.h Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,14 @@ +#ifndef TEXGEN_H_ +#define TEXGEN_H_ + +#include "image.h" +#include "vmath/vmath.h" + +Image *texgen_solid(int xsz, int ysz, const Vector4 &color); +Image *texgen_chess(int xsz, int ysz, int usub, int vsub, const Vector4 &col1, const Vector4 &col2); +Image *texgen_fbm(int xsz, int ysz, float usize, float vsize, int octaves, const Vector4 &col1, const Vector4 &col2); +Image *texgen_fbm_abs(int xsz, int ysz, float usize, float vsize, int octaves, const Vector4 &col1, const Vector4 &col2); + +Image *texgen(int xsz, int ysz, float usize, float vsize, Vector4 (*eval)(float, float, void*), void *cls); + +#endif // TEXGEN_H_ diff -r 000000000000 -r 1873dfd13f2d src/texman.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/texman.cc Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,3 @@ +#include "texman.h" + +TextureSet texset; diff -r 000000000000 -r 1873dfd13f2d src/texman.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/texman.h Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,8 @@ +#ifndef TEXMAN_H_ +#define TEXMAN_H_ + +#include "texture.h" + +extern TextureSet texset; + +#endif // TEXMAN_H_ diff -r 000000000000 -r 1873dfd13f2d src/texture.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/texture.cc Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,370 @@ +#include "texture.h" +#include "image.h" +#include "opengl.h" +#include "imago2.h" +#include "logger.h" +#include "datapath.h" + +static int glifmt_from_ifmt(unsigned int ifmt); +static int glfmt_from_ifmt(unsigned int ifmt); +static int gltype_from_ifmt(unsigned int ifmt); + +static int glifmt_from_imgfmt(Image::Format fmt); + +static unsigned int cur_target[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 +}; + +void set_texture(Texture *tex, int tunit) +{ + if(tex) { + tex->bind(tunit); + } else { + glActiveTexture(GL_TEXTURE0 + tunit); + glBindTexture(cur_target[tunit], 0); + glActiveTexture(GL_TEXTURE0); + } +} + +Texture *load_texture(const char *fname) +{ + TextureCube *texcube = new TextureCube; + if(texcube->load(fname)) { + return texcube; + } + delete texcube; + + Texture2D *tex = new Texture2D; + if(tex->load(fname)) { + return tex; + } + delete tex; + return 0; +} + + +Texture::Texture() +{ + target = 0; + sz[0] = sz[1] = sz[2] = 0; + texfmt = 0; + + glGenTextures(1, &id); +} + +Texture::~Texture() +{ + if(id) { + glDeleteTextures(1, &id); + } +} + +void Texture::set_wrapping(unsigned int wrap) +{ + if(!target) { + return; + } + + glBindTexture(target, id); + glTexParameteri(target, GL_TEXTURE_WRAP_S, wrap); + glTexParameteri(target, GL_TEXTURE_WRAP_T, wrap); +} + +void Texture::set_filtering(unsigned int filt) +{ + unsigned int mag_filter; + + if(!target) { + return; + } + + switch(filt) { + case GL_LINEAR_MIPMAP_NEAREST: + case GL_LINEAR_MIPMAP_LINEAR: + mag_filter = GL_LINEAR; + break; + + case GL_NEAREST_MIPMAP_NEAREST: + case GL_NEAREST_MIPMAP_LINEAR: + mag_filter = GL_NEAREST; + break; + + default: + mag_filter = filt; + } + + set_filtering(filt, mag_filter); +} + +void Texture::set_filtering(unsigned int min_filt, unsigned int mag_filt) +{ + glBindTexture(target, id); + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, min_filt); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, mag_filt); +} + +unsigned int Texture::get_format() const +{ + return texfmt; +} + +int Texture::get_size(int dim) const +{ + if(dim < 0 || dim >= 3) { + return 0; + } + return sz[dim]; +} + +unsigned int Texture::get_id() const +{ + return id; +} + +void Texture::bind(int tex_unit) const +{ + glActiveTexture(GL_TEXTURE0 + tex_unit); + glBindTexture(target, id); + glActiveTexture(GL_TEXTURE0); + + cur_target[tex_unit] = target; +} + + +// ---- Texture2D ---- + +Texture2D::Texture2D() +{ + target = GL_TEXTURE_2D; +} + +void Texture2D::create(int xsz, int ysz, unsigned int ifmt) +{ + int fmt = glfmt_from_ifmt(ifmt); + int type = gltype_from_ifmt(ifmt); + + glBindTexture(GL_TEXTURE_2D, id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, glifmt_from_ifmt(ifmt), xsz, ysz, 0, fmt, type, 0); + CHECKGLERR; + sz[0] = xsz; + sz[1] = ysz; + texfmt = ifmt; +} + +void Texture2D::set_image(const Image &img, int idx) +{ + texfmt = glifmt_from_imgfmt(img.get_format()); + unsigned int fmt = glfmt_from_ifmt(texfmt); + unsigned int type = gltype_from_ifmt(texfmt); + + sz[0] = img.get_width(); + sz[1] = img.get_height(); + + glBindTexture(GL_TEXTURE_2D, id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + +#ifdef __GLEW_H__ + if(GLEW_SGIS_generate_mipmap) { + glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE); +#endif + glTexImage2D(GL_TEXTURE_2D, 0, texfmt, sz[0], sz[1], 0, fmt, type, img.get_pixels()); +#ifdef __GLEW_H__ + } else { + gluBuild2DMipmaps(GL_TEXTURE_2D, texfmt, sz[0], sz[1], fmt, type, img.get_pixels()); + } +#endif + +#ifdef GL_ES_VERSION_2_0 + glGenerateMipmap(GL_TEXTURE_2D); +#endif +} + +bool Texture2D::load(const char *fname) +{ + Image img; + if(!img.load(fname) == -1) { + error_log("failed to load 2D texture: %s\n", fname); + return false; + } + set_image(img); + + info_log("loaded 2D texture: %s\n", fname); + return true; +} + +bool Texture2D::save(const char *fname) const +{ +#ifndef GL_ES_VERSION_2_0 + unsigned char *pixels = new unsigned char[sz[0] * sz[1] * 4]; + + glBindTexture(GL_TEXTURE_2D, id); + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + if(img_save_pixels(fname, pixels, sz[0], sz[1]) == -1) { + error_log("failed to save 2D texture: %s\n", fname); + delete [] pixels; + return false; + } + + info_log("saved 2D texture: %s\n", fname); + delete [] pixels; + return true; +#else + return false; // TODO +#endif +} + +// ---- TextureCube ---- +static unsigned int cube_faces[] = { + GL_TEXTURE_CUBE_MAP_POSITIVE_X, + GL_TEXTURE_CUBE_MAP_NEGATIVE_X, + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + GL_TEXTURE_CUBE_MAP_POSITIVE_Z, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z +}; + +TextureCube::TextureCube() +{ + target = GL_TEXTURE_CUBE_MAP; +} + +void TextureCube::create(int xsz, int ysz, unsigned int ifmt) +{ + if(xsz != ysz) { + error_log("trying to create cubemap with different width and height (%dx%d)\n", xsz, ysz); + return; + } + + texfmt = ifmt; + + glBindTexture(GL_TEXTURE_CUBE_MAP, id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + for(int i=0; i<6; i++) { + glTexImage2D(cube_faces[i], 0, ifmt, xsz, ysz, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); + } +} + +void TextureCube::set_image(const Image &img, int idx) +{ + // TODO +} + +bool TextureCube::load(const char *fname) +{ + return false; // TODO +} + +bool TextureCube::save(const char *fname) const +{ + return false; // TODO +} + +static int glifmt_from_ifmt(unsigned int ifmt) +{ +#ifdef GL_ES_VERSION_2_0 + switch(ifmt) { + case GL_LUMINANCE16F: + case GL_LUMINANCE32F: + ifmt = GL_LUMINANCE; + break; + + case GL_RGB16F: + case GL_RGB32F: + ifmt = GL_RGB; + break; + + case GL_RGBA16F: + case GL_RGBA32F: + ifmt = GL_RGBA; + break; + + default: + break; + } +#endif + return ifmt; // by default just pass it through... +} + +static int glfmt_from_ifmt(unsigned int ifmt) +{ + switch(ifmt) { + case GL_LUMINANCE16F: + case GL_LUMINANCE32F: + return GL_LUMINANCE; + + case GL_RGB16F: + case GL_RGB32F: + return GL_RGB; + + case GL_RGBA16F: + case GL_RGBA32F: + return GL_RGBA; + + default: + break; + } + return ifmt; +} + +static int gltype_from_ifmt(unsigned int ifmt) +{ + switch(ifmt) { + case GL_RGB16F: + case GL_RGBA16F: + case GL_LUMINANCE16F: +#ifdef GL_ES_VERSION_2_0 + return GL_HALF_FLOAT_OES; +#endif + case GL_RGB32F: + case GL_RGBA32F: + case GL_LUMINANCE32F: + return GL_FLOAT; + + default: + break; + } + return GL_UNSIGNED_BYTE; +} + +static int glifmt_from_imgfmt(Image::Format fmt) +{ + switch(fmt) { + case Image::FMT_GREY: + return GL_LUMINANCE; + case Image::FMT_GREY_FLOAT: + return GL_LUMINANCE16F; + case Image::FMT_RGB: + return GL_RGB; + case Image::FMT_RGB_FLOAT: + return GL_RGB16F; + case Image::FMT_RGBA: + return GL_RGBA; + case Image::FMT_RGBA_FLOAT: + return GL_RGBA16F; + default: + break; + } + return 0; +} + +// ---- TextureSet ---- +static void destroy_texture(Texture *tex) +{ + delete tex; +} + +TextureSet::TextureSet() + : DataSet(load_texture, destroy_texture) +{ +} diff -r 000000000000 -r 1873dfd13f2d src/texture.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/texture.h Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,76 @@ +#ifndef TEXTURE_H_ +#define TEXTURE_H_ + +#include "dataset.h" +#include "opengl.h" + +class Image; + +class Texture { +protected: + unsigned int id; + unsigned int target; + unsigned int texfmt; + int sz[3]; + + Texture(const Texture &tex) {} + Texture &operator =(const Texture &tex) { return *this; } + +public: + Texture(); + virtual ~Texture(); + + void set_wrapping(unsigned int wrap); + void set_filtering(unsigned int filt); + void set_filtering(unsigned int min_filt, unsigned int mag_filt); + + unsigned int get_format() const; + + virtual int get_size(int dim) const; + + virtual void create(int xsz, int ysz, unsigned int ifmt = GL_RGBA) = 0; + virtual void set_image(const Image &img, int idx = 0) = 0; + + virtual bool load(const char *fname) = 0; + virtual bool save(const char *fname) const = 0; + + virtual unsigned int get_id() const; + + virtual void bind(int tex_unit = 0) const; +}; + +class Texture2D : public Texture { +public: + Texture2D(); + + virtual void create(int xsz, int ysz, unsigned int ifmt = GL_RGBA); + virtual void set_image(const Image &img, int idx = 0); + + virtual bool load(const char *fname); + virtual bool save(const char *fname) const; +}; + +class TextureCube : public Texture { +public: + TextureCube(); + + virtual void create(int xsz, int ysz, unsigned int ifmt = GL_RGBA); + virtual void set_image(const Image &img, int idx = 0); + + virtual bool load(const char *fname); + virtual bool save(const char *fname) const; +}; + +void set_texture(Texture *tex, int tunit = 0); + +/** loads a texture autodetecting whether it's a 2D texture or + * cubemap and creating the correct Texture subclass instance. + */ +Texture *load_texture(const char *fname); + +class TextureSet : public DataSet { +public: + TextureSet(); +}; + +#endif // TEXTURE_H_ diff -r 000000000000 -r 1873dfd13f2d src/timer.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/timer.cc Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,118 @@ +#include "timer.h" + +#if defined(__APPLE__) && !defined(__unix__) +#define __unix__ +#endif + +#ifdef __unix__ +#include +#include +#include + +#ifdef CLOCK_MONOTONIC +unsigned long get_time_msec(void) +{ + struct timespec ts; + static struct timespec ts0; + + clock_gettime(CLOCK_MONOTONIC, &ts); + if(ts0.tv_sec == 0 && ts0.tv_nsec == 0) { + ts0 = ts; + return 0; + } + return (ts.tv_sec - ts0.tv_sec) * 1000 + (ts.tv_nsec - ts0.tv_nsec) / 1000000; +} +#else /* no fancy POSIX clocks, fallback to good'ol gettimeofday */ +unsigned long get_time_msec(void) +{ + struct timeval tv; + static struct timeval tv0; + + gettimeofday(&tv, 0); + if(tv0.tv_sec == 0 && tv0.tv_usec == 0) { + tv0 = tv; + return 0; + } + return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000; +} +#endif /* !posix clock */ + +void sleep_msec(unsigned long msec) +{ + usleep(msec * 1000); +} +#endif + +#ifdef WIN32 +#include +#pragma comment(lib, "winmm.lib") + +unsigned long get_time_msec(void) +{ + return timeGetTime(); +} + +void sleep_msec(unsigned long msec) +{ + Sleep(msec); +} +#endif + +double get_time_sec(void) +{ + return get_time_msec() / 1000.0f; +} + +void sleep_sec(double sec) +{ + if(sec > 0.0f) { + sleep_msec(sec * 1000.0f); + } +} + + +Timer::Timer() +{ + reset(); +} + +void Timer::reset() +{ + pause_time = 0; + start_time = get_time_msec(); +} + +void Timer::start() +{ + if(!is_running()) { + // resuming + start_time += get_time_msec() - pause_time; + pause_time = 0; + } +} + +void Timer::stop() +{ + if(is_running()) { + pause_time = get_time_msec(); + } +} + +bool Timer::is_running() const +{ + return pause_time == 0; +} + +unsigned long Timer::get_msec() const +{ + if(!is_running()) { + // in paused state... + return pause_time - start_time; + } + return get_time_msec() - start_time; +} + +double Timer::get_sec() const +{ + return (double)get_msec() / 1000.0; +} diff -r 000000000000 -r 1873dfd13f2d src/timer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/timer.h Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,29 @@ +#ifndef TIMER_H_ +#define TIMER_H_ + +unsigned long get_time_msec(void); +void sleep_msec(unsigned long msec); + +double get_time_sec(void); +void sleep_sec(double sec); + + +class Timer { +private: + unsigned long start_time, pause_time; + +public: + Timer(); + + void reset(); + + void start(); + void stop(); + + bool is_running() const; + + unsigned long get_msec() const; + double get_sec() const; +}; + +#endif // TIMER_H_ diff -r 000000000000 -r 1873dfd13f2d src/unistate.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/unistate.cc Thu Nov 14 05:27:09 2013 +0200 @@ -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 000000000000 -r 1873dfd13f2d src/unistate.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/unistate.h Thu Nov 14 05:27:09 2013 +0200 @@ -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 000000000000 -r 1873dfd13f2d src/xform_node.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xform_node.cc Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,370 @@ +#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); +} + +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; +} + +void XFormNode::add_child(XFormNode *child) +{ + children.push_back(child); + anm_link_node(anm, child->anm); +} + +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); + } +} + +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::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 000000000000 -r 1873dfd13f2d src/xform_node.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xform_node.h Thu Nov 14 05:27:09 2013 +0200 @@ -0,0 +1,134 @@ +/* +TODO: add multiple animations per node in libanim (i.e. multiple sets of tracks) +*/ +#ifndef XFORM_NODE_H_ +#define XFORM_NODE_H_ + +#include +#include "vmath/vector.h" +#include "vmath/quat.h" +#include "vmath/matrix.h" + +enum Interp { INTERP_STEP, INTERP_LINEAR, INTERP_CUBIC }; +enum Extrap { EXTRAP_EXTEND, EXTRAP_CLAMP, EXTRAP_REPEAT }; + +struct anm_node; +struct anm_track; + +// XXX all time arguments are milliseconds + +class XFormNode { +private: + struct anm_node *anm; + std::vector children; + + 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(); + + void set_name(const char *name); + const char *get_name() const; + + void set_interpolator(Interp in); + Interp get_interpolator() const; + void set_extrapolator(Extrap ex); + Extrap get_extrapolator() const; + + // children management + void add_child(XFormNode *child); + void remove_child(XFormNode *child); + + int get_children_count() const; + XFormNode *get_child(int idx); + const XFormNode *get_child(int idx) const; + + + void set_position(const Vector3 &pos, long tmsec = 0); + Vector3 get_node_position(long tmsec = 0) const; + + void set_rotation(const Quaternion &quat, long tmsec = 0); + Quaternion get_node_rotation(long tmsec = 0) const; + + void set_scaling(const Vector3 &pos, long tmsec = 0); + Vector3 get_node_scaling(long tmsec = 0) const; + + // these take hierarchy into account + Vector3 get_position(long tmsec = 0) const; + Quaternion get_rotation(long tmsec = 0) const; + Vector3 get_scaling(long tmsec = 0) const; + + void set_pivot(const Vector3 &pivot); + Vector3 get_pivot() const; + + // the local matrix is concatenated with the regular node/anim matrix + void set_local_matrix(const Matrix4x4 &mat); + const Matrix4x4 &get_local_matrix() const; + + // for bone nodes, the transformation of the bone in bind position + void set_bone_matrix(const Matrix4x4 &bmat); + const Matrix4x4 &get_bone_matrix() const; + + // node transformation alone + void get_node_xform(long tmsec, Matrix4x4 *mat, Matrix4x4 *inv_mat = 0) const; + + // node transformation taking hierarchy into account + 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 /* XFORM_NODE_H_ */