1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
6
7 This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
8
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.
13
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.
18
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/>.
21
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.
23
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.
25
26 ===========================================================================
27 */
28
29 #include "sys/platform.h"
30 #include "renderer/tr_local.h"
31 #include "renderer/Model_local.h"
32
33 #include "renderer/ModelOverlay.h"
34
35 /*
36 ====================
37 idRenderModelOverlay::idRenderModelOverlay
38 ====================
39 */
idRenderModelOverlay()40 idRenderModelOverlay::idRenderModelOverlay() {
41 }
42
43 /*
44 ====================
45 idRenderModelOverlay::~idRenderModelOverlay
46 ====================
47 */
~idRenderModelOverlay()48 idRenderModelOverlay::~idRenderModelOverlay() {
49 int i, k;
50
51 for ( k = 0; k < materials.Num(); k++ ) {
52 for ( i = 0; i < materials[k]->surfaces.Num(); i++ ) {
53 FreeSurface( materials[k]->surfaces[i] );
54 }
55 materials[k]->surfaces.Clear();
56 delete materials[k];
57 }
58 materials.Clear();
59 }
60
61 /*
62 ====================
63 idRenderModelOverlay::Alloc
64 ====================
65 */
Alloc(void)66 idRenderModelOverlay *idRenderModelOverlay::Alloc( void ) {
67 return new idRenderModelOverlay;
68 }
69
70 /*
71 ====================
72 idRenderModelOverlay::Free
73 ====================
74 */
Free(idRenderModelOverlay * overlay)75 void idRenderModelOverlay::Free( idRenderModelOverlay *overlay ) {
76 delete overlay;
77 }
78
79 /*
80 ====================
81 idRenderModelOverlay::FreeSurface
82 ====================
83 */
FreeSurface(overlaySurface_t * surface)84 void idRenderModelOverlay::FreeSurface( overlaySurface_t *surface ) {
85 if ( surface->verts ) {
86 Mem_Free( surface->verts );
87 }
88 if ( surface->indexes ) {
89 Mem_Free( surface->indexes );
90 }
91 Mem_Free( surface );
92 }
93
94 /*
95 =====================
96 idRenderModelOverlay::CreateOverlay
97
98 This projects on both front and back sides to avoid seams
99 The material should be clamped, because entire triangles are added, some of which
100 may extend well past the 0.0 to 1.0 texture range
101 =====================
102 */
CreateOverlay(const idRenderModel * model,const idPlane localTextureAxis[2],const idMaterial * mtr)103 void idRenderModelOverlay::CreateOverlay( const idRenderModel *model, const idPlane localTextureAxis[2], const idMaterial *mtr ) {
104 int i, maxVerts, maxIndexes, surfNum;
105
106 // count up the maximum possible vertices and indexes per surface
107 maxVerts = 0;
108 maxIndexes = 0;
109 for ( surfNum = 0; surfNum < model->NumSurfaces(); surfNum++ ) {
110 const modelSurface_t *surf = model->Surface( surfNum );
111 if ( surf->geometry->numVerts > maxVerts ) {
112 maxVerts = surf->geometry->numVerts;
113 }
114 if ( surf->geometry->numIndexes > maxIndexes ) {
115 maxIndexes = surf->geometry->numIndexes;
116 }
117 }
118
119 // make temporary buffers for the building process
120 overlayVertex_t *overlayVerts = (overlayVertex_t *)_alloca( maxVerts * sizeof( *overlayVerts ) );
121 glIndex_t *overlayIndexes = (glIndex_t *)_alloca16( maxIndexes * sizeof( *overlayIndexes ) );
122
123 // pull out the triangles we need from the base surfaces
124 for ( surfNum = 0; surfNum < model->NumBaseSurfaces(); surfNum++ ) {
125 const modelSurface_t *surf = model->Surface( surfNum );
126 float d;
127
128 if ( !surf->geometry || !surf->shader ) {
129 continue;
130 }
131
132 // some surfaces can explicitly disallow overlays
133 if ( !surf->shader->AllowOverlays() ) {
134 continue;
135 }
136
137 const srfTriangles_t *stri = surf->geometry;
138
139 // try to cull the whole surface along the first texture axis
140 d = stri->bounds.PlaneDistance( localTextureAxis[0] );
141 if ( d < 0.0f || d > 1.0f ) {
142 continue;
143 }
144
145 // try to cull the whole surface along the second texture axis
146 d = stri->bounds.PlaneDistance( localTextureAxis[1] );
147 if ( d < 0.0f || d > 1.0f ) {
148 continue;
149 }
150
151 byte *cullBits = (byte *)_alloca16( stri->numVerts * sizeof( cullBits[0] ) );
152 idVec2 *texCoords = (idVec2 *)_alloca16( stri->numVerts * sizeof( texCoords[0] ) );
153
154 SIMDProcessor->OverlayPointCull( cullBits, texCoords, localTextureAxis, stri->verts, stri->numVerts );
155
156 glIndex_t *vertexRemap = (glIndex_t *)_alloca16( sizeof( vertexRemap[0] ) * stri->numVerts );
157 SIMDProcessor->Memset( vertexRemap, -1, sizeof( vertexRemap[0] ) * stri->numVerts );
158
159 // find triangles that need the overlay
160 int numVerts = 0;
161 int numIndexes = 0;
162 int triNum = 0;
163 for ( int index = 0; index < stri->numIndexes; index += 3, triNum++ ) {
164 int v1 = stri->indexes[index+0];
165 int v2 = stri->indexes[index+1];
166 int v3 = stri->indexes[index+2];
167
168 // skip triangles completely off one side
169 if ( cullBits[v1] & cullBits[v2] & cullBits[v3] ) {
170 continue;
171 }
172
173 // we could do more precise triangle culling, like the light interaction does, if desired
174
175 // keep this triangle
176 for ( int vnum = 0; vnum < 3; vnum++ ) {
177 int ind = stri->indexes[index+vnum];
178 if ( vertexRemap[ind] == (glIndex_t)-1 ) {
179 vertexRemap[ind] = numVerts;
180
181 overlayVerts[numVerts].vertexNum = ind;
182 overlayVerts[numVerts].st[0] = texCoords[ind][0];
183 overlayVerts[numVerts].st[1] = texCoords[ind][1];
184
185 numVerts++;
186 }
187 overlayIndexes[numIndexes++] = vertexRemap[ind];
188 }
189 }
190
191 if ( !numIndexes ) {
192 continue;
193 }
194
195 overlaySurface_t *s = (overlaySurface_t *) Mem_Alloc( sizeof( overlaySurface_t ) );
196 s->surfaceNum = surfNum;
197 s->surfaceId = surf->id;
198 s->verts = (overlayVertex_t *)Mem_Alloc( numVerts * sizeof( s->verts[0] ) );
199 memcpy( s->verts, overlayVerts, numVerts * sizeof( s->verts[0] ) );
200 s->numVerts = numVerts;
201 s->indexes = (glIndex_t *)Mem_Alloc( numIndexes * sizeof( s->indexes[0] ) );
202 memcpy( s->indexes, overlayIndexes, numIndexes * sizeof( s->indexes[0] ) );
203 s->numIndexes = numIndexes;
204
205 for ( i = 0; i < materials.Num(); i++ ) {
206 if ( materials[i]->material == mtr ) {
207 break;
208 }
209 }
210 if ( i < materials.Num() ) {
211 materials[i]->surfaces.Append( s );
212 } else {
213 overlayMaterial_t *mat = new overlayMaterial_t;
214 mat->material = mtr;
215 mat->surfaces.Append( s );
216 materials.Append( mat );
217 }
218 }
219
220 // remove the oldest overlay surfaces if there are too many per material
221 for ( i = 0; i < materials.Num(); i++ ) {
222 while( materials[i]->surfaces.Num() > MAX_OVERLAY_SURFACES ) {
223 FreeSurface( materials[i]->surfaces[0] );
224 materials[i]->surfaces.RemoveIndex( 0 );
225 }
226 }
227 }
228
229 /*
230 ====================
231 idRenderModelOverlay::AddOverlaySurfacesToModel
232 ====================
233 */
AddOverlaySurfacesToModel(idRenderModel * baseModel)234 void idRenderModelOverlay::AddOverlaySurfacesToModel( idRenderModel *baseModel ) {
235 int i, j, k, numVerts, numIndexes, surfaceNum;
236 const modelSurface_t *baseSurf;
237 idRenderModelStatic *staticModel;
238 overlaySurface_t *surf;
239 srfTriangles_t *newTri;
240 modelSurface_t *newSurf;
241
242 if ( baseModel == NULL || baseModel->IsDefaultModel() ) {
243 return;
244 }
245
246 // md5 models won't have any surfaces when r_showSkel is set
247 if ( !baseModel->NumSurfaces() ) {
248 return;
249 }
250
251 if ( baseModel->IsDynamicModel() != DM_STATIC ) {
252 common->Error( "idRenderModelOverlay::AddOverlaySurfacesToModel: baseModel is not a static model" );
253 }
254
255 assert( dynamic_cast<idRenderModelStatic *>(baseModel) != NULL );
256 staticModel = static_cast<idRenderModelStatic *>(baseModel);
257
258 staticModel->overlaysAdded = 0;
259
260 if ( !materials.Num() ) {
261 staticModel->DeleteSurfacesWithNegativeId();
262 return;
263 }
264
265 for ( k = 0; k < materials.Num(); k++ ) {
266
267 numVerts = numIndexes = 0;
268 for ( i = 0; i < materials[k]->surfaces.Num(); i++ ) {
269 numVerts += materials[k]->surfaces[i]->numVerts;
270 numIndexes += materials[k]->surfaces[i]->numIndexes;
271 }
272
273 if ( staticModel->FindSurfaceWithId( -1 - k, surfaceNum ) ) {
274 newSurf = &staticModel->surfaces[surfaceNum];
275 } else {
276 newSurf = &staticModel->surfaces.Alloc();
277 newSurf->geometry = NULL;
278 newSurf->shader = materials[k]->material;
279 newSurf->id = -1 - k;
280 }
281
282 if ( newSurf->geometry == NULL || newSurf->geometry->numVerts < numVerts || newSurf->geometry->numIndexes < numIndexes ) {
283 R_FreeStaticTriSurf( newSurf->geometry );
284 newSurf->geometry = R_AllocStaticTriSurf();
285 R_AllocStaticTriSurfVerts( newSurf->geometry, numVerts );
286 R_AllocStaticTriSurfIndexes( newSurf->geometry, numIndexes );
287 SIMDProcessor->Memset( newSurf->geometry->verts, 0, numVerts * sizeof( newTri->verts[0] ) );
288 } else {
289 R_FreeStaticTriSurfVertexCaches( newSurf->geometry );
290 }
291
292 newTri = newSurf->geometry;
293 numVerts = numIndexes = 0;
294
295 for ( i = 0; i < materials[k]->surfaces.Num(); i++ ) {
296 surf = materials[k]->surfaces[i];
297
298 // get the model surface for this overlay surface
299 if ( surf->surfaceNum < staticModel->NumSurfaces() ) {
300 baseSurf = staticModel->Surface( surf->surfaceNum );
301 } else {
302 baseSurf = NULL;
303 }
304
305 // if the surface ids no longer match
306 if ( !baseSurf || baseSurf->id != surf->surfaceId ) {
307 // find the surface with the correct id
308 if ( staticModel->FindSurfaceWithId( surf->surfaceId, surf->surfaceNum ) ) {
309 baseSurf = staticModel->Surface( surf->surfaceNum );
310 } else {
311 // the surface with this id no longer exists
312 FreeSurface( surf );
313 materials[k]->surfaces.RemoveIndex( i );
314 i--;
315 continue;
316 }
317 }
318
319 // copy indexes;
320 for ( j = 0; j < surf->numIndexes; j++ ) {
321 newTri->indexes[numIndexes + j] = numVerts + surf->indexes[j];
322 }
323 numIndexes += surf->numIndexes;
324
325 // copy vertices
326 for ( j = 0; j < surf->numVerts; j++ ) {
327 overlayVertex_t *overlayVert = &surf->verts[j];
328
329 newTri->verts[numVerts].st[0] = overlayVert->st[0];
330 newTri->verts[numVerts].st[1] = overlayVert->st[1];
331
332 if ( overlayVert->vertexNum >= baseSurf->geometry->numVerts ) {
333 // This can happen when playing a demofile and a model has been changed since it was recorded, so just issue a warning and go on.
334 common->Warning( "idRenderModelOverlay::AddOverlaySurfacesToModel: overlay vertex out of range. Model has probably changed since generating the overlay." );
335 FreeSurface( surf );
336 materials[k]->surfaces.RemoveIndex( i );
337 staticModel->DeleteSurfaceWithId( newSurf->id );
338 return;
339 }
340 newTri->verts[numVerts].xyz = baseSurf->geometry->verts[overlayVert->vertexNum].xyz;
341 numVerts++;
342 }
343 }
344
345 newTri->numVerts = numVerts;
346 newTri->numIndexes = numIndexes;
347 R_BoundTriSurf( newTri );
348
349 staticModel->overlaysAdded++; // so we don't create an overlay on an overlay surface
350 }
351 }
352
353 /*
354 ====================
355 idRenderModelOverlay::RemoveOverlaySurfacesFromModel
356 ====================
357 */
RemoveOverlaySurfacesFromModel(idRenderModel * baseModel)358 void idRenderModelOverlay::RemoveOverlaySurfacesFromModel( idRenderModel *baseModel ) {
359 idRenderModelStatic *staticModel;
360
361 assert( dynamic_cast<idRenderModelStatic *>(baseModel) != NULL );
362 staticModel = static_cast<idRenderModelStatic *>(baseModel);
363
364 staticModel->DeleteSurfacesWithNegativeId();
365 staticModel->overlaysAdded = 0;
366 }
367
368 /*
369 ====================
370 idRenderModelOverlay::ReadFromDemoFile
371 ====================
372 */
ReadFromDemoFile(idDemoFile * f)373 void idRenderModelOverlay::ReadFromDemoFile( idDemoFile *f ) {
374 // FIXME: implement
375 }
376
377 /*
378 ====================
379 idRenderModelOverlay::WriteToDemoFile
380 ====================
381 */
WriteToDemoFile(idDemoFile * f) const382 void idRenderModelOverlay::WriteToDemoFile( idDemoFile *f ) const {
383 // FIXME: implement
384 }
385