/** * $Id: RAS_MeshObject.cpp 26841 2010-02-12 13:34:04Z campbellbarton $ * ***** BEGIN GPL LICENSE BLOCK ***** * * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. * * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. * All rights reserved. * * The Original Code is: all of this file. * * Contributor(s): none yet. * * ***** END GPL LICENSE BLOCK ***** */ #include "MEM_guardedalloc.h" #include "DNA_object_types.h" #include "DNA_key_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "RAS_MeshObject.h" #include "RAS_IRasterizer.h" #include "MT_MinMax.h" #include "MT_Point3.h" #include /* polygon sorting */ struct RAS_MeshObject::polygonSlot { float m_z; int m_index[4]; polygonSlot() {} /* pnorm is the normal from the plane equation that the distance from is * used to sort again. */ void get(const RAS_TexVert *vertexarray, const unsigned short *indexarray, int offset, int nvert, const MT_Vector3& pnorm) { MT_Vector3 center(0, 0, 0); int i; for(i=0; i b.m_z; } }; /* mesh object */ STR_String RAS_MeshObject::s_emptyname = ""; RAS_MeshObject::RAS_MeshObject(Mesh* mesh) : m_bModified(true), m_bMeshModified(true), m_mesh(mesh) { if (m_mesh && m_mesh->key) { KeyBlock *kb; int count=0; // initialize weight cache for shape objects // count how many keys in this mesh for(kb= (KeyBlock*)m_mesh->key->block.first; kb; kb= (KeyBlock*)kb->next) count++; m_cacheWeightIndex.resize(count,-1); } } RAS_MeshObject::~RAS_MeshObject() { vector::iterator it; if (m_mesh && m_mesh->key) { KeyBlock *kb; // remove the weight cache to avoid memory leak for(kb= (KeyBlock*)m_mesh->key->block.first; kb; kb= (KeyBlock*)kb->next) { if(kb->weights) MEM_freeN(kb->weights); kb->weights= NULL; } } for(it=m_Polygons.begin(); it!=m_Polygons.end(); it++) delete (*it); m_sharedvertex_map.clear(); m_Polygons.clear(); m_materials.clear(); } bool RAS_MeshObject::MeshModified() { return m_bMeshModified; } //unsigned int RAS_MeshObject::GetLightLayer() //{ // return m_lightlayer; //} int RAS_MeshObject::NumMaterials() { return m_materials.size(); } const STR_String& RAS_MeshObject::GetMaterialName(unsigned int matid) { RAS_MeshMaterial* mmat = GetMeshMaterial(matid); if(mmat) return mmat->m_bucket->GetPolyMaterial()->GetMaterialName(); return s_emptyname; } RAS_MeshMaterial* RAS_MeshObject::GetMeshMaterial(unsigned int matid) { if (m_materials.size() > 0 && (matid < m_materials.size())) { list::iterator it = m_materials.begin(); while (matid--) ++it; return &*it; } return NULL; } int RAS_MeshObject::NumPolygons() { return m_Polygons.size(); } RAS_Polygon* RAS_MeshObject::GetPolygon(int num) const { return m_Polygons[num]; } list::iterator GetFirstMaterial(); list::iterator GetLastMaterial(); list::iterator RAS_MeshObject::GetFirstMaterial() { return m_materials.begin(); } list::iterator RAS_MeshObject::GetLastMaterial() { return m_materials.end(); } void RAS_MeshObject::SetName(const char *name) { m_name = name; } STR_String& RAS_MeshObject::GetName() { return m_name; } const STR_String& RAS_MeshObject::GetTextureName(unsigned int matid) { RAS_MeshMaterial* mmat = GetMeshMaterial(matid); if(mmat) return mmat->m_bucket->GetPolyMaterial()->GetTextureName(); return s_emptyname; } RAS_MeshMaterial *RAS_MeshObject::GetMeshMaterial(RAS_IPolyMaterial *mat) { list::iterator mit; /* find a mesh material */ for(mit = m_materials.begin(); mit != m_materials.end(); mit++) if(mit->m_bucket->GetPolyMaterial() == mat) return &*mit; return NULL; } int RAS_MeshObject::GetMaterialId(RAS_IPolyMaterial *mat) { list::iterator mit; int imat; /* find a mesh material */ for(imat=0, mit = m_materials.begin(); mit != m_materials.end(); mit++, imat++) if(mit->m_bucket->GetPolyMaterial() == mat) return imat; return -1; } RAS_Polygon* RAS_MeshObject::AddPolygon(RAS_MaterialBucket *bucket, int numverts) { RAS_MeshMaterial *mmat; RAS_Polygon *poly; RAS_MeshSlot *slot; /* find a mesh material */ mmat = GetMeshMaterial(bucket->GetPolyMaterial()); /* none found, create a new one */ if(!mmat) { RAS_MeshMaterial meshmat; meshmat.m_bucket = bucket; meshmat.m_baseslot = meshmat.m_bucket->AddMesh(numverts); meshmat.m_baseslot->m_mesh = this; m_materials.push_back(meshmat); mmat = &m_materials.back(); } /* add it to the bucket, this also adds new display arrays */ slot = mmat->m_baseslot; slot->AddPolygon(numverts); /* create a new polygon */ RAS_DisplayArray *darray = slot->CurrentDisplayArray(); poly = new RAS_Polygon(bucket, darray, numverts); m_Polygons.push_back(poly); return poly; } void RAS_MeshObject::DebugColor(unsigned int abgr) { /*int numpolys = NumPolygons(); for (int i=0;iVertexCount();v++) RAS_TexVert* vtx = poly->GetVertex(v)->setDebugRGBA(abgr); } */ /* m_debugcolor = abgr; */ } void RAS_MeshObject::SetVertexColor(RAS_IPolyMaterial* mat,MT_Vector4 rgba) { RAS_MeshMaterial *mmat = GetMeshMaterial(mat); RAS_MeshSlot *slot = mmat->m_baseslot; RAS_MeshSlot::iterator it; size_t i; for(slot->begin(it); !slot->end(it); slot->next(it)) for(i=it.startvertex; iGetMaterial()->GetPolyMaterial()); slot = mmat->m_baseslot; darray = slot->CurrentDisplayArray(); { /* Shared Vertex! */ /* find vertices shared between faces, with the restriction * that they exist in the same display array, and have the * same uv coordinate etc */ vector& sharedmap = m_sharedvertex_map[origindex]; vector::iterator it; for(it = sharedmap.begin(); it != sharedmap.end(); it++) { if(it->m_darray != darray) continue; if(!it->m_darray->m_vertex[it->m_offset].closeTo(&texvert)) continue; /* found one, add it and we're done */ if(poly->IsVisible()) slot->AddPolygonVertex(it->m_offset); poly->SetVertexOffset(i, it->m_offset); return; } } /* no shared vertex found, add a new one */ offset = slot->AddVertex(texvert); if(poly->IsVisible()) slot->AddPolygonVertex(offset); poly->SetVertexOffset(i, offset); { /* Shared Vertex! */ SharedVertex shared; shared.m_darray = darray; shared.m_offset = offset; m_sharedvertex_map[origindex].push_back(shared); } } int RAS_MeshObject::NumVertices(RAS_IPolyMaterial* mat) { RAS_MeshMaterial *mmat; RAS_MeshSlot *slot; RAS_MeshSlot::iterator it; size_t len = 0; mmat = GetMeshMaterial(mat); slot = mmat->m_baseslot; for(slot->begin(it); !slot->end(it); slot->next(it)) len += it.endvertex - it.startvertex; return len; } RAS_TexVert* RAS_MeshObject::GetVertex(unsigned int matid, unsigned int index) { RAS_MeshMaterial *mmat; RAS_MeshSlot *slot; RAS_MeshSlot::iterator it; size_t len; mmat = GetMeshMaterial(matid); if(!mmat) return NULL; slot = mmat->m_baseslot; len = 0; for(slot->begin(it); !slot->end(it); slot->next(it)) { if(index >= len + it.endvertex - it.startvertex) len += it.endvertex - it.startvertex; else return &it.vertex[index - len]; } return NULL; } const float* RAS_MeshObject::GetVertexLocation(unsigned int orig_index) { vector& sharedmap = m_sharedvertex_map[orig_index]; vector::iterator it= sharedmap.begin(); return it->m_darray->m_vertex[it->m_offset].getXYZ(); } void RAS_MeshObject::AddMeshUser(void *clientobj, SG_QList *head, RAS_Deformer* deformer) { list::iterator it; list::iterator mit; for(it = m_materials.begin();it!=m_materials.end();++it) { /* always copy from the base slot, which is never removed * since new objects can be created with the same mesh data */ if (deformer && !deformer->UseVertexArray()) { // HACK! // this deformer doesn't use vertex array => derive mesh // we must keep only the mesh slots that have unique material id // this is to match the derived mesh drawing function // Need a better solution in the future: scan the derive mesh and create vertex array RAS_IPolyMaterial* curmat = it->m_bucket->GetPolyMaterial(); if (curmat->GetFlag() & RAS_BLENDERGLSL) { for(mit = m_materials.begin(); mit != it; ++mit) { RAS_IPolyMaterial* mat = mit->m_bucket->GetPolyMaterial(); if ((mat->GetFlag() & RAS_BLENDERGLSL) && mat->GetMaterialIndex() == curmat->GetMaterialIndex()) // no need to convert current mesh slot break; } if (mit != it) continue; } } RAS_MeshSlot *ms = it->m_bucket->CopyMesh(it->m_baseslot); ms->m_clientObj = clientobj; ms->SetDeformer(deformer); it->m_slots.insert(clientobj, ms); head->QAddBack(ms); } } void RAS_MeshObject::RemoveFromBuckets(void *clientobj) { list::iterator it; for(it = m_materials.begin();it!=m_materials.end();++it) { RAS_MeshSlot **msp = it->m_slots[clientobj]; if(!msp) continue; RAS_MeshSlot *ms = *msp; it->m_bucket->RemoveMesh(ms); it->m_slots.remove(clientobj); } } //void RAS_MeshObject::Transform(const MT_Transform& trans) //{ //m_trans.translate(MT_Vector3(0,0,1));//.operator *=(trans); // for (int i=0;iTransform(trans); // } //} /* void RAS_MeshObject::RelativeTransform(const MT_Vector3& vec) { for (int i=0;iRelativeTransform(vec); } } */ void RAS_MeshObject::SortPolygons(RAS_MeshSlot& ms, const MT_Transform &transform) { // Limitations: sorting is quite simple, and handles many // cases wrong, partially due to polygons being sorted per // bucket. // // a) mixed triangles/quads are sorted wrong // b) mixed materials are sorted wrong // c) more than 65k faces are sorted wrong // d) intersecting objects are sorted wrong // e) intersecting polygons are sorted wrong // // a) can be solved by making all faces either triangles or quads // if they need to be z-sorted. c) could be solved by allowing // larger buckets, b) and d) cannot be solved easily if we want // to avoid excessive state changes while drawing. e) would // require splitting polygons. RAS_MeshSlot::iterator it; size_t j; for(ms.begin(it); !ms.end(it); ms.next(it)) { unsigned int nvert = (int)it.array->m_type; unsigned int totpoly = it.totindex/nvert; if(totpoly <= 1) continue; if(it.array->m_type == RAS_DisplayArray::LINE) continue; // Extract camera Z plane... const MT_Vector3 pnorm(transform.getBasis()[2]); // unneeded: const MT_Scalar pval = transform.getOrigin()[2]; vector slots(totpoly); /* get indices and z into temporary array */ for(j=0; jdefbase.first; curdef; curdef=(bDeformGroup*)curdef->next, index++) if (!strcmp(curdef->name, vgroup)) return index; return -1; } void RAS_MeshObject::CheckWeightCache(Object* obj) { KeyBlock *kb; int kbindex, defindex; MDeformVert *dvert= NULL; int totvert, i, j; float *weights; if (!m_mesh->key) return; for(kbindex=0, kb= (KeyBlock*)m_mesh->key->block.first; kb; kb= (KeyBlock*)kb->next, kbindex++) { // first check the cases where the weight must be cleared if (kb->vgroup[0] == 0 || m_mesh->dvert == NULL || (defindex = get_def_index(obj, kb->vgroup)) == -1) { if (kb->weights) { MEM_freeN(kb->weights); kb->weights = NULL; } m_cacheWeightIndex[kbindex] = -1; } else if (m_cacheWeightIndex[kbindex] != defindex) { // a weight array is required but the cache is not matching if (kb->weights) { MEM_freeN(kb->weights); kb->weights = NULL; } dvert= m_mesh->dvert; totvert= m_mesh->totvert; weights= (float*)MEM_callocN(totvert*sizeof(float), "weights"); for (i=0; i < totvert; i++, dvert++) { for(j=0; jtotweight; j++) { if (dvert->dw[j].def_nr == defindex) { weights[i]= dvert->dw[j].weight; break; } } } kb->weights = weights; m_cacheWeightIndex[kbindex] = defindex; } } }