/* Copyright (C) 2003 This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * Copyright (C) 2004, 2006 Mekensleep * * Mekensleep * 24 rue vieille du temple * 75004 Paris * licensing@mekensleep.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * * Authors: * Cedric PINSON * Loic Dachary * Igor Kravtchenko * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef USE_NPROFILE #include #else // USE_NPROFILE #define NPROFILE_SAMPLE(a) #endif // USE_NPROFILE #define GL_GLEXT_PROTOTYPES #ifdef WIN32 # include #endif // WIN32 #define OSGCAL_MAX_BONES_PER_MESH 24 #include #include // it contains the number of parameter used (light,material,...) except for the matrix (bone), the matrix bone will used the param // available //#define OSGCAL_VP_PARAM_USED (22) //#define OSGCAL_USE_MAX_VP_LOCAL #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef OSGCAL_USE_MAX_VP_LOCAL #include #endif #include #include #include #include #include #include #include "osgCal/HardwareModel" std::map > g_filename2texture; static osg::ref_ptr g_tmpTexture; static const int TEXSIZE = 512; static char VERTEX_PROGRAM_STR[]= "!!ARBvp1.0\n"\ "PARAM constant = { 1, 3, 0, 0 };\n"\ "TEMP R0, R1, R2, R3, R4, R5, tmp;\n"\ "ADDRESS A0;\n"\ "ATTRIB texCoord = vertex.texcoord[0];\n"\ "ATTRIB normal = vertex.normal;\n"\ "ATTRIB index = vertex.attrib[6];\n"\ "ATTRIB weight = vertex.attrib[1];\n"\ "ATTRIB position = vertex.position;\n"\ "# PARAM worldViewProjMatrix[4] = { state.matrix.mvp };\n"\ "PARAM mViewInverse[4] = { state.matrix.modelview.invtrans };\n"\ "PARAM mView[4] = { state.matrix.modelview };\n"\ "PARAM mProj[4] = { state.matrix.projection };\n"\ "PARAM mTex[4] = { state.matrix.texture[0] };\n"\ "PARAM globalAmbient = state.lightmodel.ambient;\n"\ "PARAM emission = state.material.emission;\n"\ "PARAM ambient = state.material.ambient;\n"\ "# its a position light;\n"\ "PARAM lightDir = state.light[0].position;\n"\ "PARAM diffuse = state.lightprod[0].diffuse;\n"\ "PARAM fogalpha = program.local[72];\n" \ #ifdef OSGCAL_USE_MAX_VP_LOCAL "PARAM matrix[OSGCAL_VP_MAX_LOCAL_STR] = { program.local[0..OSGCAL_VP_MAX_LOCAL_MINUS_ONE_STR] };\n" #else "PARAM matrix[72] = { program.local[0..71] };\n" #endif "# multiply UV by texture matrix;\n"\ "DPH result.texcoord[0].x, texCoord.xyzw, mTex[0]; \n"\ "DPH result.texcoord[0].y, texCoord.xyzw, mTex[1]; \n"\ "DPH result.texcoord[0].z, texCoord.xyzw, mTex[2]; \n"\ "DPH result.texcoord[0].w, texCoord.xyzw, mTex[3]; \n"\ "MOV result.texcoord[1], vertex.texcoord[1];\n"\ "MUL R4, index, constant.y; \n"\ "ARL A0.x, R4.y;\n"\ "DP3 R0.x, matrix[A0.x].xyzx, normal.xyzx;\n"\ "DP3 R0.y, matrix[A0.x + 1].xyzx, normal.xyzx;\n"\ "DP3 R0.z, matrix[A0.x + 2].xyzx, normal.xyzx;\n"\ "MUL R1.yzw, R0.xxyz, weight.y;\n"\ "ARL A0.x, R4.x;\n"\ "DP3 R0.x, matrix[A0.x].xyzx, normal.xyzx;\n"\ "DP3 R0.y, matrix[A0.x + 1].xyzx, normal.xyzx;\n"\ "DP3 R0.z, matrix[A0.x + 2].xyzx, normal.xyzx;\n"\ "MAD R1.yzw, R0.xxyz, weight.x, R1.yyzw;\n"\ "# compute normal with m and weight 3 add the result to R1\n"\ "#ARL A0.x, R4.z;\n"\ "#DP3 R0.x, matrix[A0.x], normal;\n"\ "#DP3 R0.y, matrix[A0.x + 1], normal;\n"\ "#DP3 R0.z, matrix[A0.x + 2], normal;\n"\ "#MAD R1, R0, weight.z, R1;\n"\ "# compute normal with m and weight 4 add the result to R1\n"\ "#ARL A0.x, R4.w;\n"\ "#DP3 R0.x, matrix[A0.x], normal;\n"\ "#DP3 R0.y, matrix[A0.x + 1], normal;\n"\ "#DP3 R0.z, matrix[A0.x + 2], normal;\n"\ "#MAD R1, R0, weight.w, R1;\n"\ "DP3 R0.x, R1.yzwy, R1.yzwy;\n"\ "RSQ R0.x, R0.x;\n"\ "MUL R0.xyz, R0.x, R1.yzwy;\n"\ "# rotate the normal in modelview result in R2\n"\ "DP3 R5.x, R0, mViewInverse[0];\n"\ "DP3 R5.y, R0, mViewInverse[1];\n"\ "DP3 R5.z, R0, mViewInverse[2];\n"\ "#MOV R5, R0 ;\n"\ "# MOV R0, R5;\n"\ "# MUL R0, R0, constant.z;\n"\ "ARL A0.x, R4.w;\n"\ "DPH R0.x, position.xyzx, matrix[A0.x];\n"\ "DPH R0.y, position.xyzx, matrix[A0.x + 1];\n"\ "DPH R0.z, position.xyzx, matrix[A0.x + 2];\n"\ "ARL A0.x, R4.z;\n"\ "DPH R3.x, position.xyzx, matrix[A0.x];\n"\ "DPH R3.y, position.xyzx, matrix[A0.x + 1];\n"\ "DPH R3.z, position.xyzx, matrix[A0.x + 2];\n"\ "ARL A0.x, R4.y;\n"\ "DPH R1.y, position.xyzx, matrix[A0.x];\n"\ "DPH R1.z, position.xyzx, matrix[A0.x + 1];\n"\ "DPH R1.w, position.xyzx, matrix[A0.x + 2];\n"\ "MUL R2.xyz, R1.yzwy, weight.y;\n"\ "ARL A0.x, R4.x;\n"\ "DPH R1.x, position.xyzx, matrix[A0.x];\n"\ "DPH R1.y, position.xyzx, matrix[A0.x + 1];\n"\ "DPH R1.z, position.xyzx, matrix[A0.x + 2];\n"\ "MAD R1.xyz, R1.xyzx, weight.x, R2.xyzx;\n"\ "MAD R1.xyz, R3.xyzx, weight.z, R1.xyzx;\n"\ "MAD R0.xyz, R0.xyzx, weight.w, R1.xyzx;\n"\ "DPH R3.x, R0.xyzx, mView[0];\n"\ "DPH R3.y, R0.xyzx, mView[1];\n"\ "DPH R3.z, R0.xyzx, mView[2];\n"\ "DPH R3.w, R0.xyzx, mView[3];\n"\ "SUB R2, lightDir,R3 ;\n" \ "DPH result.position.x, R3.xyzx, mProj[0];\n"\ "DPH result.position.y, R3.xyzx, mProj[1];\n"\ "DPH result.position.z, R3.xyzx, mProj[2];\n"\ "DPH result.position.w, R3.xyzx, mProj[3];\n"\ /* "DP3 vtx.w, R3, R3;\n"\ "RSQ vtx.w, vtx.w;\n"\ "RCP vtx.w, vtx.w;\n"\ "SUB vtx.w, vtx.w, fogalpha.x;\n"\ "MUL vtx.w, vtx.w, fogalpha.y;\n"\ "MIN vtx.w, 1, vtx.w;\n"\ */ "MOV R0,R5;\n"\ "MOV R3,R2;\n"\ "# DP3 R1.x, lightDir.xyzx, lightDir.xyzx;\n"\ "DP3 R1.x, R3.xyzx, R3.xyzx;\n"\ "RSQ R1.x, R1.x;\n"\ "# MUL R2.xyz, R1.x, lightDir.xyzx;\n"\ "MUL R2.xyz, R1.x, R3.xyzx;\n"\ "DP3 R0.x, R0.xyzx, R2.xyzx;\n"\ "MAX R0.x, R0.x, constant.z;\n"\ "MAD tmp, R0.x, diffuse, emission;\n"\ "MAD tmp, globalAmbient, ambient, tmp;\n"\ "MOV tmp.w, diffuse.w;\n"\ /* "MUL tmp.w, vtx.w, diffuse.w;\n"\ */ "MOV result.color.front.primary, tmp;\n"\ "END\n\0"; using namespace osgCal; const int DEPTHMASK = 0x1234; class DepthMask : public osg::StateAttribute { public: DepthMask(bool bWriteMask = true); /// Copy constructor using CopyOp to manage deep vs shallow copy. DepthMask(const DepthMask &dp, const osg::CopyOp ©op = osg::CopyOp::SHALLOW_COPY) : StateAttribute(dp, copyop), writeMask_(dp.writeMask_) {} META_StateAttribute(osgCal, DepthMask, (osg::StateAttribute::Type) DEPTHMASK); // return -1 if *this < *rhs, 0 if *this==*rhs, 1 if *this>*rhs. virtual int compare(const StateAttribute &sa) const { // check the types are equal and then create the rhs variable // used by the COMPARE_StateAttribute_Parameter macro's below. COMPARE_StateAttribute_Types(DepthMask, sa) // compare each parameter in turn against the rhs. COMPARE_StateAttribute_Parameter(writeMask_) return 0; // passed all the above comparison macro's, must be equal. } virtual bool getModeUsage(ModeUsage& usage) const { usage.usesMode(GL_DEPTH_TEST); return true; } inline void setWriteMask(GLboolean mask) { writeMask_ = mask; } inline GLboolean getWriteMask() const { return writeMask_; } virtual void apply(osg::State& state) const; protected: virtual ~DepthMask(); GLboolean writeMask_; }; DepthMask::DepthMask(bool writeMask): writeMask_(writeMask) { } DepthMask::~DepthMask() { } void DepthMask::apply(osg::State &) const { glDepthMask(writeMask_); } class CalUpdateCallback: public osg::NodeCallback { public: CalUpdateCallback() : previous(timer.tick()), _lastestTime(0) {} virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { #ifdef USE_NPROFILE NPROFILE_SAMPLE("CalUpdateCallback"); #endif Model* model = dynamic_cast(node); float deltaTime = 0; if (!nv->getFrameStamp()) { osg::Timer_t current = timer.tick(); deltaTime = (float)timer.delta_s(previous, current); previous = current; } else { double time = nv->getFrameStamp()->getReferenceTime(); deltaTime = time - _lastestTime; _lastestTime = time; } CalModel * pCalModel = model->getCalModel(); pCalModel->getAbstractMixer()->updateAnimation(deltaTime); pCalModel->getAbstractMixer()->updateSkeleton(); model->update(); traverse(node,nv); node->dirtyBound(); } protected: osg::Timer timer; osg::Timer_t previous; double _lastestTime; }; static std::string convertProxyName(const std::string &_name) { std::string::size_type pos = _name.rfind("_proxy"); if (pos == std::string::npos) return _name; std::string str; str = _name.substr(0, pos); return str; } Model::Model() : mHardwareModel(NULL), _calModel(NULL) { #if CAL3D_VERSION < 10 _calModel = new CalModel(); #endif // CAL3D_VERSION < 10 setUseVertexProgram(false); setUseColorOrTexture(true); for (int i = 0; i < MODEL_VBO_SIZE; i++) _vbo[i] = 0; // // Preserve notify level so that complex debug notifications have a negligeable impact // when not requested, as opposed to evaluating every argument of the << operator. // _notify = osg::getNotifyLevel(); } Model::Model(const Model& model, const osg::CopyOp& copyop) : osg::Geode(model, copyop), _useVertexProgram(model._useVertexProgram), _useColorOrTexture(model._useColorOrTexture) { _calModel = 0; for (int i=0; i< MODEL_VBO_SIZE; i++) _vbo[i] = 0; mHardwareModel = 0; setUpdateCallback(0); // // Preserve notify level so that complex debug notifications have a negligeable impact // when not requested, as opposed to evaluating every argument of the << operator. // _notify = osg::getNotifyLevel(); } Model::~Model() { setUpdateCallback(0); if(_coreModel.valid()) _coreModel->garbageCollect(); #if CAL3D_VERSION < 10 if(_calModel) { _calModel->destroy(); } #endif // CAL3D_VERSION < 10 if(_coreModel.valid()) for (std::vector::iterator i=_activeMeshes.begin();i!=_activeMeshes.end();i++) _coreModel->SubUsingMeshId(*i); osg::Drawable::Extensions* drExt = osg::Drawable::getExtensions(0,true); if (_vbo[0]) drExt->glDeleteBuffers(MODEL_VBO_SIZE, _vbo); if(mHardwareModel) delete mHardwareModel; if(_calModel) { delete _calModel; _calModel = NULL; } } bool Model::setCoreModel(CoreModel* coreModel) { _coreModel = coreModel; #if CAL3D_VERSION >= 10 _calModel = new CalModel(_coreModel->getCalCoreModel()); return true; #else // CAL3D_VERSION >= 10 return _calModel->create(_coreModel->getCalCoreModel()); // _calModel->setAbstractMixer(NULL); #endif // CAL3D_VERSION >= 10 } bool Model::setUseVertexProgram(bool useVertexProgram, unsigned int contextID) { if(useVertexProgram) { osg::VertexProgram::Extensions* vpExt = osg::VertexProgram::getExtensions(contextID, true); void *glBindBuffer = osg::getGLExtensionFuncPtr("glBindBuffer", "glBindBuffer"); if(!vpExt || !vpExt->isVertexProgramSupported() || !glBindBuffer) { useVertexProgram = false; osg::notify(osg::WARN) << "Model::setUseVertexProgram if you have the extension be sure to have a context gl actif\n if you use Producer you can have a consistent context with viewer.realize(Producer::CameraGroup::SingleThreaded)\n"; } } return _useVertexProgram = useVertexProgram; } bool Model::create(void) { if(_activeMeshes.empty()) { // By default all attached meshes are active. for(int coreMeshId = 0; coreMeshId < getCalCoreModel()->getCoreMeshCount(); coreMeshId++) { if(getCalModel()->getMesh(coreMeshId)) { invertUVs(coreMeshId); _activeMeshes.push_back(coreMeshId); _coreModel->AddUsingMeshId(coreMeshId); } } // All mesh are used for collisions/bounding boxes _collisionMeshes = _activeMeshes; } _calModel->update(0); setUpdateCallback(new CalUpdateCallback()); if(getUseVertexProgram()) return createHardware(); else return createSoftware(); return true; } bool Model::createHardware(void) { float *tmpVertexBuffer = new float[OSGCAL_MAX_VERTEX_PER_MODEL*3]; float *tmpWeightBuffer = new float[OSGCAL_MAX_VERTEX_PER_MODEL*4]; float *tmpMatrixIndexBuffer = new float[OSGCAL_MAX_VERTEX_PER_MODEL*4]; float *tmpNormalBuffer = new float[OSGCAL_MAX_VERTEX_PER_MODEL*3]; float *tmpTex0CoordBuffer = new float[OSGCAL_MAX_VERTEX_PER_MODEL*2]; float *tmpTex1CoordBuffer = new float[OSGCAL_MAX_VERTEX_PER_MODEL*2]; CalIndex *tmpIndexBuffer = new CalIndex[OSGCAL_MAX_VERTEX_PER_MODEL*3]; char *tmpIMesh = new char[OSGCAL_MAX_VERTEX_PER_MODEL]; CalCoreModel* calCoreModel = getCalCoreModel(); mHardwareModel = new GlueCalHardwareModel(calCoreModel); mHardwareModel->setVertexBuffer((char*)tmpVertexBuffer,3*sizeof(float)); mHardwareModel->setNormalBuffer((char*)tmpNormalBuffer,3*sizeof(float)); mHardwareModel->setWeightBuffer((char*)tmpWeightBuffer,4*sizeof(float)); mHardwareModel->setMatrixIndexBuffer((char*)tmpMatrixIndexBuffer,4*sizeof(float)); mHardwareModel->setTextureCoordNum(2); mHardwareModel->setTextureCoordBuffer(0,(char*)tmpTex0CoordBuffer,2*sizeof(float)); mHardwareModel->setTextureCoordBuffer(1,(char*)tmpTex1CoordBuffer,2*sizeof(float)); mHardwareModel->setIndexBuffer(tmpIndexBuffer); mHardwareModel->setCoreMeshIds(_activeMeshes); // here check number of local variable to minimize number of drawable #ifdef OSGCAL_USE_MAX_VP_LOCAL GLint numberOfLocalParameterAvailableForSkinning = 0; glGetProgramivARB(GL_VERTEX_PROGRAM_ARB, GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB, &numberOfLocalParameterAvailableForSkinning); if (!numberOfLocalParameterAvailableForSkinning) { assert(0); } numberOfLocalParameterAvailableForSkinning = numberOfLocalParameterAvailableForSkinning - OSGCAL_VP_PARAM_USED; osg::notify(osg::NOTICE) << "GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB " << numberOfLocalParameterAvailableForSkinning << std::endl; unsigned int maxBones = numberOfLocalParameterAvailableForSkinning / 3; mHardwareModel->load( 0, 0, maxBones); osg::notify(osg::NOTICE) << "MAX NUMBER BONES AVAILABLE " << maxBones <load( 0, 0, OSGCAL_MAX_BONES_PER_MESH); #endif int iv = 0; int nbActiveMeshes = _activeMeshes.size(); for (int i = 0; i < nbActiveMeshes; i++) { int meshId = _activeMeshes[i]; CalCoreMesh *coreMesh = calCoreModel->getCoreMesh(meshId); int submeshCount = coreMesh->getCoreSubmeshCount(); for (int j = 0; j < submeshCount; j++) { CalCoreSubmesh *coreSubmesh = coreMesh->getCoreSubmesh(j); std::vector &vectorVertex = coreSubmesh->getVectorVertex(); int nbVertices = vectorVertex.size(); while(nbVertices--) tmpIMesh[iv++] = i; } } // the index index in pIndexBuffer are relative to the begining of the hardware mesh, // we make them relative to the begining of the buffer. int hardwareMeshId; int nb = mHardwareModel->getHardwareMeshCount(); osg::notify(osg::NOTICE) << "Hardware Mesh " << nb << std::endl; for(hardwareMeshId = 0; hardwareMeshId < nb; hardwareMeshId++) { mHardwareModel->selectHardwareMesh(hardwareMeshId); int faceId; for(faceId = 0; faceId < mHardwareModel->getFaceCount(); faceId++) { tmpIndexBuffer[faceId*3+0+ mHardwareModel->getStartIndex()]+=mHardwareModel->getBaseVertexIndex(); tmpIndexBuffer[faceId*3+1+ mHardwareModel->getStartIndex()]+=mHardwareModel->getBaseVertexIndex(); tmpIndexBuffer[faceId*3+2+ mHardwareModel->getStartIndex()]+=mHardwareModel->getBaseVertexIndex(); } } int nvtx = mHardwareModel->getTotalVertexCount(); CUSTOM_ASSERT(nvtx < OSGCAL_MAX_VERTEX_PER_MODEL && "Increase size of OSGCAL_MAX_VERTEX_PER_MODEL"); fixNormalHW(nvtx, (osg::Vec3f*) tmpVertexBuffer, (osg::Vec3f*) tmpNormalBuffer, tmpIMesh, 0.02f); osg::Drawable::Extensions* drExt = osg::Drawable::getExtensions(0,true); drExt->glGenBuffers(MODEL_VBO_SIZE, _vbo); float *vertexAndNormal = new float[(3+3+2+2+4+4+4)*nvtx]; float *v = tmpVertexBuffer; float *n = tmpNormalBuffer; float *uv0 = tmpTex0CoordBuffer; float *uv1 = tmpTex1CoordBuffer; float *weight = tmpWeightBuffer; float *index = tmpMatrixIndexBuffer; float *base = vertexAndNormal; for (int i = 0; i < nvtx; i++) { *base++ = *v++; *base++ = *v++; *base++ = *v++; *base++ = *n++; *base++ = *n++; *base++ = *n++; *base++ = *uv0++; *base++ = *uv0++; *base++ = *uv1++; *base++ = *uv1++; *base++ = *weight++; *base++ = *weight++; *base++ = *weight++; *base++ = *weight++; *base++ = *index++; *base++ = *index++; *base++ = *index++; *base++ = *index++; *base++ = 0.0f; *base++ = 0.0f; *base++ = 0.0f; *base++ = 0.1f; } drExt->glBindBuffer(GL_ARRAY_BUFFER_ARB, _vbo[MODEL_VBO_GEOMETRY]); drExt->glBufferData(GL_ARRAY_BUFFER_ARB, nvtx*(3+3+2+2+4+4+4)*sizeof(float), (const void*)vertexAndNormal, GL_STATIC_DRAW_ARB); drExt->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB, _vbo[MODEL_VBO_INDEX]); if (sizeof(CalIndex) == sizeof(unsigned short)) { drExt->glBufferData(GL_ELEMENT_ARRAY_BUFFER_ARB, mHardwareModel->getTotalFaceCount()*3*sizeof(unsigned short), (const void*)tmpIndexBuffer, GL_STATIC_DRAW_ARB); } else if (sizeof(CalIndex) == sizeof(unsigned int)) { drExt->glBufferData(GL_ELEMENT_ARRAY_BUFFER_ARB, mHardwareModel->getTotalFaceCount()*3*sizeof(unsigned int), (const void*)tmpIndexBuffer, GL_STATIC_DRAW_ARB); } else { assert(0 && "invalid index size"); } drExt->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB,0); drExt->glBindBuffer(GL_ARRAY_BUFFER_ARB,0); _vp = new osg::VertexProgram; #ifdef OSGCAL_USE_MAX_VP_LOCAL char number[128]; sprintf(number,"%d",numberOfLocalParameterAvailableForSkinning-1); std::string myVP(VERTEX_PROGRAM_STR); std::string replaceFrom("OSGCAL_VP_MAX_LOCAL_MINUS_ONE_STR"); std::string::size_type it = myVP.find(replaceFrom); std::string toReplace(number); myVP.replace(it,replaceFrom.size(),toReplace); replaceFrom = std::string("OSGCAL_VP_MAX_LOCAL_STR"); sprintf(number,"%d",numberOfLocalParameterAvailableForSkinning); it = myVP.find(replaceFrom); toReplace = std::string(number); myVP.replace(it,replaceFrom.size(),toReplace); _vp->setVertexProgram(myVP.c_str()); osg::notify(osg::DEBUG_FP) << myVP <setVertexProgram(VERTEX_PROGRAM_STR); #endif // process hardware meshes int nbHWMesh = mHardwareModel->getHardwareMeshCount(); for(hardwareMeshId = 0; hardwareMeshId < nbHWMesh; hardwareMeshId++) { mHardwareModel->selectHardwareMesh(hardwareMeshId); const GlueCalHardwareModel::CalHardwareMesh& hardwareMesh = mHardwareModel->getVectorHardwareMesh()[hardwareMeshId]; int coreMeshId = hardwareMesh.meshId; // incorrect naming in hardwareMesh, the meshId is really a coreMeshId bool supportsPrimitiveFunctor = isCollisionMesh(coreMeshId); bool invisible = isInvisibleMesh(coreMeshId); //bool forceSW = isForceSoftwareMesh(coreMeshId); //if(!invisible && !supportsPrimitiveFunctor && !forceSW) { if(!invisible && !supportsPrimitiveFunctor) { SubMeshHardware* submesh = new SubMeshHardware(this, hardwareMeshId); CalCoreMesh* coreMesh = calCoreModel->getCoreMesh(coreMeshId); const std::string &name = coreMesh->getName(); submesh->setName(name); int indexInBuffer=mHardwareModel->getStartIndex(); submesh->InitHardwareMesh( // tmpIndexBuffer + mHardwareModel->getStartIndex(), mHardwareModel->getFaceCount()*3, _vp.get(), mHardwareModel, _vbo, indexInBuffer, nvtx); CalMesh* cmesh = _calModel->getMesh(coreMeshId); if (!cmesh) osg::notify(osg::FATAL) << "mesh " << name << " not found in calModel" <getSubmesh(hardwareMesh.submeshId); if (!sbm) osg::notify(osg::FATAL) << "submesh " << hardwareMesh.submeshId << " of " << name << " not found" <_staticbbox=_bbox; osg::Vec3 min=_bbox._min; osg::Vec3 max=_bbox._max; min[2]-=hardwareMeshId/(1.0*mHardwareModel->getHardwareMeshCount()); max[2]-=hardwareMeshId/(1.0*mHardwareModel->getHardwareMeshCount()); submesh->_staticbbox=osg::BoundingBox(min,max); */ #if 0 // display bbox osg::ShapeDrawable* sh=new osg::ShapeDrawable; // osg::notify(osg::INFO) << "Box " << submesh->_staticbbox._min << " - " << submesh->_staticbbox._max <setShape(bb); addDrawable(sh); #endif } } // process collid meshes for(std::vector::iterator coreMeshId = _activeMeshes.begin(); coreMeshId != _activeMeshes.end(); coreMeshId++) { bool supportsPrimitiveFunctor = isCollisionMesh(*coreMeshId); bool invisible = isInvisibleMesh(*coreMeshId); //bool forceSW = isForceSoftwareMesh(*coreMeshId); //if(invisible || supportsPrimitiveFunctor || forceSW) { if(invisible || supportsPrimitiveFunctor) { if(!createSubMeshSoftware(*coreMeshId)) return false; } } delete [] tmpVertexBuffer; delete [] tmpNormalBuffer; delete [] tmpWeightBuffer; delete [] tmpMatrixIndexBuffer; delete [] tmpTex0CoordBuffer; delete [] tmpTex1CoordBuffer; delete [] tmpIndexBuffer; delete [] tmpIMesh; delete [] vertexAndNormal; mHardwareModel->setIndexBuffer(NULL); return true; } bool Model::createSoftware(void) { for(std::vector::iterator coreMeshId = _activeMeshes.begin(); coreMeshId != _activeMeshes.end(); coreMeshId++) { if(!createSubMeshSoftware(*coreMeshId)) return false; } //int t1 = timeGetTime(); fixNormalSW(0.02f); //int t2 = timeGetTime(); //t2 -= t1; return true; } bool Model::createSubMeshSoftware(int coreMeshId) { Drawables submeshes; bool supportsPrimitiveFunctor = isCollisionMesh(coreMeshId); bool invisible = isInvisibleMesh(coreMeshId); CalCoreMesh* calCoreMesh = getCalCoreModel()->getCoreMesh(coreMeshId); if(calCoreMesh == NULL) { osg::notify(osg::FATAL) << "Model::createSubMeshSoftware(" << coreMeshId << ") failed " << CalError::getLastErrorDescription() << std::endl; return false; } const std::string& name = calCoreMesh->getName(); int coreSubmeshCount = calCoreMesh->getCoreSubmeshCount(); CalMesh* calMesh = _calModel->getMesh(coreMeshId); if(calMesh == NULL) { osg::notify(osg::FATAL) << "Model::createSubMeshSoftware(" << coreMeshId << ") could not find calMesh for coreMeshId " << coreMeshId << " : " << CalError::getLastErrorDescription() << std::endl; return false; } for(int coreSubmeshId = 0; coreSubmeshId < coreSubmeshCount; coreSubmeshId++) { int& submeshId = coreSubmeshId; CalSubmesh* calSubmesh = calMesh->getSubmesh(submeshId); if(calSubmesh == NULL) { osg::notify(osg::FATAL) << "Model::createSubMeshSoftware(" << coreMeshId << ") could not find calSubmesh for coreMeshId " << coreMeshId << " submeshId " << submeshId << " : " << CalError::getLastErrorDescription() << std::endl; return false; } CalCoreSubmesh* calCoreSubmesh = calSubmesh->getCoreSubmesh(); SubMeshSoftware* submesh = new SubMeshSoftware(_calModel, coreMeshId, coreSubmeshId); char str[200]; sprintf(str, "%s%d", name.c_str(), coreSubmeshId); if (_coreModel->_name2Normal[str] == NULL) { std::vector &vectorVertex = calCoreSubmesh->getVectorVertex(); int size = vectorVertex.size(); CalVector *readNormals = new CalVector[ size ]; for (int i = 0; i < size; i++) { readNormals[i] = vectorVertex[i].normal; } _coreModel->_name2Normal[str] = readNormals; } submesh->setName(name); submesh->setInvisible(invisible); submesh->setSupportsPrimitiveFunctor(supportsPrimitiveFunctor); submesh->create(); if(!invisible) { if(!setupMaterial(submesh, calSubmesh)) return false; /* submesh->_staticbbox=_bbox; osg::Vec3 min=_bbox._min; osg::Vec3 max=_bbox._max; min[2]-=coreSubmeshId/(1.0*coreSubmeshCount); max[2]-=coreSubmeshId/(1.0*coreSubmeshCount); submesh->_staticbbox=osg::BoundingBox(min,max); */ } addDrawable(submesh); submeshes.push_back(submesh); } _coreMeshId2Drawables[coreMeshId] = submeshes; return true; } void Model::update(void) { // LARGE_INTEGER time1, time2; // QueryPerformanceCounter(&time1); for(unsigned int i = 0; i < getNumDrawables(); i++) { osg::Drawable* drawable = getDrawable(i); SubMeshHardware* hardware = dynamic_cast(drawable); if(hardware) { hardware->update(); } else { SubMeshSoftware* software = dynamic_cast(drawable); if(software) software->update(); else { CalError::setLastError(CalError::NULL_BUFFER, __FILE__, __LINE__, "unexpected drawable type"); } } } } inline void Color2Vec4(const CalCoreMaterial::Color& color, osg::Vec4& vec4) { vec4[0] = color.red / 255.f; vec4[1] = color.green / 255.f; vec4[2] = color.blue / 255.f; vec4[3] = color.alpha == 0 ? 1.f : (color.alpha / 255.f); } bool Model::setupMaterial(osg::Drawable* drawable, CalSubmesh* calSubmesh) { int coreMaterialId = calSubmesh->getCoreMaterialId(); if(coreMaterialId < 0) { CalError::setLastError(CalError::INDEX_BUILD_FAILED, __FILE__, __LINE__); return false; } CalCoreMaterial* calCoreMaterial = getCalCoreModel()->getCoreMaterial(coreMaterialId); if(calCoreMaterial == 0) { CalError::setLastError(CalError::NULL_BUFFER, __FILE__, __LINE__); return false; } const std::string &materialName = calCoreMaterial->getName(); SubMeshSoftware *smsw = dynamic_cast (drawable); if (smsw) smsw->setMaterialName( materialName ); else { SubMeshHardware *smhw = dynamic_cast (drawable); smhw->setMaterialName( materialName ); } osg::StateSet *set = drawable->getOrCreateStateSet(); set->setDataVariance(DYNAMIC); osg::Material *material = new osg::Material(); set->setAttributeAndModes(material, osg::StateAttribute::ON); if (!getUseColorOrTexture() || calCoreMaterial->getMapCount() <= 0) { osg::Vec4 materialColor; // set the material ambient color Color2Vec4(calCoreMaterial->getAmbientColor(), materialColor); material->setAmbient(osg::Material::FRONT_AND_BACK, materialColor); // set the material diffuse color Color2Vec4(calCoreMaterial->getDiffuseColor(), materialColor); material->setDiffuse(osg::Material::FRONT_AND_BACK, materialColor); // // Disabled because hardware accelerated meshes do not handle specular // // set the material specular color //Color2Vec4(calCoreMaterial->getSpecularColor(), materialColor); //material->setSpecular(osg::Material::FRONT_AND_BACK, materialColor); } // set the material shininess factor float shininess = calCoreMaterial->getShininess(); material->setShininess(osg::Material::FRONT_AND_BACK, shininess); osg::Texture2D *texture = NULL; osg::BlendFunc *bf = NULL; std::map &mat = getCoreModel()->_materials; std::string targetmap = mat[materialName].targetmap; if (targetmap == "") { // no targetmap for this material, just take the "normal" texture CoreModel::Textures2D *textures = getTextures2D(coreMaterialId); if (textures && textures->size() > 0) { int size = textures->size(); if (size == 2) { texture = (*textures)[1].get(); set->setTextureAttributeAndModes(1, texture); osg::TexEnv *tenv = new osg::TexEnv(); tenv->setMode( osg::TexEnv::MODULATE); set->setTextureAttributeAndModes(1, tenv); } texture = (*textures)[0].get(); osg::Image *img = texture->getImage(); if (texture->getInternalFormat() == GL_RGBA || texture->getInternalFormat() == 4 || (img && img->getInternalTextureFormat() == 4)) { bf = new osg::BlendFunc; bf->setFunction(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } } } else { //CoreModel::Textures2D *textures = getTextures2D(coreMaterialId); const std::string &transparency = mat[materialName].transparency; const std::string &envmap = mat[materialName].envmap; texture = targetmap2texture_[ targetmap ].get(); if (!texture) { texture = new osg::Texture2D(); texture->setInternalFormat(GL_RGBA); int texsize = 256; if (targetmap.find("bodymap") != std::string::npos) texsize = 512; unsigned char *pixs = new unsigned char[texsize*texsize]; for (int i = 0; i < texsize*texsize; i++) pixs[i] = 255; osg::Image *img = new osg::Image(); img->setImage(texsize, texsize, 1, 1, GL_LUMINANCE, GL_UNSIGNED_BYTE, pixs, osg::Image::USE_NEW_DELETE); texture->setTextureSize(texsize, texsize); texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); texture->setImage(img); if (fxState_.valid()) texture->apply(*fxState_.get()); else osg::notify(osg::WARN) << "Model::setupMaterial: no FX state yet defined" << envmap << std::endl; targetmap2texture_[ targetmap ] = texture; } if (transparency == "add") { bf = new osg::BlendFunc; bf->setFunction(GL_SRC_ALPHA, GL_ONE); } else if (transparency == "blend") { bf = new osg::BlendFunc; bf->setFunction(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } if (envmap != "") { osg::Texture2D *texture = NULL; if (g_filename2texture.find(envmap) != g_filename2texture.end()) { texture = g_filename2texture[envmap].get(); } else { osg::Image *img = osgDB::readImageFile(envmap); if (!img) osg::notify(osg::WARN) << "Model::setupMaterial: failed to read image file" << envmap << std::endl; else { texture = new osg::Texture2D; texture->setImage(img); texture->setUnRefImageDataAfterApply(true); texture->setUseHardwareMipMapGeneration(false); texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR_MIPMAP_NEAREST); texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR); } } if (texture) { g_filename2texture[envmap] = texture; set->setTextureAttributeAndModes(1, texture); osg::TexEnvCombine *combiner = new osg::TexEnvCombine; combiner->setCombine_RGB(GL_ADD); combiner->setCombine_Alpha(GL_MODULATE); combiner->setSource0_RGB(GL_TEXTURE); combiner->setSource0_Alpha(GL_TEXTURE); combiner->setOperand0_RGB(GL_SRC_COLOR); combiner->setOperand0_Alpha(GL_SRC_ALPHA); combiner->setSource1_RGB(GL_PREVIOUS_ARB); combiner->setSource1_Alpha(GL_PREVIOUS_ARB); combiner->setOperand1_RGB(GL_SRC_COLOR); combiner->setOperand1_Alpha(GL_SRC_ALPHA); set->setTextureAttributeAndModes(1, combiner); osg::TexGen *texgen = new osg::TexGen(); texgen->setMode( osg::TexGen::NORMAL_MAP ); set->setTextureAttributeAndModes(1, texgen); osg::TexMat *texmat = new osg::TexMat(); texmat->setMatrix( osg::Matrix::scale(0.5f, 0.5f, 0) * osg::Matrix::translate(0.5f, 0.5f, 0) ); set->setTextureAttributeAndModes(1, texmat); } } std::vector texturesName; getCoreModel()->getTexturesNameFromMaterialID(coreMaterialId, texturesName); /* if (texturesName.size() == 2) { const std::string &tex = texturesName[1]; osg::Texture2D *texture = NULL; if (g_filename2texture.find(envmap) != g_filename2texture.end()) { texture = g_filename2texture[envmap].get(); } else { osg::Image *img = osgDB::readImageFile(tex); if (!img) osg::notify(osg::WARN) << "Model::setupMaterial: failed to read image file" << envmap << std::endl; else { // we have Luminance, need to create an alpha texture img->setInternalTextureFormat(GL_ALPHA); img->setPixelFormat(GL_ALPHA); texture = new osg::Texture2D; texture->setImage(img); texture->setUnRefImageDataAfterApply(true); } } if (texture) { set->setTextureAttributeAndModes(1, texture); set->setMode(GL_BLEND, osg::StateAttribute::ON); bf = new osg::BlendFunc(); bf->setFunction(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); //bf->setFunction(GL_SRC_ALPHA, GL_ONE); osg::TexEnvCombine *combiner = new osg::TexEnvCombine; combiner->setCombine_RGB(GL_REPLACE); combiner->setCombine_Alpha(GL_MODULATE); combiner->setSource0_RGB(GL_PREVIOUS_ARB); combiner->setSource0_Alpha(GL_PREVIOUS_ARB); combiner->setOperand0_RGB(GL_SRC_COLOR); combiner->setOperand0_Alpha(GL_SRC_ALPHA); combiner->setSource1_Alpha(GL_TEXTURE); combiner->setOperand1_Alpha(GL_SRC_ALPHA); set->setTextureAttributeAndModes(1, combiner); } } */ } if (bf) { set->setMode(GL_BLEND, osg::StateAttribute::ON); set->setAttributeAndModes(bf, osg::StateAttribute::ON); set->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); } if(texture) { set->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON); set->setMode(GL_ALPHA_TEST, 0); } //channel++; //break; // because we don't manage multitexture yet !!! so use the first texture //} return true; } Model::Textures2D* Model::getTextures2D(const std::string& materialName) { int coreMaterialId = getCalCoreModel()->getCoreMaterialId(materialName); if (coreMaterialId < 0) { CalError::setLastError(CalError::INDEX_BUILD_FAILED, __FILE__, __LINE__); return 0; } else return getTextures2D(coreMaterialId); } Model::Textures2D* Model::getTextures2D(int coreMaterialId) { if(coreMaterialId < 0) { CalError::setLastError(CalError::INDEX_BUILD_FAILED, __FILE__, __LINE__); return 0; } return getCoreModel()->getTextures2D(coreMaterialId); } Model::Drawables* Model::getDrawables(const std::string& meshName) { int coreMeshId = getCalCoreModel()->getCoreMeshId(meshName); if(coreMeshId < 0) { CalError::setLastError(CalError::INDEX_BUILD_FAILED, __FILE__, __LINE__); return 0; } else return getDrawables(coreMeshId); } bool Model::hasDrawables(int coreMeshId) { if(coreMeshId < 0) { CalError::setLastError(CalError::INDEX_BUILD_FAILED, __FILE__, __LINE__); return false; } return _coreMeshId2Drawables.find(coreMeshId) != _coreMeshId2Drawables.end(); } Model::Drawables* Model::getDrawables(int coreMeshId) { if(coreMeshId < 0) { CalError::setLastError(CalError::INDEX_BUILD_FAILED, __FILE__, __LINE__); return 0; } if(_coreMeshId2Drawables.find(coreMeshId) != _coreMeshId2Drawables.end()) { return &_coreMeshId2Drawables[coreMeshId]; } else { CalError::setLastError(CalError::INDEX_BUILD_FAILED, __FILE__, __LINE__); return 0; } } bool Model::bindMaterials(const std::string& meshName, const std::vector& materials) { int coreMeshId = getCalCoreModel()->getCoreMeshId(meshName); if(coreMeshId < 0) { CalError::setLastError(CalError::INDEX_BUILD_FAILED, __FILE__, __LINE__); return false; } CalMesh* calMesh = _calModel->getMesh(coreMeshId); if(calMesh == 0) return false; if(materials.size() != (unsigned int)calMesh->getSubmeshCount()) { CalError::setLastError(CalError::INDEX_BUILD_FAILED, __FILE__, __LINE__); for (int i=0;i<(int)materials.size();i++) { std::string name=materials[i]; if(_notify > osg::NOTICE) osg::notify(osg::INFO) << "material required in mesh " << meshName << " : " << name << std::endl; } return false; } for(int submeshId = 0; submeshId < calMesh->getSubmeshCount(); submeshId++) { const std::string &id = materials[submeshId]; int materialId = getCalCoreModel()->getCoreMaterialId(id); if(materialId < 0) { CalError::setLastError(CalError::INDEX_BUILD_FAILED, __FILE__, __LINE__); return false; } calMesh->getSubmesh(submeshId)->setCoreMaterialId(materialId); if(_notify > osg::NOTICE) osg::notify(osg::INFO) << "Model::bindMaterials: material required in mesh " << meshName << " : submeshId = " << submeshId << ", name = " << materials[submeshId] << ", materialId = " << materialId << std::endl; } return true; } bool Model::setActiveMesh(const std::string &_meshName) { int coreMeshId = getCalCoreModel()->getCoreMeshId(_meshName); if(coreMeshId < 0) { const CoreModel::Name2Filename& meshName2Filename = getCoreModel()->getMeshName2Filename(); if(meshName2Filename.find(_meshName) == meshName2Filename.end()) { CalError::setLastError(CalError::INDEX_BUILD_FAILED, __FILE__, __LINE__); return false; } const std::string& filename = meshName2Filename.find(_meshName)->second; //char str[200]; //sprintf(str, "Load Mesh \"%s\"", filename.c_str()); //NPROFILE_SAMPLE(str); NPROFILE_SAMPLE("Load Mesh"); coreMeshId = getCalCoreModel()->loadCoreMesh(filename, _meshName); if(_notify > osg::NOTICE) osg::notify(osg::INFO) << "LOAD OSGCAL mesh name " << filename << " with coreid " << coreMeshId << std::endl; if(coreMeshId >= 0) invertUVs(coreMeshId); } if(coreMeshId < 0) return false; if(_notify > osg::NOTICE) osg::notify(osg::INFO) << "OSGCAL active/attach mesh " << _meshName << " " << coreMeshId << std::endl; _activeMeshes.push_back(coreMeshId); // osg::notify(osg::INFO) << this << " add active meshes " << coreMeshId << std::endl; bool res = _calModel->attachMesh(coreMeshId); assert(res); _coreModel->AddUsingMeshId(coreMeshId); return true; } void Model::invertUVs(int coreMeshId) { CalCoreMesh* calCoreMesh = getCalCoreModel()->getCoreMesh(coreMeshId); int count = calCoreMesh->getCoreSubmeshCount(); for(int coreSubmeshId = 0; coreSubmeshId < count; coreSubmeshId++) { CalCoreSubmesh* coreSubmesh = calCoreMesh->getCoreSubmesh(coreSubmeshId); std::vector >& uv = coreSubmesh->getVectorVectorTextureCoordinate(); for(unsigned int n = 0; n < uv.size(); n++) for(unsigned int iuv = 0; iuv < uv[n].size(); iuv++) uv[n][iuv].v=1.0-uv[n][iuv].v; } } bool Model::setCollisionMeshNames(const std::vector& meshNames) { _collisionMeshes.clear(); _collisionMeshNames.clear(); for(std::vector::const_iterator i = meshNames.begin(); i != meshNames.end(); i++) if(!setCollisionMesh(*i)) return false; return true; } bool Model::setCollisionMesh(const std::string& meshName) { int coreMeshId = getCalCoreModel()->getCoreMeshId(meshName); if(coreMeshId < 0) { const CoreModel::Name2Filename& meshName2Filename = getCoreModel()->getMeshName2Filename(); if(meshName2Filename.find(meshName) == meshName2Filename.end()) { CalError::setLastError(CalError::INDEX_BUILD_FAILED, __FILE__, __LINE__); return false; } const std::string& filename = meshName2Filename.find(meshName)->second; NPROFILE_SAMPLE("Load Mesh"); coreMeshId = getCalCoreModel()->loadCoreMesh(filename, meshName); } if(coreMeshId < 0) return false; _collisionMeshNames.push_back(meshName); _collisionMeshes.push_back(coreMeshId); return true; } bool Model::removeCollisionMesh(const std::string& meshName) { int coreMeshId = getCalCoreModel()->getCoreMeshId(meshName); if(coreMeshId < 0) { const CoreModel::Name2Filename& meshName2Filename = getCoreModel()->getMeshName2Filename(); if(meshName2Filename.find(meshName) == meshName2Filename.end()) { CalError::setLastError(CalError::INDEX_BUILD_FAILED, __FILE__, __LINE__); return false; } const std::string& filename = meshName2Filename.find(meshName)->second; coreMeshId = getCalCoreModel()->loadCoreMesh(filename, meshName); } if(coreMeshId < 0) return false; for (int i=0;i<(int)_collisionMeshNames.size();i++) if (_collisionMeshNames[i]==meshName) { _collisionMeshNames.erase(_collisionMeshNames.begin()+i); break; } for (int i=0;i<(int)_collisionMeshNames.size();i++) if (_collisionMeshes[i]==coreMeshId) { _collisionMeshes.erase(_collisionMeshes.begin()+i); break; } return true; } std::vector Model::getMeshFromSlot(const std::string& slotType,int index) { std::vector result; OutfitDescription::SlotMap::const_iterator it=_outfit.getSlots().find(slotType); if (it==_outfit.getSlots().end()) return result; if ((int)it->second.size()>= index) return result; const std::string& slotName=it->second[index].getDefault(); CoreModel::SlotDescription* sl=getCoreModel()->getSlotFromTypeAndName(slotType,slotName); if (!sl) return result; return sl->_meshes; } bool Model::setInvisibleMesh(const std::string& meshName) { int coreMeshId = getCalCoreModel()->getCoreMeshId(meshName); if(coreMeshId < 0) { const CoreModel::Name2Filename& meshName2Filename = getCoreModel()->getMeshName2Filename(); if(meshName2Filename.find(meshName) == meshName2Filename.end()) { CalError::setLastError(CalError::INDEX_BUILD_FAILED, __FILE__, __LINE__); return false; } const std::string& filename = meshName2Filename.find(meshName)->second; //char str[200]; //sprintf(str, "Load Mesh \"%s\"", filename.c_str()); //NPROFILE_SAMPLE(str); NPROFILE_SAMPLE("Load Mesh"); coreMeshId = getCalCoreModel()->loadCoreMesh(filename, meshName); } if(coreMeshId < 0) return false; _invisibleMeshes.push_back(coreMeshId); return true; } bool Model::removeInvisibleMesh(const std::string& meshName) { int coreMeshId = getCalCoreModel()->getCoreMeshId(meshName); if(coreMeshId < 0) { const CoreModel::Name2Filename& meshName2Filename = getCoreModel()->getMeshName2Filename(); if(meshName2Filename.find(meshName) == meshName2Filename.end()) { CalError::setLastError(CalError::INDEX_BUILD_FAILED, __FILE__, __LINE__); osg::notify(osg::WARN) << "Model::removeInvisibleMesh: unable to find the filename associated with the mesh named " << meshName << std::endl; return false; } const std::string& filename = meshName2Filename.find(meshName)->second; coreMeshId = getCalCoreModel()->loadCoreMesh(filename, meshName); } if(coreMeshId < 0) { osg::notify(osg::WARN) << "Model::removeInvisibleMesh: unable to find a coreMeshId associated with the mesh named " << meshName << std::endl; return false; } for (int i=0;i<(int)_invisibleMeshes.size();i++) { if (_invisibleMeshes[i]==coreMeshId) { _invisibleMeshes.erase(_invisibleMeshes.begin()+i); break; } } return true; } bool Model::find(const std::vector& vector, int item) { for(std::vector::const_iterator i = vector.begin(); i != vector.end(); i++) if(*i == item) return true; return false; } bool Model::bindMesh(const CoreModel::MeshDescription& meshDescription) { const std::string &meshName = meshDescription.find("name")->second; if(_notify > osg::NOTICE) osg::notify(osg::DEBUG_INFO) << "Model::bindMesh: " << meshName << std::endl; //int coreMeshId = getCalCoreModel()->getCoreMeshId(meshName); if(meshDescription.find("collision") != meshDescription.end()) { if(!setInvisibleMesh(meshName)) osg::notify(osg::FATAL) << "setInvisibleMesh(" << meshName << ") failed " << CalError::getLastErrorDescription() < materialNames; for(CoreModel::MeshDescription::const_iterator pair = meshDescription.begin(); pair != meshDescription.end(); pair++) { const std::string& variable = pair->first; const std::string& value = pair->second; if(variable.substr(0, 8) == "material") materialNames.push_back(value); } if(!bindMaterials(meshName, materialNames)) osg::notify(osg::FATAL) << "bindMaterials(" << meshName << ") failed " << CalError::getLastErrorDescription() << " (hint: run with export OSG_NOTIFY_LEVEL=DEBUG and check the logs)" <second; if(_notify > osg::NOTICE) osg::notify(osg::DEBUG_INFO) << "Model::unBindMesh: " << meshName << std::endl; if(meshDescription.find("collision") != meshDescription.end()) { removeInvisibleMesh(meshName); removeCollisionMesh(meshName); } int coreMeshId = getCalCoreModel()->getCoreMeshId(meshName); if(coreMeshId < 0) return false; int size=(int)_activeMeshes.size(); for (int i=0; i < size; i++) { if (_activeMeshes[i]==coreMeshId) { _activeMeshes.erase(_activeMeshes.begin()+i); break; } } if(!_calModel->detachMesh(coreMeshId)) { osg::notify(osg::FATAL) << "Model::unBindMesh: failed to detach coreMeshId " << coreMeshId << ":" << CalError::getLastErrorDescription() << std::endl; return false; } if(!_coreModel->SubUsingMeshId(coreMeshId)) return false; if(_coreMeshId2Drawables.find(coreMeshId) == _coreMeshId2Drawables.end()) { osg::notify(osg::FATAL) << "Model::unBindMesh: no drawable for coreMeshId " << coreMeshId << std::endl; return false; } int nbDrawable2remove=_coreMeshId2Drawables[coreMeshId].size(); for (int i = 0; i < nbDrawable2remove; i++) { if(!removeDrawable(_coreMeshId2Drawables[coreMeshId][i].get())) { osg::notify(osg::FATAL) << "Model::unBindMesh: can't remove drawable number " << i << " for coreMeshId " << coreMeshId << std::endl; return false; } } _coreMeshId2Drawables.erase(coreMeshId); return true; } bool Model::getSlotListFromSlotType(const std::string& name,CoreModel::SlotBank& result) { OutfitDescription::SlotMap::iterator it=_outfit.getSlots().find(name); if (it == _outfit.getSlots().end()) return false; std::map::const_iterator slotFound=getCoreModel()->getSlots().find(name); if (slotFound==getCoreModel()->getSlots().end()) { osg::notify(osg::FATAL) << "Model::getSlotListFromSlotType: no slot named " << name << std::endl; return false; } const CoreModel::SlotBank& res=slotFound->second; result=res; return true; } bool Model::loadOutfit(OutfitDescription* outfit, std::vector *toBeIgnored) { if(getUpdateCallback() != NULL) { osg::notify(osg::FATAL) << "Model::loadOutfit: must be called before the create method" << std::endl; return false; } if (!outfit) return false; for(OutfitDescription::SlotMap::iterator slotMapIt = outfit->getSlots().begin(); slotMapIt != outfit->getSlots().end(); slotMapIt++) { std::string slotType = slotMapIt->first; for (OutfitDescription::SlotBank::iterator slotVectIt=slotMapIt->second.begin(); slotVectIt!=slotMapIt->second.end(); slotVectIt++) { const std::string& slotName=slotVectIt->getDefault(); if (!slotName.empty() ) { CoreModel::SlotDescription* slot=getCoreModel()->getSlotFromTypeAndName(slotType,slotName); if (!slot) { osg::notify(osg::FATAL) << "Model::loadOutfit: uislot type " << slotType << " and name " << slotName << " not found" << std::endl; return false; } std::vector& vec=slot->_meshes; for (std::vector::iterator it=vec.begin();it!=vec.end();it++) { std::map::const_iterator meshfile=getCoreModel()->getMeshes().find(*it); if (meshfile==getCoreModel()->getMeshes().end()) { osg::notify(osg::FATAL) << "Model::loadOutfit: mesh " << *it << " not found, check your meshs in lib" << std::endl; return false; } std::string meshName=*it; if(!setActiveMesh(meshName/*, slotName*/)) osg::notify(osg::FATAL) << "Model::loadOutfit: setActiveMesh(" << meshName << ") failed " << CalError::getLastErrorDescription() <second; bindMesh(meshDescription); } } } } /* for (std::vector::iterator it=getCoreModel()->getCommonMeshes().begin();it!=getCoreModel()->getCommonMeshes().end();it++) { const std::string& meshName=(*it)["name"]; // currently only for common mesh if (toBeIgnored) { int size = toBeIgnored->size(); int i; for (i = 0; i < size; i++) { const std::string name = (*toBeIgnored)[i]; if (name == meshName) break; } if (i != size) continue; } if(!setActiveMesh(meshName)) osg::notify(osg::FATAL) << "Model::loadOutfit: setActiveMesh(" << meshName << ") failed " << CalError::getLastErrorDescription() < &commonMeshes = getCoreModel()->getCommonMeshes(); for (std::vector::const_iterator it = commonMeshes.begin(); it != commonMeshes.end(); it++) { const std::string &meshName = (*it); // currently only for common mesh if (toBeIgnored) { int size = toBeIgnored->size(); int i; for (i = 0; i < size; i++) { const std::string &name = (*toBeIgnored)[i]; if (name == meshName) break; } if (i != size) continue; } if(!setActiveMesh(meshName)) osg::notify(osg::FATAL) << "Model::loadOutfit: setActiveMesh(" << meshName << ") failed " << CalError::getLastErrorDescription() << std::endl; const std::map &meshName2MeshDesc = getCoreModel()->getMeshes(); std::map::const_iterator itdesc = meshName2MeshDesc.find(meshName); if (itdesc == meshName2MeshDesc.end()) { osg::notify(osg::FATAL) << "Model::loadOutfit: " << meshName << " is not described" << std::endl; return false; } const CoreModel::MeshDescription &meshDesc = (*itdesc).second; bindMesh(meshDesc); } if (outfit!=&_outfit) _outfit=*outfit; return true; } void Model::unapplySlot(const std::string &_slotType, int _slotIndex) { const std::string &slotUsed = _outfit.getSlots()[_slotType][_slotIndex].getDefault(); if (slotUsed.empty()) return; CoreModel::SlotBank sl; getSlotListFromSlotType(_slotType, sl); CoreModel::SlotDescription &sd = sl[slotUsed]; for (std::vector::const_iterator it = sd.getMeshes().begin(); it != sd.getMeshes().end(); it++) { unBindMesh(getCoreModel()->getMeshes().find(*it)->second); } _outfit.getSlots()[_slotType][_slotIndex].setDefault(""); char str[200]; std::string stype; if (_slotType == "accessory") { sprintf(str, "%s%d", _slotType.c_str(), _slotIndex); stype = str; } else stype = _slotType; FlattenConf &flattenConf = slotName2FlattenConf_[stype]; flattenConf.clear(); slotDependencies_[stype].clear(); for (std::map >::iterator it = slotDependencies_.begin(); it != slotDependencies_.end(); it++) { const std::string &str1 = (*it).first; std::vector &vec = (*it).second; int size = vec.size(); for (int i = 0; i < size; i++) { const std::string &str2 = vec[i]; if (str2 == stype) { vec.erase( vec.begin() + i ); setupTLF(str1, 0); break; } } } if (!getUseVertexProgram()) fixNormalSW(0.02f); } bool Model::isSlotAlreadyApplied(const std::string &_slotType, const std::string &_slotName) { if (_slotName == "none") // slot none is a special case return false; // not this function is not the good way to resolve the problem to avoid multiple slot applied to the model OutfitDescription::SlotBank& slots = _outfit.getSlots()[_slotType]; for ( OutfitDescription::SlotBank::iterator it = slots.begin(); it != slots.end(); it++ ) if ( it->getDefault() == _slotName ) return true; return false; } bool Model::applySlot(const std::string &_slotType, const std::string &_slotName, int _slotIndex) { // int i, j; // // Get or create the outfit slot OutfitDescription::SlotBank& outfitSlotbank = _outfit.getSlots()[_slotType]; if(outfitSlotbank.size() <= (unsigned int)_slotIndex) { outfitSlotbank.resize(_slotIndex + 1); } OutfitDescription::Slot& outfitSlot = outfitSlotbank[_slotIndex]; const std::string currentUislot= outfitSlot.getDefault(); if (currentUislot == _slotName) return true; if (isSlotAlreadyApplied(_slotType,_slotName) ) { assert( 0 && "slot already applied !!!"); return false; } unapplySlot(_slotType, _slotIndex); CoreModel::SlotBank sl; getSlotListFromSlotType(_slotType, sl); // const CoreModel::SlotDescription &sld = sl[_slotName]; // setup meshes const std::vector &mesh = sl[_slotName].getMeshes(); for (std::vector::const_iterator it = mesh.begin(); it != mesh.end(); it++) { const std::string &meshName = *it; if (!setActiveMesh(meshName)) { osg::notify(osg::FATAL) << "error in setActiveMesh with mesh " << meshName << std::endl; assert(0); } int coreMeshId = getCalCoreModel()->getCoreMeshId(*it); const std::map &meshDesc = getCoreModel()->getMeshes(); bindMesh(meshDesc.find(meshName)->second); createSubMeshSoftware(coreMeshId); } outfitSlot.setDefault(_slotName); fixNormalSW(0.02f); return true; } void Model::setupLayers(const std::string &_slotType, const std::string &_slotName, int _slotIndex) { // int i, j; char str[200]; //return; #ifdef USE_NPROFILE osg::notify(osg::INFO) << "OSGCAL: entering setupLayers()" << std::endl; double time = nprf::GetRealTime(); #endif CoreModel::SlotBank sl; getSlotListFromSlotType(_slotType, sl); CoreModel::SlotBank::const_iterator itc=sl.find(_slotName); if (itc == sl.end()) { osg::notify(osg::FATAL) << "error slot " << _slotName << " not found in slot bank of type " << _slotType << std::endl; assert(0); } const CoreModel::SlotDescription &sld = itc->second; std::string stype; if (_slotType == "accessory") { sprintf(str, "%s%d", _slotType.c_str(), _slotIndex); stype = str; } else stype = _slotType; FlattenConf &flattenConf = slotName2FlattenConf_[stype]; flattenConf.clear(); const CoreModel::SlotDescription::TextureEntries &tdesc = sld._textures; for (CoreModel::SlotDescription::TextureEntries::const_iterator it = tdesc.begin(); it != tdesc.end(); it++) { const std::string &targetmap = (*it).first; const CoreModel::SlotDescription::TextureDescription &texDesc = (*it).second; TargetMap &targetMap = flattenConf[targetmap]; targetMap.colorMask = texDesc._mask; targetMap.alphaPart = texDesc._alpha; std::vector &layers = targetMap.layers; std::vector ¶ms = targetMap.params; layers.clear(); params.clear(); int nbLayers = texDesc._layers.size(); for (int i = 0; i < nbLayers; i++) { const CoreModel::SlotDescription::LayerDescription &layer = texDesc._layers[i]; const std::string &name = layer._layer; const std::string ¶m = layer._parameter; if (getCoreModel()->_layers.find(name) == getCoreModel()->_layers.end()) continue; const CoreModel::LayerDescription &layerDesc = getCoreModel()->_layers[name]; osg::Texture2D *texture = NULL; if (g_filename2texture.find(layerDesc._file) != g_filename2texture.end()) { texture = g_filename2texture[layerDesc._file].get(); } else { osg::Image *img = NULL; #ifdef USE_NPROFILE double time = nprf::GetRealTime(); #endif #ifdef OSGCAL_USE_BINARY_IMAGE_CACHE img = ImageCache::loadImage(layerDesc._file); #else img = osgDB::readImageFile(layerDesc._file); #endif #ifdef USE_NPROFILE time = nprf::GetRealTime() - time; osg::notify(osg::INFO) << " OSGCAL: Reading Image \"" << layerDesc._file << "\" took " << int(time*1000) << "ms" << std::endl; #endif if (!img) { osg::notify(osg::FATAL) << "Cannot read image file " << layerDesc._file << std::endl; continue; } bool bIsTIFF = layerDesc._file.rfind(".tif") == std::string::npos ? false : true; //if (i > 0 && bIsTIFF == true) if (bIsTIFF == true) InvertPremultipliedAlpha(*img); texture = new osg::Texture2D; texture->setImage(img); texture->setUnRefImageDataAfterApply(true); texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::NEAREST); texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::NEAREST); // if (layerDesc._textureFormat == osgCal::CoreModel::LayerDescription::TEXTUREFORMAT_RGB32) texture->setInternalFormatMode( osg::Texture::USE_IMAGE_DATA_FORMAT ); //texture->setInternalFormat( GL_RGBA4 ); /* else { if (layerDesc._textureFormat == osgCal::CoreModel::LayerDescription::TEXTUREFORMAT_RGB16) { if (img->getPixelFormat() == GL_RGBA) { texture->setInternalFormatMode( osg::Texture::USE_USER_DEFINED_FORMAT); texture->setInternalFormat( GL_RGBA4 ); } else { texture->setInternalFormatMode( osg::Texture::USE_USER_DEFINED_FORMAT); texture->setInternalFormat( GL_RGB5 ); } } else if (layerDesc._textureFormat == osgCal::CoreModel::LayerDescription::TEXTUREFORMAT_DXT1) texture->setInternalFormatMode( osg::Texture::USE_S3TC_DXT1_COMPRESSION ); else if (layerDesc._textureFormat == osgCal::CoreModel::LayerDescription::TEXTUREFORMAT_DXT3) texture->setInternalFormatMode( osg::Texture::USE_S3TC_DXT3_COMPRESSION ); else if (layerDesc._textureFormat == osgCal::CoreModel::LayerDescription::TEXTUREFORMAT_DXT5) texture->setInternalFormatMode( osg::Texture::USE_S3TC_DXT5_COMPRESSION ); } */ if (fxState_.valid()) { #ifdef USE_NPROFILE double time = nprf::GetRealTime(); #endif // texture->apply( *fxState_.get() ); #ifdef USE_NPROFILE time = nprf::GetRealTime() - time; osg::notify(osg::INFO) << " OSGCAL: Creating the texture took " << int(time*1000) << "ms" << std::endl; #endif } g_filename2texture[layerDesc._file] = texture; } TextureLayersFlatten::Layer lyr; lyr.pixelOp_ = TextureLayersFlatten::PIXELOP_NORMAL; if (layerDesc._mode == "overlay") lyr.pixelOp_ = TextureLayersFlatten::PIXELOP_OVERLAY; else if (layerDesc._mode == "additive") lyr.pixelOp_ = TextureLayersFlatten::PIXELOP_LINEARDODGE; else if (layerDesc._mode == "multiply") lyr.pixelOp_ = TextureLayersFlatten::PIXELOP_MULTIPLY; lyr.texture_ = texture; layers.push_back(lyr); if (param != "") { TargetMap::Param par; par.name = param; par.layerIndex = i; params.push_back(par); } } } #ifdef USE_NPROFILE time = nprf::GetRealTime() - time; osg::notify(osg::INFO) << "OSGCAL: exiting setupLayers (took " << int(time*1000) << "ms)" << std::endl; #endif } void Model::setupTLF(const std::string &_slotType, int _slotIndex) { char str[200]; #ifdef USE_NPROFILE osg::notify(osg::INFO) << "OSGCAL: entering setupTLF()" << std::endl; double time = nprf::GetRealTime(); #endif //return; std::string stype; if (_slotType == "accessory") { sprintf(str, "%s%d", _slotType.c_str(), _slotIndex); stype = str; } else stype = _slotType; FlattenConf &flattenConf = slotName2FlattenConf_[stype]; if (!g_tmpTexture.valid()) { g_tmpTexture = new osg::Texture2D(); g_tmpTexture->setInternalFormat(GL_RGB); g_tmpTexture->setTextureSize(TEXSIZE, TEXSIZE); g_tmpTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::NEAREST); g_tmpTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::NEAREST); } for (std::map::iterator it = flattenConf.begin(); it != flattenConf.end(); it++) { const std::string &tm = (*it).first; bool bAdditional = tm.rfind("_proxy") != std::string::npos ? true : false; const std::string &targetmap = convertProxyName( tm ); TargetMap &tmap = (*it).second; if (bAdditional == true) { // search the slot to be recalculated for (std::map::iterator itfc = slotName2FlattenConf_.begin(); itfc != slotName2FlattenConf_.end(); itfc++) { const std::string &slotType2 = (*itfc).first; FlattenConf &fconf2 = (*itfc).second; for (std::map::iterator ittm = fconf2.begin(); ittm != fconf2.end(); ittm++) { const std::string &tm = (*ittm).first; bool bAdditional = tm.rfind("_proxy") != std::string::npos ? true : false; const std::string &targetmap2 = convertProxyName( tm ); TargetMap &tmap2 = (*ittm).second; if (bAdditional == true || targetmap != targetmap2 || tmap2.colorMask != tmap.colorMask) continue; setupTLF(slotType2, 0); break; } } continue; } for (std::map >::iterator it = slotDependencies_.begin(); it != slotDependencies_.end(); it++) { const std::string &str1 = (*it).first; std::vector &vec = (*it).second; int size = vec.size(); for (int i = 0; i < size; i++) { const std::string &str2 = vec[i]; if (str2 == stype) { vec.erase( vec.begin() + i ); setupTLF(str1, 0); break; } } } tmap.tp_tlf = NULL; tmap.bFlushIfDependent = false; TextureLayersFlatten *tlf = tmap.tlf.get(); if (!tlf) { tlf = new TextureLayersFlatten(); tmap.tlf = tlf; } osg::Texture2D *colorMask = NULL; if (tmap.colorMask != "") { if (g_filename2texture.find(tmap.colorMask) != g_filename2texture.end()) { colorMask = g_filename2texture[tmap.colorMask].get(); } else { osg::Image *img; #ifdef USE_NPROFILE double time = nprf::GetRealTime(); #ifdef OSGCAL_USE_BINARY_IMAGE_CACHE img = ImageCache::loadImage(tmap.colorMask); #else img = osgDB::readImageFile(tmap.colorMask); #endif time = nprf::GetRealTime() - time; osg::notify(osg::INFO) << " OSGCAL: Reading Image \"" << tmap.colorMask << "\" took " << int(time*1000) << "ms" << std::endl; #else #ifdef OSGCAL_USE_BINARY_IMAGE_CACHE img = ImageCache::loadImage(tmap.colorMask); #else img = osgDB::readImageFile(tmap.colorMask); #endif #endif if (!img) { osg::notify(osg::WARN) << "Cannot read image file " << tmap.colorMask << std::endl; continue; } else { colorMask = new osg::Texture2D; colorMask->setImage(img); colorMask->setUnRefImageDataAfterApply(true); colorMask->setFilter(osg::Texture::MIN_FILTER, osg::Texture::NEAREST); colorMask->setFilter(osg::Texture::MAG_FILTER, osg::Texture::NEAREST); //colorMask->setInternalFormatMode(osg::Texture::USE_S3TC_DXT3_COMPRESSION); g_filename2texture[tmap.colorMask] = colorMask; } } } osg::Texture2D *targetMap = targetmap2texture_[targetmap].get(); if (!targetMap) { osg::notify(osg::WARN) << "Unknown target map " << targetmap; continue; } std::vector layers = tmap.layers; // look for dependencies for (std::map::iterator itfc = slotName2FlattenConf_.begin(); itfc != slotName2FlattenConf_.end(); itfc++) { const std::string &slotType2 = (*itfc).first; FlattenConf &fconf2 = (*itfc).second; for (std::map::iterator ittm = fconf2.begin(); ittm != fconf2.end(); ittm++) { const std::string &tm = (*ittm).first; bool bAdditional = tm.rfind("_proxy") != std::string::npos ? true : false; const std::string &targetmap2 = convertProxyName( tm ); TargetMap &tmap2 = (*ittm).second; if (bAdditional == false || targetmap != targetmap2 || tmap2.colorMask != tmap.colorMask) continue; int nbLayers = tmap2.layers.size(); tmap2.baseLayerIndex = layers.size(); tmap2.tp_tlf = tmap.tlf; tmap.bFlushIfDependent = true; slotDependencies_[_slotType].push_back(slotType2); for (int i = 0; i < nbLayers; i++) { TextureLayersFlatten::Layer &layer = tmap2.layers[i]; layers.push_back(layer); } } } osg::Texture2D *alphaPart = NULL; if (tmap.alphaPart != "") { if (g_filename2texture.find(tmap.alphaPart) != g_filename2texture.end()) { alphaPart = g_filename2texture[tmap.alphaPart].get(); } else { osg::Image *img; #ifdef USE_NPROFILE double time = nprf::GetRealTime(); #endif #ifdef OSGCAL_USE_BINARY_IMAGE_CACHE img = ImageCache::loadImage(tmap.alphaPart); #else img = osgDB::readImageFile(tmap.alphaPart); #endif #ifdef USE_NPROFILE time = nprf::GetRealTime() - time; osg::notify(osg::INFO) << " OSGCAL: Reading Image \"" << tmap.alphaPart << "\" took " << int(time*1000) << "ms" << std::endl; #endif if (!img) { osg::notify(osg::WARN) << "Cannot read image file " << tmap.alphaPart << std::endl; continue; } else { unsigned char *pixs = img->data(); int size = img->s() * img->t(); unsigned char *new_pixs = (unsigned char*) malloc(size); unsigned char *org_pixs = new_pixs; int nbBitsPerPixel = img->getPixelSizeInBits(); if (nbBitsPerPixel == 8) { for (int i = 0; i < size; i++) { unsigned char gray = *pixs++; *new_pixs++ = gray; } } else { for (int i = 0; i < size; i++) { unsigned char r = *pixs++; unsigned char g = *pixs++; unsigned char b = *pixs++; //unsigned char gray = (unsigned char)(r * 0.299f + g * 0.587f + b * 0.114f); unsigned char gray = ((r + g + b) * 341) >> 10; *new_pixs++ = gray; pixs += (nbBitsPerPixel >> 3) - 3; } } osg::Image *real = new osg::Image(); real->setImage( img->s(), img->t(), 1, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, org_pixs, osg::Image::USE_MALLOC_FREE); alphaPart = new osg::Texture2D; alphaPart->setImage(real); alphaPart->setUnRefImageDataAfterApply(true); alphaPart->setFilter(osg::Texture::MIN_FILTER, osg::Texture::NEAREST); alphaPart->setFilter(osg::Texture::MAG_FILTER, osg::Texture::NEAREST); g_filename2texture[tmap.alphaPart] = alphaPart; img->unref(); } } } #ifdef USE_NPROFILE double time = nprf::GetRealTime(); #endif if (colorMask) tlf->init(TEXSIZE, TEXSIZE, layers, fxGroup_.get(), g_tmpTexture.get(), colorMask, targetMap, alphaPart ); else tlf->init(TEXSIZE, TEXSIZE, layers, fxGroup_.get(), g_tmpTexture.get(), NULL, targetMap, alphaPart ); #ifdef USE_NPROFILE time = nprf::GetRealTime() - time; osg::notify(osg::INFO) << " OSGCAL: TLF::init took " << int(time*1000) << "ms" << std::endl; #endif } #ifdef USE_NPROFILE time = nprf::GetRealTime() - time; osg::notify(osg::INFO) << "OSGCAL: exiting setupTLF (took " << int(time*1000) << "ms)" << std::endl; #endif } void Model::freeAllLayersRessource() { slotDependencies_.clear(); for (std::map::iterator it = slotName2FlattenConf_.begin(); it != slotName2FlattenConf_.end(); ++it) { FlattenConf &fconf = (*it).second; fconf.clear(); } slotName2FlattenConf_.clear(); } void Model::freeLayersRessource() { TextureLayersFlatten::destroy(); g_filename2texture.clear(); g_tmpTexture = NULL; } bool Model::setParam(const std::string &name, const std::string &type, int value) { int i; CoreModel *coreModel = getCoreModel(); std::map::iterator parameter2description = coreModel->_parameters.find(name); if(parameter2description == coreModel->_parameters.end()) { osg::notify(osg::FATAL) << "Model::setParam: unknown parameter " << name << std::endl; return false; } CoreModel::ParameterDescription ¶mDesc = parameter2description->second; bool found = false; for (std::map::iterator it = slotName2FlattenConf_.begin(); it != slotName2FlattenConf_.end(); it++) { FlattenConf &conf = it->second; for (std::map::iterator itt = conf.begin(); itt != conf.end(); itt++) { TargetMap &tmap = itt->second; int nbParams = tmap.params.size(); for (i = 0; i < nbParams; i++) { const TargetMap::Param ¶m = tmap.params[i]; int layerIndex = param.layerIndex; if (param.name != name) continue; TextureLayersFlatten::BaseRenderTechnic *brt = NULL; if (tmap.tp_tlf.valid()) brt = tmap.tp_tlf->getBaseRenderTechnic(layerIndex + tmap.baseLayerIndex); else { if (tmap.tlf.valid()) brt = tmap.tlf->getBaseRenderTechnic(layerIndex); else continue; } if (!brt) continue; if (type == "color" || type == "color_set") { if (paramDesc._type == "color") { CoreModel::ParameterDescription::ColorElement &color = paramDesc._colors._colors[value]; float r = color[0] / 255.0f; float g = color[1] / 255.0f; float b = color[2] / 255.0f; osg::Vec3f osgCol(r, g, b); brt->setColorFactor( osgCol ); found = true; } } else if (type == "opacity") { if (value < 0) value = 0; else if (value > 100) value = 100; brt->setOpacity( value / 100.0f ); found = true; } else { osg::notify(osg::FATAL) << "Model::setParam: unknown type " << type << " for parameter " << name << std::endl; assert(0 && "Fix data or fix the code"); } if (tmap.tp_tlf.valid()) tmap.tp_tlf->flushTextureCacheForAllBRT(); else tmap.tlf->flushTextureCacheForAllBRT(); } } } if(!found) { osg::notify(osg::FATAL) << "Model::setParam(name = " << name << ", type = " << type << ", value = " << value << ") was not applied (missing setupTLF and setupLayers is the most likely cause)" << std::endl; // commented for the linux version 1.10 should be fixed in pokeroutfit.py //assert(0 && "Fix data or fix the code"); } return found; } void Model::flushTextureCache() { for (std::map::iterator it = slotName2FlattenConf_.begin(); it != slotName2FlattenConf_.end(); ++it) { FlattenConf &conf = (*it).second; for (std::map::iterator itt = conf.begin(); itt != conf.end(); itt++) { TargetMap &tmap = (*itt).second; tmap.tlf->flushTextureCacheForAllBRT(); } } } static void node2Attributes(xmlNodePtr node,std::map& map) { map.clear(); xmlAttr* attribute; for(attribute = node->properties; attribute; attribute = attribute->next) { const char* value = (const char*)xmlNodeGetContent((xmlNode*)attribute); const char* variable = (const char*)attribute->name; map[variable] = value; xmlFree((void*)value); } } static bool parseOutfit(osgCal::Model::OutfitDescription& outfit, xmlXPathContextPtr xpathContext, const std::string &xpath, int version, const std::string& pathOrString) { bool status = true; xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression((xmlChar*)xpath.c_str(), xpathContext); if(xpathObj == NULL || xpathObj->nodesetval == NULL) { osg::notify(osg::FATAL) << "Model::parseOutfit: " << xpath << " not found in " << pathOrString << std::endl; return false; } std::map attributes; xmlNodeSetPtr nodes = xpathObj->nodesetval; if (nodes->nodeNr == 1) { xmlNodePtr node = nodes->nodeTab[0]; node2Attributes(node, attributes); int outfit_version = 0; if(attributes.find("version") != attributes.end()) outfit_version = atoi(attributes["version"].c_str()); if(version != outfit_version) { xmlXPathFreeObject(xpathObj); osg::notify(osg::FATAL) << "Model::parseOutfit: version is " << outfit_version << ", expected version " << version << " while reading " << pathOrString << std::endl; return false; } std::set duplicatesSlot; int uislot_count = 0; for (xmlNodePtr onode = node->children; onode; onode = onode->next) { switch(onode->type) { case XML_COMMENT_NODE: { const char* value = (const char*)xmlNodeGetContent(onode); outfit._comment += value; } break; case XML_ELEMENT_NODE: { uislot_count++; std::string nodeName = (const char*)onode->name; if(nodeName != "uislot") { osg::notify(osg::FATAL) << "Model::parseOutfit: expected element 'uislot' and got '" << nodeName << "' at /outfit while reading " << pathOrString << std::endl; status = false; } std::map attributes; node2Attributes(onode, attributes); if(attributes.find("type") == attributes.end()) { osg::notify(osg::FATAL) << "Model::parseOutfit: missing type attribute for " << uislot_count << "th uislot at /outfit while reading " << pathOrString << std::endl; status = false; } std::string slotType = attributes["type"]; if(slotType != "accessory" && duplicatesSlot.find(slotType) != duplicatesSlot.end()) { osg::notify(osg::FATAL) << "Model::parseOutfit: duplicate slot type " << slotType << " at " << uislot_count << "th uislot at /outfit while reading " << pathOrString << std::endl; status = false; } else { if(attributes.find("default") == attributes.end()) { osg::notify(osg::FATAL) << "Model::parseOutfit: missing attribute 'default' for " << uislot_count << "th uislot at /outfit while reading " << pathOrString << std::endl; status = false; } duplicatesSlot.insert(slotType); osgCal::Model::OutfitDescription::Slot slot; slot._default = attributes["default"]; int value_count = 0; for(xmlNodePtr oonode = onode->children; oonode; oonode = oonode->next) { if(oonode->type == XML_ELEMENT_NODE) { if(!strcmp((const char*)oonode->name, "value")) { value_count++; node2Attributes(oonode, attributes); if(attributes.find("parameter") == attributes.end()) { osg::notify(osg::FATAL) << "Model::parseOutfit: missing attribute 'parameter' for " << value_count << "th value in slot " << slotType << " (" << uislot_count << "th uislot) at /outfit while reading " << pathOrString << std::endl; status = false; } if(attributes.find("type") == attributes.end()) { if(attributes.find("name") == attributes.end()) { osg::notify(osg::FATAL) << "Model::parseOutfit: missing attribute 'type' (or the backward compatibility alias 'name') for " << value_count << "th value in slot " << slotType << " (" << uislot_count << "th uislot) at /outfit while reading " << pathOrString << std::endl; status = false; } else { attributes["type"] = attributes["name"]; } } if(attributes.find("value") == attributes.end()) { osg::notify(osg::FATAL) << "Model::parseOutfit: missing attribute 'value' for " << value_count << "th value in slot " << slotType << " (" << uislot_count << "th uislot) at /outfit while reading " << pathOrString << std::endl; status = false; } const std::string& parameter = attributes["parameter"]; const std::string& type = attributes["type"]; int value = atoi(attributes["value"].c_str()); slot._params[ std::pair (parameter, type) ] = value; } else { osg::notify(osg::FATAL) << "Model::parseOutfit: expected element 'value' got " << (const char*)oonode->name << " for slot " << slotType << " (" << uislot_count << "th uislot), ignored at /outfit while reading " << pathOrString << std::endl; } } } outfit._slots[slotType].push_back(slot); } } break; default: break; } } } else { osg::notify(osg::FATAL) << "Model::parseOutfit: " << xpath << " found " << nodes->nodeNr << " nodes, expected exactly one" << std::endl; status = false; } xmlXPathFreeObject(xpathObj); return status; } static bool xmlStringDocAndXPath(const std::string& xmlString, xmlDocPtr& doc, xmlXPathContextPtr& xpathContext) { doc=xmlReadMemory(xmlString.c_str(),xmlString.size(),"/",0,0); if (!doc) { osg::notify(osg::FATAL)<< "error no xml document read from " << xmlString << std::endl; return false; } xpathContext = xmlXPathNewContext(doc); if(xpathContext == NULL) { osg::notify(osg::FATAL)<< "unable to create new XPath context " << std::endl; xmlFreeDoc(doc); doc = NULL; return false; } return true; } bool Model::initOutfitFromFile(const std::string &_fname, std::vector *_excludeMesh) { if (getUpdateCallback() != NULL) { osg::notify(osg::FATAL) << "Model::initOutfitFromFile: must be called before the create method" << std::endl; return false; } xmlDocPtr doc = xmlParseFile(_fname.c_str()); xmlXPathContextPtr xpathContext = xmlXPathNewContext(doc); bool status = true; if (!parseOutfit(_outfit, xpathContext, "/cal3d/outfit", 0, "")) { osg::notify(osg::FATAL) << "Model::initOutfitFromXMLString: failed to parse outfit" << std::endl; status = false; } xmlXPathFreeContext(xpathContext); xmlFreeDoc(doc); if (status) status = loadOutfit(&_outfit, _excludeMesh); return status; } bool Model::initOutfitFromXMLString(const std::string& xmlString, std::vector* excludeMesh) { if(getUpdateCallback() != NULL) { osg::notify(osg::FATAL) << "Model::initOutfitFromXMLString: must be called before the create method" << std::endl; return false; } #ifdef USE_NPROFILE double time = nprf::GetRealTime(); #endif xmlDocPtr doc = NULL; xmlXPathContextPtr xpathContext = NULL; if (!xmlStringDocAndXPath(xmlString, doc, xpathContext)) return false; bool status = true; if (!parseOutfit(_outfit, xpathContext, "/outfit", _coreModel->getVersion(), xmlString)) { osg::notify(osg::FATAL) << "Model::initOutfitFromXMLString: failed to parse outfit" << std::endl; status = false; } xmlFreeDoc(doc); xmlXPathFreeContext(xpathContext); if(status) status = loadOutfit(&_outfit, excludeMesh); #ifdef USE_NPROFILE time = nprf::GetRealTime() - time; osg::notify(osg::INFO) << "OSGCAL: initOutfitFromXMLString took " << int(time*1000) << "ms" << std::endl; #endif return status; } bool Model::installOutfitFromXMLString(const std::string& xmlString) { if(getUpdateCallback() == NULL) { osg::notify(osg::FATAL) << "Model::installOutfitFromXMLString: must be called after the create method" << std::endl; return false; } xmlDocPtr doc = NULL; xmlXPathContextPtr xpathContext = NULL; if(!xmlStringDocAndXPath(xmlString, doc, xpathContext)) return false; OutfitDescription outfit; if(!parseOutfit(outfit, xpathContext, "/outfit", _coreModel->getVersion(), xmlString)) { osg::notify(osg::FATAL) << "Model::installOutfitFromXMLString: failed to parse outfit" << std::endl; return false; } xmlFreeDoc(doc); xmlXPathFreeContext(xpathContext); bool status = applySlot("accessory", "none", 0) && applySlot("accessory", "none", 1) && applySlot("accessory", "none", 2); for(OutfitDescription::SlotMap::const_iterator type2uislots = outfit.getSlots().begin(); type2uislots != outfit.getSlots().end(); type2uislots++) { const std::string& type = type2uislots->first; const OutfitDescription::SlotBank& uislots = type2uislots->second; int index = 0; for(OutfitDescription::SlotBank::const_iterator uislot = uislots.begin(); uislot != uislots.end(); uislot++) { if(!applySlot(type, uislot->getDefault(), index)) status = false; setupLayers(type, uislot->getDefault(), index); NPROFILE_SAMPLE("setupTLF"); setupTLF(type, index); index++; } } for(OutfitDescription::SlotMap::const_iterator type2uislots = outfit.getSlots().begin(); type2uislots != outfit.getSlots().end(); type2uislots++) { const OutfitDescription::SlotBank& uislots = type2uislots->second; int index = 0; for(OutfitDescription::SlotBank::const_iterator uislot = uislots.begin(); uislot != uislots.end(); uislot++) { const OutfitDescription::Parameters& parameters = uislot->_params; for(OutfitDescription::Parameters::const_iterator parameter = parameters.begin(); parameter != parameters.end(); parameter++) { const std::string& name = parameter->first.first; const std::string& type = parameter->first.second; const int& value = parameter->second; if(!setParam(name, type, value)) status = false; } index++; } } _outfit = outfit; return status; } bool Model::applyParameterFromOutfitDescription() { if(getUpdateCallback() == NULL) { osg::notify(osg::FATAL) << "Model::applyParameterFromOutfitDescription: must be called after the create method" << std::endl; return false; } // loop on slotmap if(!fxGroup_.valid() || !fxState_.valid()) { osg::notify(osg::FATAL) << "applyParameterFromOutfitDescrition call setFXGroup() and setFXState() to provide valid pointers" << std::endl; return false; } #ifdef USE_NPROFILE double time = nprf::GetRealTime(); #endif bool status = true; for (OutfitDescription::SlotMap::iterator it = _outfit.getSlots().begin(); it != _outfit.getSlots().end(); it++ ) { const std::string& slot_type=it->first; int slot_index=0; // loop on vector slot need to loop only for accessory for (OutfitDescription::SlotBank::iterator itslot = it->second.begin(); itslot != it->second.end(); itslot++) { OutfitDescription::Slot& slot=*itslot; const std::string& slot_name=slot.getDefault(); setupLayers(slot_type, slot_name, slot_index); setupTLF(slot_type, slot_index); slot_index++; } } for (OutfitDescription::SlotMap::iterator it = _outfit.getSlots().begin(); it != _outfit.getSlots().end(); it++ ) { for (OutfitDescription::SlotBank::iterator itslot = it->second.begin(); itslot != it->second.end(); itslot++) { OutfitDescription::Slot &slot = *itslot; // loop on parameter for a slot for (OutfitDescription::Parameters::iterator itp = slot._params.begin(); itp != slot._params.end(); itp++) { const std::string ¶meter = itp->first.first; const std::string ¶meterType = itp->first.second; int parameterValue = itp->second; if(!setParam(parameter,parameterType,parameterValue)) status = false; } } } #ifdef USE_NPROFILE time = nprf::GetRealTime() - time; osg::notify(osg::INFO) << "OSGCAL: applyParameterFromOutfitDescription() took " << int(time*1000) << "ms" << std::endl; #endif return status; } namespace osgCal { struct RadixUserSW { int iMesh; CalVector readNormal; CalCoreSubmesh::Vertex *calVertex; }; } // took ~15ms for a model of ~5000 vertices // this is quite brute force, could be optimised a lot by pre-flagging vertices void Model::fixNormalSW(float _ptThreshold) { int i, j, k; CalCoreModel* calCoreModel = getCalCoreModel(); int nbActiveMeshes = _activeMeshes.size(); if(nbActiveMeshes == 0) return; int nbVertices = 0; for (i = 0; i < nbActiveMeshes; i++) { int coreMeshId = _activeMeshes[i]; CalCoreMesh *coreMesh = calCoreModel->getCoreMesh(coreMeshId); Drawables &drawables = _coreMeshId2Drawables[ coreMeshId ]; int submeshCount = coreMesh->getCoreSubmeshCount(); for (j = 0; j < submeshCount; j++) { SubMeshSoftware *geom = dynamic_cast (drawables[j].get()); if (geom && geom->isInvisible()) continue; CalCoreSubmesh *coreSubmesh = coreMesh->getCoreSubmesh(j); nbVertices += coreSubmesh->getVertexCount(); } } RadixUserSW *users = new RadixUserSW[nbVertices]; FloatRadix *radix = new FloatRadix(nbVertices); RadixFloatItem *items = new RadixFloatItem[nbVertices]; int iv = 0; for (i = 0; i < nbActiveMeshes; i++) { int coreMeshId = _activeMeshes[i]; CalCoreMesh *coreMesh = calCoreModel->getCoreMesh(coreMeshId); const std::string &name = coreMesh->getName(); int submeshCount = coreMesh->getCoreSubmeshCount(); Drawables &drawables = _coreMeshId2Drawables[ coreMeshId ]; for (j = 0; j < submeshCount; j++) { SubMeshSoftware *geom = dynamic_cast (drawables[j].get()); if (geom->isInvisible()) continue; // CalSubmesh *submesh = mesh->getSubmesh(j); CalCoreSubmesh *coreSubmesh = coreMesh->getCoreSubmesh(j); std::vector &vectorVertex = coreSubmesh->getVectorVertex(); int size = vectorVertex.size(); char str[200]; sprintf(str, "%s%d", name.c_str(), j); CalVector *readNormals = NULL; if(_coreModel->_name2Normal.find(str) != _coreModel->_name2Normal.end()) { readNormals = _coreModel->_name2Normal[str]; } else { osg::notify(osg::FATAL) << "fixNormalSW: no normals for " << str <iMesh = i; user->calVertex = &vectorVertex[k]; user->readNormal = readNormals[k]; items[iv].value = user->calVertex->position.x; items[iv].user.userSW = user; iv++; } } } RadixFloatItem **sorted = radix->sort(items, iv); std::vector sp; std::vector< std::vector > sameX; RadixUserSW *f = sorted[0]->user.userSW; sp.push_back(f); CalVector *cmp_point = &(f->calVertex->position); for (i = 1; i < iv; i++) { RadixUserSW *ruser = sorted[i]->user.userSW; CalVector *point = &ruser->calVertex->position; float diff_x = cmp_point->x - point->x; if (diff_x < _ptThreshold && diff_x > -_ptThreshold) { sp.push_back(ruser); } else { if (sp.size() > 1) sameX.push_back(sp); cmp_point = point; sp.clear(); sp.push_back(ruser); } } if (sp.size() > 1) sameX.push_back(sp); int nbSameX = sameX.size(); for (i = 0; i < nbSameX; i++) { std::vector &same = sameX[i]; int nbPoints = same.size(); std::vector samePoints; for (j = 0; j < nbPoints; j++) { samePoints.clear(); RadixUserSW *v0 = same[j]; samePoints.push_back( v0 ); for (k = j+1; k < nbPoints; k++) { RadixUserSW *v1 = same[k]; float diff_y = v0->calVertex->position.y - v1->calVertex->position.y; float diff_z = v0->calVertex->position.z - v1->calVertex->position.z; if (v0->iMesh != v1->iMesh && diff_y < _ptThreshold && diff_y > -_ptThreshold && diff_z < _ptThreshold && diff_z > -_ptThreshold) { samePoints.push_back( v1 ); } } int nbSamePoints = samePoints.size(); if (nbSamePoints > 1) { CalVector blendedNormal(0, 0, 0); for (k = 0; k < nbSamePoints; k++) { RadixUserSW *v = samePoints[k]; CalVector &normal = v->readNormal; blendedNormal += normal; } blendedNormal /= nbSamePoints; for (k = 0; k < nbSamePoints; k++) { RadixUserSW *v = samePoints[k]; v->calVertex->normal = blendedNormal; } } } } delete radix; delete [] items; delete [] users; } void Model::fixNormalHW(int _nbVertices, osg::Vec3f *_pos, osg::Vec3f *_normal, char *_iMesh, float _ptThreshold) { int i, j, k; CUSTOM_ASSERT(_nbVertices > 0); CUSTOM_ASSERT(_pos); CUSTOM_ASSERT(_normal); CUSTOM_ASSERT(_iMesh); FloatRadix *radix = new FloatRadix(_nbVertices); RadixFloatItem *items = new RadixFloatItem[_nbVertices]; for (i = 0; i < _nbVertices; i++) { items[i].value = _pos[i]._v[0]; items[i].user.userHW = i; } RadixFloatItem **sorted = radix->sort(items, _nbVertices); std::vector sp; std::vector< std::vector > sameX; sp.push_back( sorted[0]->user.userHW ); osg::Vec3f *cmp_point = &_pos[ sorted[0]->user.userHW ]; for (i = 1; i < _nbVertices; i++) { int rindex = sorted[i]->user.userHW; osg::Vec3f *point = &_pos[rindex]; float diff_x = cmp_point->_v[0] - point->_v[0]; if (diff_x < _ptThreshold && diff_x > -_ptThreshold) { sp.push_back(rindex); } else { if (sp.size() > 1) sameX.push_back(sp); cmp_point = point; sp.clear(); sp.push_back(rindex); } } if (sp.size() > 1) sameX.push_back(sp); int nbSameX = sameX.size(); for (i = 0; i < nbSameX; i++) { std::vector &same = sameX[i]; int nbPoints = same.size(); std::vector samePoints; for (j = 0; j < nbPoints; j++) { samePoints.clear(); int index0 = same[j]; osg::Vec3f &point0 = _pos[index0]; int imesh0 = _iMesh[index0]; samePoints.push_back( index0 ); for (k = j+1; k < nbPoints; k++) { int index1 = same[k]; osg::Vec3f &point1 = _pos[index1]; int imesh1 = _iMesh[index1]; float diff_y = point0._v[1] - point1._v[1]; float diff_z = point0._v[2] - point1._v[2]; if (imesh0 != imesh1 && diff_y < _ptThreshold && diff_y > -_ptThreshold && diff_z < _ptThreshold && diff_z > -_ptThreshold) { samePoints.push_back( index1 ); } } int nbSamePoints = samePoints.size(); if (nbSamePoints > 1) { osg::Vec3f blendedNormal(0, 0, 0); for (k = 0; k < nbSamePoints; k++) { int index = samePoints[k]; osg::Vec3f &normal = _normal[index]; blendedNormal += normal; } blendedNormal /= nbSamePoints; for (k = 0; k < nbSamePoints; k++) { int index = samePoints[k]; osg::Vec3f *normal = &_normal[index]; *normal = blendedNormal; } } } } delete radix; delete [] items; } union UnionType { char c; short s; int Int; float Float; char *pC; short *pS; int *pI; float *pF; }; struct RdxFloat32ByteMarker { int mask; int shift; } rdxFloat32ByteMarker[5] = { { 0xff, 0 }, { 0xff, 8 }, { 0x7f, 16 }, { 0xff, 23 }, { 0x1, 31 } }; FloatRadix::FloatRadix(int nbItems) { nbItems_ = nbItems; pTmpListPtr1_ = new RadixFloatItem*[nbItems]; pTmpListPtr2_ = new RadixFloatItem*[nbItems]; } FloatRadix::~FloatRadix() { delete [] pTmpListPtr1_; delete [] pTmpListPtr2_; } RadixFloatItem** FloatRadix::sort(RadixFloatItem *pItemList, int nbItems) { int radixIndex[257]; UnionType un; int i; RadixFloatItem **src = pTmpListPtr1_; RadixFloatItem **dst = pTmpListPtr2_; for (i = 0; i < nbItems; i++) src[i] = &pItemList[i]; for (int iPass = 0; iPass < 5; iPass++) { int mask = rdxFloat32ByteMarker[iPass].mask; int shift = rdxFloat32ByteMarker[iPass].shift; memset(radixIndex, 0, sizeof radixIndex); for (i = 0; i < nbItems; i++) { un.Float = src[i]->value; int n = (un.Int >> shift) & mask; if (iPass == 4) n = 1 - n; radixIndex[n + 1]++; } for (i = 1; i < 257; i++) radixIndex[i] += radixIndex[i - 1]; for (i = 0; i < nbItems; i++) { un.Float = src[i]->value; int n = (un.Int >> shift) & mask; if (iPass == 4) n = 1 - n; int index = radixIndex[n]++; dst[index] = src[i]; } RadixFloatItem **tmp = src; src = dst; dst = tmp; } // final very quick pass to reverse the order of negative values int nbNegative = radixIndex[0]; if (nbNegative > 1){ int a = 0; int b = nbNegative - 1; nbNegative >>= 1; while(nbNegative--) { RadixFloatItem *tmp; tmp = src[a]; src[a] = src[b]; src[b] = tmp; a++; b--; } } return src; }