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