2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
26 ===========================================================================
29 #include "../idlib/precompiled.h"
33 #include "Model_local.h"
34 #include "Model_ase.h"
35 #include "Model_lwo.h"
38 idCVar idRenderModelStatic::r_mergeModelSurfaces( "r_mergeModelSurfaces", "1", CVAR_BOOL|CVAR_RENDERER, "combine model surfaces with the same material" );
39 idCVar idRenderModelStatic::r_slopVertex( "r_slopVertex", "0.01", CVAR_RENDERER, "merge xyz coordinates this far apart" );
40 idCVar idRenderModelStatic::r_slopTexCoord( "r_slopTexCoord", "0.001", CVAR_RENDERER, "merge texture coordinates this far apart" );
41 idCVar idRenderModelStatic::r_slopNormal( "r_slopNormal", "0.02", CVAR_RENDERER, "merge normals that dot less than this" );
45 idRenderModelStatic::idRenderModelStatic
48 idRenderModelStatic::idRenderModelStatic() {
51 lastModifiedFrame = 0;
52 lastArchivedFrame = 0;
55 isStaticWorldModel = false;
60 levelLoadReferenced = false;
66 idRenderModelStatic::~idRenderModelStatic
69 idRenderModelStatic::~idRenderModelStatic() {
75 idRenderModelStatic::Print
78 void idRenderModelStatic::Print() const {
79 common->Printf( "%s\n", name.c_str() );
80 common->Printf( "Static model.\n" );
81 common->Printf( "bounds: (%f %f %f) to (%f %f %f)\n",
82 bounds[0][0], bounds[0][1], bounds[0][2],
83 bounds[1][0], bounds[1][1], bounds[1][2] );
85 common->Printf( " verts tris material\n" );
86 for ( int i = 0 ; i < NumSurfaces() ; i++ ) {
87 const modelSurface_t *surf = Surface( i );
89 srfTriangles_t *tri = surf->geometry;
90 const idMaterial *material = surf->shader;
93 common->Printf( "%2i: %s, NULL surface geometry\n", i, material->GetName() );
97 common->Printf( "%2i: %5i %5i %s", i, tri->numVerts, tri->numIndexes / 3, material->GetName() );
98 if ( tri->generateNormals ) {
99 common->Printf( " (smoothed)\n" );
101 common->Printf( "\n" );
108 idRenderModelStatic::Memory
111 int idRenderModelStatic::Memory() const {
114 totalBytes += sizeof( *this );
115 totalBytes += name.DynamicMemoryUsed();
116 totalBytes += surfaces.MemoryUsed();
119 totalBytes += R_TriSurfMemory( shadowHull );
122 for ( int j = 0 ; j < NumSurfaces() ; j++ ) {
123 const modelSurface_t *surf = Surface( j );
124 if ( !surf->geometry ) {
127 totalBytes += R_TriSurfMemory( surf->geometry );
135 idRenderModelStatic::List
138 void idRenderModelStatic::List() const {
143 totalBytes = Memory();
146 for ( int j = 0 ; j < NumSurfaces() ; j++ ) {
147 const modelSurface_t *surf = Surface( j );
148 if ( !surf->geometry ) {
151 if ( !surf->geometry->perfectHull ) {
154 totalTris += surf->geometry->numIndexes / 3;
155 totalVerts += surf->geometry->numVerts;
157 common->Printf( "%c%4ik %3i %4i %4i %s", closed, totalBytes/1024, NumSurfaces(), totalVerts, totalTris, Name() );
159 if ( IsDynamicModel() == DM_CACHED ) {
160 common->Printf( " (DM_CACHED)" );
162 if ( IsDynamicModel() == DM_CONTINUOUS ) {
163 common->Printf( " (DM_CONTINUOUS)" );
166 common->Printf( " (DEFAULTED)" );
168 if ( bounds[0][0] >= bounds[1][0] ) {
169 common->Printf( " (EMPTY BOUNDS)" );
171 if ( bounds[1][0] - bounds[0][0] > 100000 ) {
172 common->Printf( " (HUGE BOUNDS)" );
175 common->Printf( "\n" );
180 idRenderModelStatic::IsDefaultModel
183 bool idRenderModelStatic::IsDefaultModel() const {
192 static void AddCubeFace( srfTriangles_t *tri, idVec3 v1, idVec3 v2, idVec3 v3, idVec3 v4 ) {
193 tri->verts[tri->numVerts+0].Clear();
194 tri->verts[tri->numVerts+0].xyz = v1 * 8;
195 tri->verts[tri->numVerts+0].st[0] = 0;
196 tri->verts[tri->numVerts+0].st[1] = 0;
198 tri->verts[tri->numVerts+1].Clear();
199 tri->verts[tri->numVerts+1].xyz = v2 * 8;
200 tri->verts[tri->numVerts+1].st[0] = 1;
201 tri->verts[tri->numVerts+1].st[1] = 0;
203 tri->verts[tri->numVerts+2].Clear();
204 tri->verts[tri->numVerts+2].xyz = v3 * 8;
205 tri->verts[tri->numVerts+2].st[0] = 1;
206 tri->verts[tri->numVerts+2].st[1] = 1;
208 tri->verts[tri->numVerts+3].Clear();
209 tri->verts[tri->numVerts+3].xyz = v4 * 8;
210 tri->verts[tri->numVerts+3].st[0] = 0;
211 tri->verts[tri->numVerts+3].st[1] = 1;
213 tri->indexes[tri->numIndexes+0] = tri->numVerts + 0;
214 tri->indexes[tri->numIndexes+1] = tri->numVerts + 1;
215 tri->indexes[tri->numIndexes+2] = tri->numVerts + 2;
216 tri->indexes[tri->numIndexes+3] = tri->numVerts + 0;
217 tri->indexes[tri->numIndexes+4] = tri->numVerts + 2;
218 tri->indexes[tri->numIndexes+5] = tri->numVerts + 3;
221 tri->numIndexes += 6;
226 idRenderModelStatic::MakeDefaultModel
229 void idRenderModelStatic::MakeDefaultModel() {
233 // throw out any surfaces we already have
236 // create one new surface
239 srfTriangles_t *tri = R_AllocStaticTriSurf();
241 surf.shader = tr.defaultMaterial;
244 R_AllocStaticTriSurfVerts( tri, 24 );
245 R_AllocStaticTriSurfIndexes( tri, 36 );
247 AddCubeFace( tri, idVec3(-1, 1, 1), idVec3(1, 1, 1), idVec3(1, -1, 1), idVec3(-1, -1, 1) );
248 AddCubeFace( tri, idVec3(-1, 1, -1), idVec3(-1, -1, -1), idVec3(1, -1, -1), idVec3(1, 1, -1) );
250 AddCubeFace( tri, idVec3(1, -1, 1), idVec3(1, 1, 1), idVec3(1, 1, -1), idVec3(1, -1, -1) );
251 AddCubeFace( tri, idVec3(-1, -1, 1), idVec3(-1, -1, -1), idVec3(-1, 1, -1), idVec3(-1, 1, 1) );
253 AddCubeFace( tri, idVec3(-1, -1, 1), idVec3(1, -1, 1), idVec3(1, -1, -1), idVec3(-1, -1, -1) );
254 AddCubeFace( tri, idVec3(-1, 1, 1), idVec3(-1, 1, -1), idVec3(1, 1, -1), idVec3(1, 1, 1) );
256 tri->generateNormals = true;
264 idRenderModelStatic::PartialInitFromFile
267 void idRenderModelStatic::PartialInitFromFile( const char *fileName ) {
269 InitFromFile( fileName );
274 idRenderModelStatic::InitFromFile
277 void idRenderModelStatic::InitFromFile( const char *fileName ) {
281 InitEmpty( fileName );
283 // FIXME: load new .proc map format
285 name.ExtractFileExtension( extension );
287 if ( extension.Icmp( "ase" ) == 0 ) {
288 loaded = LoadASE( name );
290 } else if ( extension.Icmp( "lwo" ) == 0 ) {
291 loaded = LoadLWO( name );
293 } else if ( extension.Icmp( "flt" ) == 0 ) {
294 loaded = LoadFLT( name );
296 } else if ( extension.Icmp( "ma" ) == 0 ) {
297 loaded = LoadMA( name );
300 common->Warning( "idRenderModelStatic::InitFromFile: unknown type for model: \'%s\'", name.c_str() );
305 common->Warning( "Couldn't load model: '%s'", name.c_str() );
310 // it is now available for use
313 // create the bounds for culling and dynamic surface creation
319 idRenderModelStatic::LoadModel
322 void idRenderModelStatic::LoadModel() {
324 InitFromFile( name );
329 idRenderModelStatic::InitEmpty
332 void idRenderModelStatic::InitEmpty( const char *fileName ) {
333 // model names of the form _area* are static parts of the
334 // world, and have already been considered for optimized shadows
335 // other model names are inline entity models, and need to be
337 if ( !idStr::Cmpn( fileName, "_area", 5 ) ) {
338 isStaticWorldModel = true;
340 isStaticWorldModel = false;
344 reloadable = false; // if it didn't come from a file, we can't reload it
352 idRenderModelStatic::AddSurface
355 void idRenderModelStatic::AddSurface( modelSurface_t surface ) {
356 surfaces.Append( surface );
357 if ( surface.geometry ) {
358 bounds += surface.geometry->bounds;
364 idRenderModelStatic::Name
367 const char *idRenderModelStatic::Name() const {
373 idRenderModelStatic::Timestamp
376 ID_TIME_T idRenderModelStatic::Timestamp() const {
382 idRenderModelStatic::NumSurfaces
385 int idRenderModelStatic::NumSurfaces() const {
386 return surfaces.Num();
391 idRenderModelStatic::NumBaseSurfaces
394 int idRenderModelStatic::NumBaseSurfaces() const {
395 return surfaces.Num() - overlaysAdded;
400 idRenderModelStatic::Surface
403 const modelSurface_t *idRenderModelStatic::Surface( int surfaceNum ) const {
404 return &surfaces[surfaceNum];
409 idRenderModelStatic::AllocSurfaceTriangles
412 srfTriangles_t *idRenderModelStatic::AllocSurfaceTriangles( int numVerts, int numIndexes ) const {
413 srfTriangles_t *tri = R_AllocStaticTriSurf();
414 R_AllocStaticTriSurfVerts( tri, numVerts );
415 R_AllocStaticTriSurfIndexes( tri, numIndexes );
421 idRenderModelStatic::FreeSurfaceTriangles
424 void idRenderModelStatic::FreeSurfaceTriangles( srfTriangles_t *tris ) const {
425 R_FreeStaticTriSurf( tris );
430 idRenderModelStatic::ShadowHull
433 srfTriangles_t *idRenderModelStatic::ShadowHull() const {
439 idRenderModelStatic::IsStaticWorldModel
442 bool idRenderModelStatic::IsStaticWorldModel() const {
443 return isStaticWorldModel;
448 idRenderModelStatic::IsDynamicModel
451 dynamicModel_t idRenderModelStatic::IsDynamicModel() const {
452 // dynamic subclasses will override this
458 idRenderModelStatic::IsReloadable
461 bool idRenderModelStatic::IsReloadable() const {
467 idRenderModelStatic::Bounds
470 idBounds idRenderModelStatic::Bounds( const struct renderEntity_s *mdef ) const {
476 idRenderModelStatic::DepthHack
479 float idRenderModelStatic::DepthHack() const {
485 idRenderModelStatic::InstantiateDynamicModel
488 idRenderModel *idRenderModelStatic::InstantiateDynamicModel( const struct renderEntity_s *ent, const struct viewDef_s *view, idRenderModel *cachedModel ) {
493 common->Error( "InstantiateDynamicModel called on static model '%s'", name.c_str() );
499 idRenderModelStatic::NumJoints
502 int idRenderModelStatic::NumJoints( void ) const {
508 idRenderModelStatic::GetJoints
511 const idMD5Joint *idRenderModelStatic::GetJoints( void ) const {
517 idRenderModelStatic::GetJointHandle
520 jointHandle_t idRenderModelStatic::GetJointHandle( const char *name ) const {
521 return INVALID_JOINT;
526 idRenderModelStatic::GetJointName
529 const char * idRenderModelStatic::GetJointName( jointHandle_t handle ) const {
535 idRenderModelStatic::GetDefaultPose
538 const idJointQuat *idRenderModelStatic::GetDefaultPose( void ) const {
544 idRenderModelStatic::NearestJoint
547 int idRenderModelStatic::NearestJoint( int surfaceNum, int a, int b, int c ) const {
548 return INVALID_JOINT;
552 //=====================================================================
557 idRenderModelStatic::FinishSurfaces
559 The mergeShadows option allows surfaces with different textures to share
560 silhouette edges for shadow calculation, instead of leaving shared edges
563 If any of the original shaders have the noSelfShadow flag set, the surfaces
564 can't be merged, because they will need to be drawn in different order.
566 If there is only one surface, a separate merged surface won't be generated.
568 A model with multiple surfaces can't later have a skinned shader change the
569 state of the noSelfShadow flag.
573 Creates mirrored copies of two sided surfaces with normal maps, which would
574 otherwise light funny.
576 Extends the bounds of deformed surfaces so they don't cull incorrectly at screen edges.
580 void idRenderModelStatic::FinishSurfaces() {
582 int totalVerts, totalIndexes;
586 // make sure we don't have a huge bounds even if we don't finish everything
589 if ( surfaces.Num() == 0 ) {
593 // renderBump doesn't care about most of this
596 for ( i = 0 ; i < surfaces.Num() ; i++ ) {
597 const modelSurface_t *surf = &surfaces[i];
599 R_BoundTriSurf( surf->geometry );
600 bounds.AddBounds( surf->geometry->bounds );
606 // cleanup all the final surfaces, but don't create sil edges
610 // decide if we are going to merge all the surfaces into one shadower
611 int numOriginalSurfaces = surfaces.Num();
613 // make sure there aren't any NULL shaders or geometry
614 for ( i = 0 ; i < numOriginalSurfaces ; i++ ) {
615 const modelSurface_t *surf = &surfaces[i];
617 if ( surf->geometry == NULL || surf->shader == NULL ) {
619 common->Error( "Model %s, surface %i had NULL geometry", name.c_str(), i );
621 if ( surf->shader == NULL ) {
623 common->Error( "Model %s, surface %i had NULL shader", name.c_str(), i );
627 // duplicate and reverse triangles for two sided bump mapped surfaces
628 // note that this won't catch surfaces that have their shaders dynamically
629 // changed, and won't work with animated models.
630 // It is better to create completely separate surfaces, rather than
631 // add vertexes and indexes to the existing surface, because the
632 // tangent generation wouldn't like the acute shared edges
633 for ( i = 0 ; i < numOriginalSurfaces ; i++ ) {
634 const modelSurface_t *surf = &surfaces[i];
636 if ( surf->shader->ShouldCreateBackSides() ) {
637 srfTriangles_t *newTri;
639 newTri = R_CopyStaticTriSurf( surf->geometry );
640 R_ReverseTriangles( newTri );
642 modelSurface_t newSurf;
644 newSurf.shader = surf->shader;
645 newSurf.geometry = newTri;
647 AddSurface( newSurf );
651 // clean the surfaces
652 for ( i = 0 ; i < surfaces.Num() ; i++ ) {
653 const modelSurface_t *surf = &surfaces[i];
655 R_CleanupTriangles( surf->geometry, surf->geometry->generateNormals, true, surf->shader->UseUnsmoothedTangents() );
656 if ( surf->shader->SurfaceCastsShadow() ) {
657 totalVerts += surf->geometry->numVerts;
658 totalIndexes += surf->geometry->numIndexes;
662 // add up the total surface area for development information
663 for ( i = 0 ; i < surfaces.Num() ; i++ ) {
664 const modelSurface_t *surf = &surfaces[i];
665 srfTriangles_t *tri = surf->geometry;
667 for ( int j = 0 ; j < tri->numIndexes ; j += 3 ) {
668 float area = idWinding::TriangleArea( tri->verts[tri->indexes[j]].xyz,
669 tri->verts[tri->indexes[j+1]].xyz, tri->verts[tri->indexes[j+2]].xyz );
670 const_cast<idMaterial *>(surf->shader)->AddToSurfaceArea( area );
674 // calculate the bounds
675 if ( surfaces.Num() == 0 ) {
679 for ( i = 0 ; i < surfaces.Num() ; i++ ) {
680 modelSurface_t *surf = &surfaces[i];
682 // if the surface has a deformation, increase the bounds
683 // the amount here is somewhat arbitrary, designed to handle
684 // autosprites and flares, but could be done better with exact
685 // deformation information.
686 // Note that this doesn't handle deformations that are skinned in
688 if ( surf->shader->Deform() != DFRM_NONE ) {
689 srfTriangles_t *tri = surf->geometry;
690 idVec3 mid = ( tri->bounds[1] + tri->bounds[0] ) * 0.5f;
691 float radius = ( tri->bounds[0] - mid ).Length();
694 tri->bounds[0][0] = mid[0] - radius;
695 tri->bounds[0][1] = mid[1] - radius;
696 tri->bounds[0][2] = mid[2] - radius;
698 tri->bounds[1][0] = mid[0] + radius;
699 tri->bounds[1][1] = mid[1] + radius;
700 tri->bounds[1][2] = mid[2] + radius;
703 // add to the model bounds
704 bounds.AddBounds( surf->geometry->bounds );
712 idRenderModelStatic::ConvertASEToModelSurfaces
715 typedef struct matchVert_s {
716 struct matchVert_s *next;
722 bool idRenderModelStatic::ConvertASEToModelSurfaces( const struct aseModel_s *ase ) {
723 aseObject_t * object;
725 aseMaterial_t * material;
726 const idMaterial *im1, *im2;
733 matchVert_t * mvTable; // all of the match verts
734 matchVert_t ** mvHash; // points inside mvTable for each xyz index
735 matchVert_t * lastmv;
738 float uOffset, vOffset, textureSin, textureCos;
739 float uTiling, vTiling;
742 static byte identityColor[4] = { 255, 255, 255, 255 };
743 modelSurface_t surf, *modelSurf;
748 if ( ase->objects.Num() < 1 ) {
752 timeStamp = ase->timeStamp;
754 // the modeling programs can save out multiple surfaces with a common
755 // material, but we would like to mege them together where possible
756 // meaning that this->NumSurfaces() <= ase->objects.currentElements
757 mergeTo = (int *)_alloca( ase->objects.Num() * sizeof( *mergeTo ) );
758 surf.geometry = NULL;
759 if ( ase->materials.Num() == 0 ) {
760 // if we don't have any materials, dump everything into a single surface
761 surf.shader = tr.defaultMaterial;
763 this->AddSurface( surf );
764 for ( i = 0 ; i < ase->objects.Num() ; i++ ) {
767 } else if ( !r_mergeModelSurfaces.GetBool() ) {
769 for ( i = 0 ; i < ase->objects.Num() ; i++ ) {
771 object = ase->objects[i];
772 material = ase->materials[object->materialRef];
773 surf.shader = declManager->FindMaterial( material->name );
774 surf.id = this->NumSurfaces();
775 this->AddSurface( surf );
778 // search for material matches
779 for ( i = 0 ; i < ase->objects.Num() ; i++ ) {
780 object = ase->objects[i];
781 material = ase->materials[object->materialRef];
782 im1 = declManager->FindMaterial( material->name );
783 if ( im1->IsDiscrete() ) {
784 // flares, autosprites, etc
785 j = this->NumSurfaces();
787 for ( j = 0 ; j < this->NumSurfaces() ; j++ ) {
788 modelSurf = &this->surfaces[j];
789 im2 = modelSurf->shader;
797 if ( j == this->NumSurfaces() ) {
801 surf.id = this->NumSurfaces();
802 this->AddSurface( surf );
807 idVectorSubset<idVec3, 3> vertexSubset;
808 idVectorSubset<idVec2, 2> texCoordSubset;
810 // build the surfaces
811 for ( objectNum = 0 ; objectNum < ase->objects.Num() ; objectNum++ ) {
812 object = ase->objects[objectNum];
813 mesh = &object->mesh;
814 material = ase->materials[object->materialRef];
815 im1 = declManager->FindMaterial( material->name );
817 bool normalsParsed = mesh->normalsParsed;
819 // completely ignore any explict normals on surfaces with a renderbump command
820 // which will guarantee the best contours and least vertexes.
821 const char *rb = im1->GetRenderBump();
823 normalsParsed = false;
826 // It seems like the tools our artists are using often generate
827 // verts and texcoords slightly separated that should be merged
828 // note that we really should combine the surfaces with common materials
829 // before doing this operation, because we can miss a slop combination
830 // if they are in different surfaces
832 vRemap = (int *)R_StaticAlloc( mesh->numVertexes * sizeof( vRemap[0] ) );
835 // renderbump doesn't care about vertex count
836 for ( j = 0; j < mesh->numVertexes; j++ ) {
840 float vertexEpsilon = r_slopVertex.GetFloat();
841 float expand = 2 * 32 * vertexEpsilon;
844 SIMDProcessor->MinMax( mins, maxs, mesh->vertexes, mesh->numVertexes );
845 mins -= idVec3( expand, expand, expand );
846 maxs += idVec3( expand, expand, expand );
847 vertexSubset.Init( mins, maxs, 32, 1024 );
848 for ( j = 0; j < mesh->numVertexes; j++ ) {
849 vRemap[j] = vertexSubset.FindVector( mesh->vertexes, j, vertexEpsilon );
853 tvRemap = (int *)R_StaticAlloc( mesh->numTVertexes * sizeof( tvRemap[0] ) );
856 // renderbump doesn't care about vertex count
857 for ( j = 0; j < mesh->numTVertexes; j++ ) {
861 float texCoordEpsilon = r_slopTexCoord.GetFloat();
862 float expand = 2 * 32 * texCoordEpsilon;
865 SIMDProcessor->MinMax( mins, maxs, mesh->tvertexes, mesh->numTVertexes );
866 mins -= idVec2( expand, expand );
867 maxs += idVec2( expand, expand );
868 texCoordSubset.Init( mins, maxs, 32, 1024 );
869 for ( j = 0; j < mesh->numTVertexes; j++ ) {
870 tvRemap[j] = texCoordSubset.FindVector( mesh->tvertexes, j, texCoordEpsilon );
874 // we need to find out how many unique vertex / texcoord combinations
875 // there are, because ASE tracks them separately but we need them unified
877 // the maximum possible number of combined vertexes is the number of indexes
878 mvTable = (matchVert_t *)R_ClearedStaticAlloc( mesh->numFaces * 3 * sizeof( mvTable[0] ) );
880 // we will have a hash chain based on the xyz values
881 mvHash = (matchVert_t **)R_ClearedStaticAlloc( mesh->numVertexes * sizeof( mvHash[0] ) );
883 // allocate triangle surface
884 tri = R_AllocStaticTriSurf();
887 R_AllocStaticTriSurfIndexes( tri, mesh->numFaces * 3 );
888 tri->generateNormals = !normalsParsed;
890 // init default normal, color and tex coord index
892 color = identityColor;
895 // find all the unique combinations
896 float normalEpsilon = 1.0f - r_slopNormal.GetFloat();
897 for ( j = 0; j < mesh->numFaces; j++ ) {
898 for ( k = 0; k < 3; k++ ) {
899 v = mesh->faces[j].vertexNum[k];
901 if ( v < 0 || v >= mesh->numVertexes ) {
902 common->Error( "ConvertASEToModelSurfaces: bad vertex index in ASE file %s", name.c_str() );
905 // collapse the position if it was slightly offset
908 // we may or may not have texcoords to compare
909 if ( mesh->numTVFaces == mesh->numFaces && mesh->numTVertexes != 0 ) {
910 tv = mesh->faces[j].tVertexNum[k];
911 if ( tv < 0 || tv >= mesh->numTVertexes ) {
912 common->Error( "ConvertASEToModelSurfaces: bad tex coord index in ASE file %s", name.c_str() );
914 // collapse the tex coord if it was slightly offset
918 // we may or may not have normals to compare
919 if ( normalsParsed ) {
920 normal = mesh->faces[j].vertexNormals[k];
923 // we may or may not have colors to compare
924 if ( mesh->colorsParsed ) {
925 color = mesh->faces[j].vertexColors[k];
928 // find a matching vert
929 for ( lastmv = NULL, mv = mvHash[v]; mv != NULL; lastmv = mv, mv = mv->next ) {
930 if ( mv->tv != tv ) {
933 if ( *(unsigned *)mv->color != *(unsigned *)color ) {
936 if ( !normalsParsed ) {
937 // if we are going to create the normals, just
938 // matching texcoords is enough
941 if ( mv->normal * normal > normalEpsilon ) {
942 break; // we already have this one
946 // allocate a new match vert and link to hash chain
947 mv = &mvTable[ tri->numVerts ];
951 *(unsigned *)mv->color = *(unsigned *)color;
961 tri->indexes[tri->numIndexes] = mv - mvTable;
966 // allocate space for the indexes and copy them
967 if ( tri->numIndexes > mesh->numFaces * 3 ) {
968 common->FatalError( "ConvertASEToModelSurfaces: index miscount in ASE file %s", name.c_str() );
970 if ( tri->numVerts > mesh->numFaces * 3 ) {
971 common->FatalError( "ConvertASEToModelSurfaces: vertex miscount in ASE file %s", name.c_str() );
974 // an ASE allows the texture coordinates to be scaled, translated, and rotated
975 if ( ase->materials.Num() == 0 ) {
976 uOffset = vOffset = 0.0f;
977 uTiling = vTiling = 1.0f;
981 material = ase->materials[object->materialRef];
982 uOffset = -material->uOffset;
983 vOffset = material->vOffset;
984 uTiling = material->uTiling;
985 vTiling = material->vTiling;
986 textureSin = idMath::Sin( material->angle );
987 textureCos = idMath::Cos( material->angle );
990 // now allocate and generate the combined vertexes
991 R_AllocStaticTriSurfVerts( tri, tri->numVerts );
992 for ( j = 0; j < tri->numVerts; j++ ) {
994 tri->verts[ j ].Clear();
995 tri->verts[ j ].xyz = mesh->vertexes[ mv->v ];
996 tri->verts[ j ].normal = mv->normal;
997 *(unsigned *)tri->verts[j].color = *(unsigned *)mv->color;
998 if ( mesh->numTVFaces == mesh->numFaces && mesh->numTVertexes != 0 ) {
999 const idVec2 &tv = mesh->tvertexes[ mv->tv ];
1000 float u = tv.x * uTiling + uOffset;
1001 float v = tv.y * vTiling + vOffset;
1002 tri->verts[ j ].st[0] = u * textureCos + v * textureSin;
1003 tri->verts[ j ].st[1] = u * -textureSin + v * textureCos;
1007 R_StaticFree( mvTable );
1008 R_StaticFree( mvHash );
1009 R_StaticFree( tvRemap );
1010 R_StaticFree( vRemap );
1012 // see if we need to merge with a previous surface of the same material
1013 modelSurf = &this->surfaces[mergeTo[ objectNum ]];
1014 srfTriangles_t *mergeTri = modelSurf->geometry;
1016 modelSurf->geometry = tri;
1018 modelSurf->geometry = R_MergeTriangles( mergeTri, tri );
1019 R_FreeStaticTriSurf( tri );
1020 R_FreeStaticTriSurf( mergeTri );
1029 idRenderModelStatic::ConvertLWOToModelSurfaces
1032 bool idRenderModelStatic::ConvertLWOToModelSurfaces( const struct st_lwObject *lwo ) {
1033 const idMaterial *im1, *im2;
1034 srfTriangles_t *tri;
1035 lwSurface * lwoSurf;
1043 matchVert_t * mvTable; // all of the match verts
1044 matchVert_t ** mvHash; // points inside mvTable for each xyz index
1045 matchVert_t * lastmv;
1050 modelSurface_t surf, *modelSurf;
1055 if ( lwo->surf == NULL ) {
1059 timeStamp = lwo->timeStamp;
1061 // count the number of surfaces
1063 for ( lwoSurf = lwo->surf; lwoSurf; lwoSurf = lwoSurf->next ) {
1067 // the modeling programs can save out multiple surfaces with a common
1068 // material, but we would like to merge them together where possible
1069 mergeTo = (int *)_alloca( i * sizeof( mergeTo[0] ) );
1070 memset( &surf, 0, sizeof( surf ) );
1072 if ( !r_mergeModelSurfaces.GetBool() ) {
1074 for ( lwoSurf = lwo->surf, i = 0; lwoSurf; lwoSurf = lwoSurf->next, i++ ) {
1076 surf.shader = declManager->FindMaterial( lwoSurf->name );
1077 surf.id = this->NumSurfaces();
1078 this->AddSurface( surf );
1081 // search for material matches
1082 for ( lwoSurf = lwo->surf, i = 0; lwoSurf; lwoSurf = lwoSurf->next, i++ ) {
1083 im1 = declManager->FindMaterial( lwoSurf->name );
1084 if ( im1->IsDiscrete() ) {
1085 // flares, autosprites, etc
1086 j = this->NumSurfaces();
1088 for ( j = 0 ; j < this->NumSurfaces() ; j++ ) {
1089 modelSurf = &this->surfaces[j];
1090 im2 = modelSurf->shader;
1098 if ( j == this->NumSurfaces() ) {
1102 surf.id = this->NumSurfaces();
1103 this->AddSurface( surf );
1108 idVectorSubset<idVec3, 3> vertexSubset;
1109 idVectorSubset<idVec2, 2> texCoordSubset;
1111 // we only ever use the first layer
1112 lwLayer *layer = lwo->layer;
1115 if ( layer->point.count <= 0 ) {
1116 common->Warning( "ConvertLWOToModelSurfaces: model \'%s\' has bad or missing vertex data", name.c_str() );
1120 vList = (idVec3 *)R_StaticAlloc( layer->point.count * sizeof( vList[0] ) );
1121 for ( j = 0; j < layer->point.count; j++ ) {
1122 vList[j].x = layer->point.pt[j].pos[0];
1123 vList[j].y = layer->point.pt[j].pos[2];
1124 vList[j].z = layer->point.pt[j].pos[1];
1127 // vertex texture coords
1130 if ( layer->nvmaps ) {
1131 for( lwVMap *vm = layer->vmap; vm; vm = vm->next ) {
1132 if ( vm->type == LWID_('T','X','U','V') ) {
1133 numTVertexes += vm->nverts;
1138 if ( numTVertexes ) {
1139 tvList = (idVec2 *)Mem_Alloc( numTVertexes * sizeof( tvList[0] ) );
1141 for( lwVMap *vm = layer->vmap; vm; vm = vm->next ) {
1142 if ( vm->type == LWID_('T','X','U','V') ) {
1143 vm->offset = offset;
1144 for ( k = 0; k < vm->nverts; k++ ) {
1145 tvList[k + offset].x = vm->val[k][0];
1146 tvList[k + offset].y = 1.0f - vm->val[k][1]; // invert the t
1148 offset += vm->nverts;
1152 common->Warning( "ConvertLWOToModelSurfaces: model \'%s\' has bad or missing uv data", name.c_str() );
1154 tvList = (idVec2 *)Mem_ClearedAlloc( numTVertexes * sizeof( tvList[0] ) );
1157 // It seems like the tools our artists are using often generate
1158 // verts and texcoords slightly separated that should be merged
1159 // note that we really should combine the surfaces with common materials
1160 // before doing this operation, because we can miss a slop combination
1161 // if they are in different surfaces
1163 vRemap = (int *)R_StaticAlloc( layer->point.count * sizeof( vRemap[0] ) );
1166 // renderbump doesn't care about vertex count
1167 for ( j = 0; j < layer->point.count; j++ ) {
1171 float vertexEpsilon = r_slopVertex.GetFloat();
1172 float expand = 2 * 32 * vertexEpsilon;
1175 SIMDProcessor->MinMax( mins, maxs, vList, layer->point.count );
1176 mins -= idVec3( expand, expand, expand );
1177 maxs += idVec3( expand, expand, expand );
1178 vertexSubset.Init( mins, maxs, 32, 1024 );
1179 for ( j = 0; j < layer->point.count; j++ ) {
1180 vRemap[j] = vertexSubset.FindVector( vList, j, vertexEpsilon );
1184 tvRemap = (int *)R_StaticAlloc( numTVertexes * sizeof( tvRemap[0] ) );
1187 // renderbump doesn't care about vertex count
1188 for ( j = 0; j < numTVertexes; j++ ) {
1192 float texCoordEpsilon = r_slopTexCoord.GetFloat();
1193 float expand = 2 * 32 * texCoordEpsilon;
1196 SIMDProcessor->MinMax( mins, maxs, tvList, numTVertexes );
1197 mins -= idVec2( expand, expand );
1198 maxs += idVec2( expand, expand );
1199 texCoordSubset.Init( mins, maxs, 32, 1024 );
1200 for ( j = 0; j < numTVertexes; j++ ) {
1201 tvRemap[j] = texCoordSubset.FindVector( tvList, j, texCoordEpsilon );
1205 // build the surfaces
1206 for ( lwoSurf = lwo->surf, i = 0; lwoSurf; lwoSurf = lwoSurf->next, i++ ) {
1207 im1 = declManager->FindMaterial( lwoSurf->name );
1209 bool normalsParsed = true;
1211 // completely ignore any explict normals on surfaces with a renderbump command
1212 // which will guarantee the best contours and least vertexes.
1213 const char *rb = im1->GetRenderBump();
1214 if ( rb && rb[0] ) {
1215 normalsParsed = false;
1218 // we need to find out how many unique vertex / texcoord combinations there are
1220 // the maximum possible number of combined vertexes is the number of indexes
1221 mvTable = (matchVert_t *)R_ClearedStaticAlloc( layer->polygon.count * 3 * sizeof( mvTable[0] ) );
1223 // we will have a hash chain based on the xyz values
1224 mvHash = (matchVert_t **)R_ClearedStaticAlloc( layer->point.count * sizeof( mvHash[0] ) );
1226 // allocate triangle surface
1227 tri = R_AllocStaticTriSurf();
1229 tri->numIndexes = 0;
1230 R_AllocStaticTriSurfIndexes( tri, layer->polygon.count * 3 );
1231 tri->generateNormals = !normalsParsed;
1233 // find all the unique combinations
1234 float normalEpsilon;
1236 normalEpsilon = 1.0f; // don't merge unless completely exact
1238 normalEpsilon = 1.0f - r_slopNormal.GetFloat();
1240 for ( j = 0; j < layer->polygon.count; j++ ) {
1241 lwPolygon *poly = &layer->polygon.pol[j];
1243 if ( poly->surf != lwoSurf ) {
1247 if ( poly->nverts != 3 ) {
1248 common->Warning( "ConvertLWOToModelSurfaces: model %s has too many verts for a poly! Make sure you triplet it down", name.c_str() );
1252 for ( k = 0; k < 3; k++ ) {
1254 v = vRemap[poly->v[k].index];
1256 normal.x = poly->v[k].norm[0];
1257 normal.y = poly->v[k].norm[2];
1258 normal.z = poly->v[k].norm[1];
1260 // LWO models aren't all that pretty when it comes down to the floating point values they store
1261 normal.FixDegenerateNormal();
1265 color[0] = lwoSurf->color.rgb[0] * 255;
1266 color[1] = lwoSurf->color.rgb[1] * 255;
1267 color[2] = lwoSurf->color.rgb[2] * 255;
1270 // first set attributes from the vertex
1271 lwPoint *pt = &layer->point.pt[poly->v[k].index];
1273 for ( nvm = 0; nvm < pt->nvmaps; nvm++ ) {
1274 lwVMapPt *vm = &pt->vm[nvm];
1276 if ( vm->vmap->type == LWID_('T','X','U','V') ) {
1277 tv = tvRemap[vm->index + vm->vmap->offset];
1279 if ( vm->vmap->type == LWID_('R','G','B','A') ) {
1280 for ( int chan = 0; chan < 4; chan++ ) {
1281 color[chan] = 255 * vm->vmap->val[vm->index][chan];
1286 // then override with polygon attributes
1287 for ( nvm = 0; nvm < poly->v[k].nvmaps; nvm++ ) {
1288 lwVMapPt *vm = &poly->v[k].vm[nvm];
1290 if ( vm->vmap->type == LWID_('T','X','U','V') ) {
1291 tv = tvRemap[vm->index + vm->vmap->offset];
1293 if ( vm->vmap->type == LWID_('R','G','B','A') ) {
1294 for ( int chan = 0; chan < 4; chan++ ) {
1295 color[chan] = 255 * vm->vmap->val[vm->index][chan];
1300 // find a matching vert
1301 for ( lastmv = NULL, mv = mvHash[v]; mv != NULL; lastmv = mv, mv = mv->next ) {
1302 if ( mv->tv != tv ) {
1305 if ( *(unsigned *)mv->color != *(unsigned *)color ) {
1308 if ( !normalsParsed ) {
1309 // if we are going to create the normals, just
1310 // matching texcoords is enough
1313 if ( mv->normal * normal > normalEpsilon ) {
1314 break; // we already have this one
1318 // allocate a new match vert and link to hash chain
1319 mv = &mvTable[ tri->numVerts ];
1322 mv->normal = normal;
1323 *(unsigned *)mv->color = *(unsigned *)color;
1333 tri->indexes[tri->numIndexes] = mv - mvTable;
1338 // allocate space for the indexes and copy them
1339 if ( tri->numIndexes > layer->polygon.count * 3 ) {
1340 common->FatalError( "ConvertLWOToModelSurfaces: index miscount in LWO file %s", name.c_str() );
1342 if ( tri->numVerts > layer->polygon.count * 3 ) {
1343 common->FatalError( "ConvertLWOToModelSurfaces: vertex miscount in LWO file %s", name.c_str() );
1346 // now allocate and generate the combined vertexes
1347 R_AllocStaticTriSurfVerts( tri, tri->numVerts );
1348 for ( j = 0; j < tri->numVerts; j++ ) {
1350 tri->verts[ j ].Clear();
1351 tri->verts[ j ].xyz = vList[ mv->v ];
1352 tri->verts[ j ].st = tvList[ mv->tv ];
1353 tri->verts[ j ].normal = mv->normal;
1354 *(unsigned *)tri->verts[j].color = *(unsigned *)mv->color;
1357 R_StaticFree( mvTable );
1358 R_StaticFree( mvHash );
1360 // see if we need to merge with a previous surface of the same material
1361 modelSurf = &this->surfaces[mergeTo[ i ]];
1362 srfTriangles_t *mergeTri = modelSurf->geometry;
1364 modelSurf->geometry = tri;
1366 modelSurf->geometry = R_MergeTriangles( mergeTri, tri );
1367 R_FreeStaticTriSurf( tri );
1368 R_FreeStaticTriSurf( mergeTri );
1372 R_StaticFree( tvRemap );
1373 R_StaticFree( vRemap );
1374 R_StaticFree( tvList );
1375 R_StaticFree( vList );
1382 idRenderModelStatic::ConvertLWOToASE
1385 struct aseModel_s *idRenderModelStatic::ConvertLWOToASE( const struct st_lwObject *obj, const char *fileName ) {
1393 // NOTE: using new operator because aseModel_t contains idList class objects
1394 ase = new aseModel_t;
1395 ase->timeStamp = obj->timeStamp;
1396 ase->objects.Resize( obj->nlayers, obj->nlayers );
1398 int materialRef = 0;
1400 for ( lwSurface *surf = obj->surf; surf; surf = surf->next ) {
1402 aseMaterial_t *mat = (aseMaterial_t *)Mem_ClearedAlloc( sizeof( *mat ) );
1403 strcpy( mat->name, surf->name );
1404 mat->uTiling = mat->vTiling = 1;
1405 mat->angle = mat->uOffset = mat->vOffset = 0;
1406 ase->materials.Append( mat );
1408 lwLayer *layer = obj->layer;
1410 aseObject_t *object = (aseObject_t *)Mem_ClearedAlloc( sizeof( *object ) );
1411 object->materialRef = materialRef++;
1413 aseMesh_t *mesh = &object->mesh;
1414 ase->objects.Append( object );
1416 mesh->numFaces = layer->polygon.count;
1417 mesh->numTVFaces = mesh->numFaces;
1418 mesh->faces = (aseFace_t *)Mem_Alloc( mesh->numFaces * sizeof( mesh->faces[0] ) );
1420 mesh->numVertexes = layer->point.count;
1421 mesh->vertexes = (idVec3 *)Mem_Alloc( mesh->numVertexes * sizeof( mesh->vertexes[0] ) );
1424 if ( layer->point.count <= 0 ) {
1425 common->Warning( "ConvertLWOToASE: model \'%s\' has bad or missing vertex data", name.c_str() );
1428 for ( j = 0; j < layer->point.count; j++ ) {
1429 mesh->vertexes[j].x = layer->point.pt[j].pos[0];
1430 mesh->vertexes[j].y = layer->point.pt[j].pos[2];
1431 mesh->vertexes[j].z = layer->point.pt[j].pos[1];
1434 // vertex texture coords
1435 mesh->numTVertexes = 0;
1437 if ( layer->nvmaps ) {
1438 for( lwVMap *vm = layer->vmap; vm; vm = vm->next ) {
1439 if ( vm->type == LWID_('T','X','U','V') ) {
1440 mesh->numTVertexes += vm->nverts;
1445 if ( mesh->numTVertexes ) {
1446 mesh->tvertexes = (idVec2 *)Mem_Alloc( mesh->numTVertexes * sizeof( mesh->tvertexes[0] ) );
1448 for( lwVMap *vm = layer->vmap; vm; vm = vm->next ) {
1449 if ( vm->type == LWID_('T','X','U','V') ) {
1450 vm->offset = offset;
1451 for ( k = 0; k < vm->nverts; k++ ) {
1452 mesh->tvertexes[k + offset].x = vm->val[k][0];
1453 mesh->tvertexes[k + offset].y = 1.0f - vm->val[k][1]; // invert the t
1455 offset += vm->nverts;
1459 common->Warning( "ConvertLWOToASE: model \'%s\' has bad or missing uv data", fileName );
1460 mesh->numTVertexes = 1;
1461 mesh->tvertexes = (idVec2 *)Mem_ClearedAlloc( mesh->numTVertexes * sizeof( mesh->tvertexes[0] ) );
1464 mesh->normalsParsed = true;
1465 mesh->colorsParsed = true; // because we are falling back to the surface color
1469 for ( j = 0; j < layer->polygon.count; j++ ) {
1470 lwPolygon *poly = &layer->polygon.pol[j];
1472 if ( poly->surf != surf ) {
1476 if ( poly->nverts != 3 ) {
1477 common->Warning( "ConvertLWOToASE: model %s has too many verts for a poly! Make sure you triplet it down", fileName );
1481 mesh->faces[faceIndex].faceNormal.x = poly->norm[0];
1482 mesh->faces[faceIndex].faceNormal.y = poly->norm[2];
1483 mesh->faces[faceIndex].faceNormal.z = poly->norm[1];
1485 for ( k = 0; k < 3; k++ ) {
1487 mesh->faces[faceIndex].vertexNum[k] = poly->v[k].index;
1489 mesh->faces[faceIndex].vertexNormals[k].x = poly->v[k].norm[0];
1490 mesh->faces[faceIndex].vertexNormals[k].y = poly->v[k].norm[2];
1491 mesh->faces[faceIndex].vertexNormals[k].z = poly->v[k].norm[1];
1493 // complete fallbacks
1494 mesh->faces[faceIndex].tVertexNum[k] = 0;
1496 mesh->faces[faceIndex].vertexColors[k][0] = surf->color.rgb[0] * 255;
1497 mesh->faces[faceIndex].vertexColors[k][1] = surf->color.rgb[1] * 255;
1498 mesh->faces[faceIndex].vertexColors[k][2] = surf->color.rgb[2] * 255;
1499 mesh->faces[faceIndex].vertexColors[k][3] = 255;
1501 // first set attributes from the vertex
1502 lwPoint *pt = &layer->point.pt[poly->v[k].index];
1504 for ( nvm = 0; nvm < pt->nvmaps; nvm++ ) {
1505 lwVMapPt *vm = &pt->vm[nvm];
1507 if ( vm->vmap->type == LWID_('T','X','U','V') ) {
1508 mesh->faces[faceIndex].tVertexNum[k] = vm->index + vm->vmap->offset;
1510 if ( vm->vmap->type == LWID_('R','G','B','A') ) {
1511 for ( int chan = 0; chan < 4; chan++ ) {
1512 mesh->faces[faceIndex].vertexColors[k][chan] = 255 * vm->vmap->val[vm->index][chan];
1517 // then override with polygon attributes
1518 for ( nvm = 0; nvm < poly->v[k].nvmaps; nvm++ ) {
1519 lwVMapPt *vm = &poly->v[k].vm[nvm];
1521 if ( vm->vmap->type == LWID_('T','X','U','V') ) {
1522 mesh->faces[faceIndex].tVertexNum[k] = vm->index + vm->vmap->offset;
1524 if ( vm->vmap->type == LWID_('R','G','B','A') ) {
1525 for ( int chan = 0; chan < 4; chan++ ) {
1526 mesh->faces[faceIndex].vertexColors[k][chan] = 255 * vm->vmap->val[vm->index][chan];
1535 mesh->numFaces = faceIndex;
1536 mesh->numTVFaces = faceIndex;
1538 aseFace_t *newFaces = ( aseFace_t* )Mem_Alloc( mesh->numFaces * sizeof ( mesh->faces[0] ) );
1539 memcpy( newFaces, mesh->faces, sizeof( mesh->faces[0] ) * mesh->numFaces );
1540 Mem_Free( mesh->faces );
1541 mesh->faces = newFaces;
1549 idRenderModelStatic::ConvertMAToModelSurfaces
1552 bool idRenderModelStatic::ConvertMAToModelSurfaces (const struct maModel_s *ma ) {
1554 maObject_t * object;
1556 maMaterial_t * material;
1558 const idMaterial *im1, *im2;
1559 srfTriangles_t *tri;
1565 matchVert_t * mvTable; // all of the match verts
1566 matchVert_t ** mvHash; // points inside mvTable for each xyz index
1567 matchVert_t * lastmv;
1570 float uOffset, vOffset, textureSin, textureCos;
1571 float uTiling, vTiling;
1574 static byte identityColor[4] = { 255, 255, 255, 255 };
1575 modelSurface_t surf, *modelSurf;
1580 if ( ma->objects.Num() < 1 ) {
1584 timeStamp = ma->timeStamp;
1586 // the modeling programs can save out multiple surfaces with a common
1587 // material, but we would like to mege them together where possible
1588 // meaning that this->NumSurfaces() <= ma->objects.currentElements
1589 mergeTo = (int *)_alloca( ma->objects.Num() * sizeof( *mergeTo ) );
1591 surf.geometry = NULL;
1592 if ( ma->materials.Num() == 0 ) {
1593 // if we don't have any materials, dump everything into a single surface
1594 surf.shader = tr.defaultMaterial;
1596 this->AddSurface( surf );
1597 for ( i = 0 ; i < ma->objects.Num() ; i++ ) {
1600 } else if ( !r_mergeModelSurfaces.GetBool() ) {
1602 for ( i = 0 ; i < ma->objects.Num() ; i++ ) {
1604 object = ma->objects[i];
1605 if(object->materialRef >= 0) {
1606 material = ma->materials[object->materialRef];
1607 surf.shader = declManager->FindMaterial( material->name );
1609 surf.shader = tr.defaultMaterial;
1611 surf.id = this->NumSurfaces();
1612 this->AddSurface( surf );
1615 // search for material matches
1616 for ( i = 0 ; i < ma->objects.Num() ; i++ ) {
1617 object = ma->objects[i];
1618 if(object->materialRef >= 0) {
1619 material = ma->materials[object->materialRef];
1620 im1 = declManager->FindMaterial( material->name );
1622 im1 = tr.defaultMaterial;
1624 if ( im1->IsDiscrete() ) {
1625 // flares, autosprites, etc
1626 j = this->NumSurfaces();
1628 for ( j = 0 ; j < this->NumSurfaces() ; j++ ) {
1629 modelSurf = &this->surfaces[j];
1630 im2 = modelSurf->shader;
1638 if ( j == this->NumSurfaces() ) {
1642 surf.id = this->NumSurfaces();
1643 this->AddSurface( surf );
1648 idVectorSubset<idVec3, 3> vertexSubset;
1649 idVectorSubset<idVec2, 2> texCoordSubset;
1651 // build the surfaces
1652 for ( objectNum = 0 ; objectNum < ma->objects.Num() ; objectNum++ ) {
1653 object = ma->objects[objectNum];
1654 mesh = &object->mesh;
1655 if(object->materialRef >= 0) {
1656 material = ma->materials[object->materialRef];
1657 im1 = declManager->FindMaterial( material->name );
1659 im1 = tr.defaultMaterial;
1662 bool normalsParsed = mesh->normalsParsed;
1664 // completely ignore any explict normals on surfaces with a renderbump command
1665 // which will guarantee the best contours and least vertexes.
1666 const char *rb = im1->GetRenderBump();
1667 if ( rb && rb[0] ) {
1668 normalsParsed = false;
1671 // It seems like the tools our artists are using often generate
1672 // verts and texcoords slightly separated that should be merged
1673 // note that we really should combine the surfaces with common materials
1674 // before doing this operation, because we can miss a slop combination
1675 // if they are in different surfaces
1677 vRemap = (int *)R_StaticAlloc( mesh->numVertexes * sizeof( vRemap[0] ) );
1680 // renderbump doesn't care about vertex count
1681 for ( j = 0; j < mesh->numVertexes; j++ ) {
1685 float vertexEpsilon = r_slopVertex.GetFloat();
1686 float expand = 2 * 32 * vertexEpsilon;
1689 SIMDProcessor->MinMax( mins, maxs, mesh->vertexes, mesh->numVertexes );
1690 mins -= idVec3( expand, expand, expand );
1691 maxs += idVec3( expand, expand, expand );
1692 vertexSubset.Init( mins, maxs, 32, 1024 );
1693 for ( j = 0; j < mesh->numVertexes; j++ ) {
1694 vRemap[j] = vertexSubset.FindVector( mesh->vertexes, j, vertexEpsilon );
1698 tvRemap = (int *)R_StaticAlloc( mesh->numTVertexes * sizeof( tvRemap[0] ) );
1701 // renderbump doesn't care about vertex count
1702 for ( j = 0; j < mesh->numTVertexes; j++ ) {
1706 float texCoordEpsilon = r_slopTexCoord.GetFloat();
1707 float expand = 2 * 32 * texCoordEpsilon;
1710 SIMDProcessor->MinMax( mins, maxs, mesh->tvertexes, mesh->numTVertexes );
1711 mins -= idVec2( expand, expand );
1712 maxs += idVec2( expand, expand );
1713 texCoordSubset.Init( mins, maxs, 32, 1024 );
1714 for ( j = 0; j < mesh->numTVertexes; j++ ) {
1715 tvRemap[j] = texCoordSubset.FindVector( mesh->tvertexes, j, texCoordEpsilon );
1719 // we need to find out how many unique vertex / texcoord / color combinations
1720 // there are, because MA tracks them separately but we need them unified
1722 // the maximum possible number of combined vertexes is the number of indexes
1723 mvTable = (matchVert_t *)R_ClearedStaticAlloc( mesh->numFaces * 3 * sizeof( mvTable[0] ) );
1725 // we will have a hash chain based on the xyz values
1726 mvHash = (matchVert_t **)R_ClearedStaticAlloc( mesh->numVertexes * sizeof( mvHash[0] ) );
1728 // allocate triangle surface
1729 tri = R_AllocStaticTriSurf();
1731 tri->numIndexes = 0;
1732 R_AllocStaticTriSurfIndexes( tri, mesh->numFaces * 3 );
1733 tri->generateNormals = !normalsParsed;
1735 // init default normal, color and tex coord index
1737 color = identityColor;
1740 // find all the unique combinations
1741 float normalEpsilon = 1.0f - r_slopNormal.GetFloat();
1742 for ( j = 0; j < mesh->numFaces; j++ ) {
1743 for ( k = 0; k < 3; k++ ) {
1744 v = mesh->faces[j].vertexNum[k];
1746 if ( v < 0 || v >= mesh->numVertexes ) {
1747 common->Error( "ConvertMAToModelSurfaces: bad vertex index in MA file %s", name.c_str() );
1750 // collapse the position if it was slightly offset
1753 // we may or may not have texcoords to compare
1754 if ( mesh->numTVertexes != 0 ) {
1755 tv = mesh->faces[j].tVertexNum[k];
1756 if ( tv < 0 || tv >= mesh->numTVertexes ) {
1757 common->Error( "ConvertMAToModelSurfaces: bad tex coord index in MA file %s", name.c_str() );
1759 // collapse the tex coord if it was slightly offset
1763 // we may or may not have normals to compare
1764 if ( normalsParsed ) {
1765 normal = mesh->faces[j].vertexNormals[k];
1768 //BSM: Todo: Fix the vertex colors
1769 // we may or may not have colors to compare
1770 if ( mesh->faces[j].vertexColors[k] != -1 && mesh->faces[j].vertexColors[k] != -999 ) {
1772 color = &mesh->colors[mesh->faces[j].vertexColors[k]*4];
1775 // find a matching vert
1776 for ( lastmv = NULL, mv = mvHash[v]; mv != NULL; lastmv = mv, mv = mv->next ) {
1777 if ( mv->tv != tv ) {
1780 if ( *(unsigned *)mv->color != *(unsigned *)color ) {
1783 if ( !normalsParsed ) {
1784 // if we are going to create the normals, just
1785 // matching texcoords is enough
1788 if ( mv->normal * normal > normalEpsilon ) {
1789 break; // we already have this one
1793 // allocate a new match vert and link to hash chain
1794 mv = &mvTable[ tri->numVerts ];
1797 mv->normal = normal;
1798 *(unsigned *)mv->color = *(unsigned *)color;
1808 tri->indexes[tri->numIndexes] = mv - mvTable;
1813 // allocate space for the indexes and copy them
1814 if ( tri->numIndexes > mesh->numFaces * 3 ) {
1815 common->FatalError( "ConvertMAToModelSurfaces: index miscount in MA file %s", name.c_str() );
1817 if ( tri->numVerts > mesh->numFaces * 3 ) {
1818 common->FatalError( "ConvertMAToModelSurfaces: vertex miscount in MA file %s", name.c_str() );
1821 // an MA allows the texture coordinates to be scaled, translated, and rotated
1822 //BSM: Todo: Does Maya support this and if so how
1823 //if ( ase->materials.Num() == 0 ) {
1824 uOffset = vOffset = 0.0f;
1825 uTiling = vTiling = 1.0f;
1829 // material = ase->materials[object->materialRef];
1830 // uOffset = -material->uOffset;
1831 // vOffset = material->vOffset;
1832 // uTiling = material->uTiling;
1833 // vTiling = material->vTiling;
1834 // textureSin = idMath::Sin( material->angle );
1835 // textureCos = idMath::Cos( material->angle );
1838 // now allocate and generate the combined vertexes
1839 R_AllocStaticTriSurfVerts( tri, tri->numVerts );
1840 for ( j = 0; j < tri->numVerts; j++ ) {
1842 tri->verts[ j ].Clear();
1843 tri->verts[ j ].xyz = mesh->vertexes[ mv->v ];
1844 tri->verts[ j ].normal = mv->normal;
1845 *(unsigned *)tri->verts[j].color = *(unsigned *)mv->color;
1846 if ( mesh->numTVertexes != 0 ) {
1847 const idVec2 &tv = mesh->tvertexes[ mv->tv ];
1848 float u = tv.x * uTiling + uOffset;
1849 float v = tv.y * vTiling + vOffset;
1850 tri->verts[ j ].st[0] = u * textureCos + v * textureSin;
1851 tri->verts[ j ].st[1] = u * -textureSin + v * textureCos;
1855 R_StaticFree( mvTable );
1856 R_StaticFree( mvHash );
1857 R_StaticFree( tvRemap );
1858 R_StaticFree( vRemap );
1860 // see if we need to merge with a previous surface of the same material
1861 modelSurf = &this->surfaces[mergeTo[ objectNum ]];
1862 srfTriangles_t *mergeTri = modelSurf->geometry;
1864 modelSurf->geometry = tri;
1866 modelSurf->geometry = R_MergeTriangles( mergeTri, tri );
1867 R_FreeStaticTriSurf( tri );
1868 R_FreeStaticTriSurf( mergeTri );
1877 idRenderModelStatic::LoadASE
1880 bool idRenderModelStatic::LoadASE( const char *fileName ) {
1883 ase = ASE_Load( fileName );
1884 if ( ase == NULL ) {
1888 ConvertASEToModelSurfaces( ase );
1897 idRenderModelStatic::LoadLWO
1900 bool idRenderModelStatic::LoadLWO( const char *fileName ) {
1901 unsigned int failID;
1905 lwo = lwGetObject( fileName, &failID, &failPos );
1906 if ( lwo == NULL ) {
1910 ConvertLWOToModelSurfaces( lwo );
1912 lwFreeObject( lwo );
1919 idRenderModelStatic::LoadMA
1922 bool idRenderModelStatic::LoadMA( const char *fileName ) {
1925 ma = MA_Load( fileName );
1930 ConvertMAToModelSurfaces( ma );
1939 idRenderModelStatic::LoadFLT
1941 USGS height map data for megaTexture experiments
1944 bool idRenderModelStatic::LoadFLT( const char *fileName ) {
1948 len = fileSystem->ReadFile( fileName, (void **)&data );
1952 int size = sqrt( len / 4.0f );
1954 // bound the altitudes
1955 float min = 9999999;
1956 float max = -9999999;
1957 for ( int i = 0 ; i < len/4 ; i++ ) {
1958 data[i] = BigFloat( data[i] );
1959 if ( data[i] == -9999 ) {
1960 data[i] = 0; // unscanned areas
1963 if ( data[i] < min ) {
1966 if ( data[i] > max ) {
1971 // write out a gray scale height map
1972 byte *image = (byte *)R_StaticAlloc( len );
1973 byte *image_p = image;
1974 for ( int i = 0 ; i < len/4 ; i++ ) {
1975 float v = ( data[i] - min ) / ( max - min );
1978 image_p[2] = v * 255;
1982 idStr tgaName = fileName;
1983 tgaName.StripFileExtension();
1985 R_WriteTGA( tgaName.c_str(), image, size, size, false );
1986 R_StaticFree( image );
1990 // find the island above sea level
1991 int minX, maxX, minY, maxY;
1994 for ( minX = 0 ; minX < size ; minX++ ) {
1995 for ( i = 0 ; i < size ; i++ ) {
1996 if ( data[i*size + minX] > 1.0 ) {
2005 for ( maxX = size-1 ; maxX > 0 ; maxX-- ) {
2006 for ( i = 0 ; i < size ; i++ ) {
2007 if ( data[i*size + maxX] > 1.0 ) {
2016 for ( minY = 0 ; minY < size ; minY++ ) {
2017 for ( i = 0 ; i < size ; i++ ) {
2018 if ( data[minY*size + i] > 1.0 ) {
2027 for ( maxY = size-1 ; maxY < size ; maxY-- ) {
2028 for ( i = 0 ; i < size ; i++ ) {
2029 if ( data[maxY*size + i] > 1.0 ) {
2039 int width = maxX - minX + 1;
2040 int height = maxY - minY + 1;
2043 // allocate triangle surface
2044 srfTriangles_t *tri = R_AllocStaticTriSurf();
2045 tri->numVerts = width * height;
2046 tri->numIndexes = (width-1) * (height-1) * 6;
2048 fastLoad = true; // don't do all the sil processing
2050 R_AllocStaticTriSurfIndexes( tri, tri->numIndexes );
2051 R_AllocStaticTriSurfVerts( tri, tri->numVerts );
2053 for ( int i = 0 ; i < height ; i++ ) {
2054 for ( int j = 0; j < width ; j++ ) {
2055 int v = i * width + j;
2056 tri->verts[ v ].Clear();
2057 tri->verts[ v ].xyz[0] = j * 10; // each sample is 10 meters
2058 tri->verts[ v ].xyz[1] = -i * 10;
2059 tri->verts[ v ].xyz[2] = data[(minY+i)*size+minX+j]; // height is in meters
2060 tri->verts[ v ].st[0] = (float) j / (width-1);
2061 tri->verts[ v ].st[1] = 1.0 - ( (float) i / (height-1) );
2065 for ( int i = 0 ; i < height-1 ; i++ ) {
2066 for ( int j = 0; j < width-1 ; j++ ) {
2067 int v = ( i * (width-1) + j ) * 6;
2069 tri->indexes[ v + 0 ] = i * width + j;
2070 tri->indexes[ v + 1 ] = (i+1) * width + j;
2071 tri->indexes[ v + 2 ] = (i+1) * width + j + 1;
2072 tri->indexes[ v + 3 ] = i * width + j;
2073 tri->indexes[ v + 4 ] = (i+1) * width + j + 1;
2074 tri->indexes[ v + 5 ] = i * width + j + 1;
2076 tri->indexes[ v + 0 ] = i * width + j;
2077 tri->indexes[ v + 1 ] = i * width + j + 1;
2078 tri->indexes[ v + 2 ] = (i+1) * width + j + 1;
2079 tri->indexes[ v + 3 ] = i * width + j;
2080 tri->indexes[ v + 4 ] = (i+1) * width + j + 1;
2081 tri->indexes[ v + 5 ] = (i+1) * width + j;
2086 fileSystem->FreeFile( data );
2088 modelSurface_t surface;
2090 surface.geometry = tri;
2092 surface.shader = tr.defaultMaterial; // declManager->FindMaterial( "shaderDemos/megaTexture" );
2094 this->AddSurface( surface );
2100 //=============================================================================
2104 idRenderModelStatic::PurgeModel
2107 void idRenderModelStatic::PurgeModel() {
2109 modelSurface_t *surf;
2111 for ( i = 0 ; i < surfaces.Num() ; i++ ) {
2112 surf = &surfaces[i];
2114 if ( surf->geometry ) {
2115 R_FreeStaticTriSurf( surf->geometry );
2125 idRenderModelStatic::FreeVertexCache
2127 We are about to restart the vertex cache, so dump everything
2130 void idRenderModelStatic::FreeVertexCache( void ) {
2131 for ( int j = 0 ; j < surfaces.Num() ; j++ ) {
2132 srfTriangles_t *tri = surfaces[j].geometry;
2136 if ( tri->ambientCache ) {
2137 vertexCache.Free( tri->ambientCache );
2138 tri->ambientCache = NULL;
2140 // static shadows may be present
2141 if ( tri->shadowCache ) {
2142 vertexCache.Free( tri->shadowCache );
2143 tri->shadowCache = NULL;
2150 idRenderModelStatic::ReadFromDemoFile
2153 void idRenderModelStatic::ReadFromDemoFile( class idDemoFile *f ) {
2156 InitEmpty( f->ReadHashString() );
2158 int i, j, numSurfaces;
2159 f->ReadInt( numSurfaces );
2161 for ( i = 0 ; i < numSurfaces ; i++ ) {
2162 modelSurface_t surf;
2164 surf.shader = declManager->FindMaterial( f->ReadHashString() );
2166 srfTriangles_t *tri = R_AllocStaticTriSurf();
2168 f->ReadInt( tri->numIndexes );
2169 R_AllocStaticTriSurfIndexes( tri, tri->numIndexes );
2170 for ( j = 0; j < tri->numIndexes; ++j )
2171 f->ReadInt( (int&)tri->indexes[j] );
2173 f->ReadInt( tri->numVerts );
2174 R_AllocStaticTriSurfVerts( tri, tri->numVerts );
2175 for ( j = 0; j < tri->numVerts; ++j ) {
2176 f->ReadVec3( tri->verts[j].xyz );
2177 f->ReadVec2( tri->verts[j].st );
2178 f->ReadVec3( tri->verts[j].normal );
2179 f->ReadVec3( tri->verts[j].tangents[0] );
2180 f->ReadVec3( tri->verts[j].tangents[1] );
2181 f->ReadUnsignedChar( tri->verts[j].color[0] );
2182 f->ReadUnsignedChar( tri->verts[j].color[1] );
2183 f->ReadUnsignedChar( tri->verts[j].color[2] );
2184 f->ReadUnsignedChar( tri->verts[j].color[3] );
2187 surf.geometry = tri;
2189 this->AddSurface( surf );
2191 this->FinishSurfaces();
2196 idRenderModelStatic::WriteToDemoFile
2199 void idRenderModelStatic::WriteToDemoFile( class idDemoFile *f ) {
2202 // note that it has been updated
2203 lastArchivedFrame = tr.frameCount;
2205 data[0] = DC_DEFINE_MODEL;
2206 f->WriteInt( data[0] );
2207 f->WriteHashString( this->Name() );
2209 int i, j, iData = surfaces.Num();
2210 f->WriteInt( iData );
2212 for ( i = 0 ; i < surfaces.Num() ; i++ ) {
2213 const modelSurface_t *surf = &surfaces[i];
2215 f->WriteHashString( surf->shader->GetName() );
2217 srfTriangles_t *tri = surf->geometry;
2218 f->WriteInt( tri->numIndexes );
2219 for ( j = 0; j < tri->numIndexes; ++j )
2220 f->WriteInt( (int&)tri->indexes[j] );
2221 f->WriteInt( tri->numVerts );
2222 for ( j = 0; j < tri->numVerts; ++j ) {
2223 f->WriteVec3( tri->verts[j].xyz );
2224 f->WriteVec2( tri->verts[j].st );
2225 f->WriteVec3( tri->verts[j].normal );
2226 f->WriteVec3( tri->verts[j].tangents[0] );
2227 f->WriteVec3( tri->verts[j].tangents[1] );
2228 f->WriteUnsignedChar( tri->verts[j].color[0] );
2229 f->WriteUnsignedChar( tri->verts[j].color[1] );
2230 f->WriteUnsignedChar( tri->verts[j].color[2] );
2231 f->WriteUnsignedChar( tri->verts[j].color[3] );
2238 idRenderModelStatic::IsLoaded
2241 bool idRenderModelStatic::IsLoaded( void ) {
2247 idRenderModelStatic::SetLevelLoadReferenced
2250 void idRenderModelStatic::SetLevelLoadReferenced( bool referenced ) {
2251 levelLoadReferenced = referenced;
2256 idRenderModelStatic::IsLevelLoadReferenced
2259 bool idRenderModelStatic::IsLevelLoadReferenced( void ) {
2260 return levelLoadReferenced;
2265 idRenderModelStatic::TouchData
2268 void idRenderModelStatic::TouchData( void ) {
2269 for ( int i = 0 ; i < surfaces.Num() ; i++ ) {
2270 const modelSurface_t *surf = &surfaces[i];
2272 // re-find the material to make sure it gets added to the
2274 declManager->FindMaterial( surf->shader->GetName() );
2280 idRenderModelStatic::DeleteSurfaceWithId
2283 bool idRenderModelStatic::DeleteSurfaceWithId( int id ) {
2286 for ( i = 0; i < surfaces.Num(); i++ ) {
2287 if ( surfaces[i].id == id ) {
2288 R_FreeStaticTriSurf( surfaces[i].geometry );
2289 surfaces.RemoveIndex( i );
2298 idRenderModelStatic::DeleteSurfacesWithNegativeId
2301 void idRenderModelStatic::DeleteSurfacesWithNegativeId( void ) {
2304 for ( i = 0; i < surfaces.Num(); i++ ) {
2305 if ( surfaces[i].id < 0 ) {
2306 R_FreeStaticTriSurf( surfaces[i].geometry );
2307 surfaces.RemoveIndex( i );
2315 idRenderModelStatic::FindSurfaceWithId
2318 bool idRenderModelStatic::FindSurfaceWithId( int id, int &surfaceNum ) {
2321 for ( i = 0; i < surfaces.Num(); i++ ) {
2322 if ( surfaces[i].id == id ) {