doom3-gpl
Doom 3 GPL source release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
renderbump.cpp
Go to the documentation of this file.
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 "../../../idlib/precompiled.h"
30 #pragma hdrstop
31 
32 #ifdef WIN32
33 #include <windows.h>
34 #include <GL/gl.h>
35 #include <GL/glu.h>
36 #include "../../../sys/win32/win_local.h"
37 #endif
38 
39 #include "../../../renderer/tr_local.h"
40 
41 /*
42 
43  render a normalmap tga file from an ase model for bump mapping
44 
45  To make ray-tracing into the high poly mesh efficient, we preconstruct
46  a 3D hash table of the triangles that need to be tested for a given source
47  point.
48 
49  This task is easier than a general ray tracing optimization, because we
50  known that all of the triangles are going to be "near" the source point.
51 
52  TraceFraction determines the maximum distance in any direction that
53  a trace will go. It is expressed as a fraction of the largest axis of
54  the bounding box, so it doesn't matter what units are used for modeling.
55 
56 
57 */
58 
59 #define MAX_QPATH 256
60 
61 #define DEFAULT_TRACE_FRACTION 0.05
62 
63 #define INITIAL_TRI_TO_LINK_EXPANSION 16 // can grow as needed
64 #define HASH_AXIS_BINS 100
65 
66 typedef struct {
67  int faceNum;
68  int nextLink;
69 } triLink_t;
70 
71 typedef struct {
72  int triLink;
73  int rayNumber; // don't need to test again if still on same ray
74 } binLink_t;
75 
76 #define MAX_LINKS_PER_BLOCK 0x100000
77 #define MAX_LINK_BLOCKS 0x100
78 typedef struct {
80  float binSize[3];
82  triLink_t *linkBlocks[MAX_LINK_BLOCKS];
84 } triHash_t;
85 
86 typedef struct {
87  char outputName[MAX_QPATH];
88  char highName[MAX_QPATH];
92  float *edgeDistances; // starts out -1 for untraced, for each texel, 0 = true interior, >0 = off-edge rasterization
93  int width, height;
94  int antiAlias;
95  int outline;
98  float traceFrac;
99  float traceDist;
100  srfTriangles_t *mesh; // high poly mesh
103 } renderBump_t;
104 
105 static float traceFraction;
106 static int rayNumber; // for avoiding retests of bins and faces
107 
108 static int oldWidth, oldHeight;
109 
110 /*
111 ===============
112 SaveWindow
113 ===============
114 */
115 static void SaveWindow( void ) {
116  oldWidth = glConfig.vidWidth;
117  oldHeight = glConfig.vidHeight;
118 }
119 
120 /*
121 ===============
122 ResizeWindow
123 ===============
124 */
125 static void ResizeWindow( int width, int height ) {
126 #ifdef WIN32
127  int winWidth, winHeight;
128  if ( glConfig.isFullscreen ) {
129  winWidth = width;
130  winHeight = height;
131  } else {
132  RECT r;
133 
134  // adjust width and height for window border
135  r.bottom = height;
136  r.left = 0;
137  r.top = 0;
138  r.right = width;
139 
140  AdjustWindowRect (&r, WINDOW_STYLE|WS_SYSMENU, FALSE);
141  winHeight = r.bottom - r.top;
142  winWidth = r.right - r.left;
143 
144  }
145  SetWindowPos( win32.hWnd, HWND_TOP, 0, 0, winWidth, winHeight, SWP_SHOWWINDOW );
146 
147  qwglMakeCurrent( win32.hDC, win32.hGLRC );
148 #endif
149 }
150 
151 /*
152 ===============
153 RestoreWindow
154 ===============
155 */
156 static void RestoreWindow( void ) {
157 #ifdef WIN32
158  int winWidth, winHeight;
159  if ( glConfig.isFullscreen ) {
160  winWidth = oldWidth;
161  winHeight = oldHeight;
162  } else {
163  RECT r;
164 
165  // adjust width and height for window border
166  r.bottom = oldHeight;
167  r.left = 0;
168  r.top = 0;
169  r.right = oldWidth;
170 
171  AdjustWindowRect (&r, WINDOW_STYLE|WS_SYSMENU, FALSE);
172  winHeight = r.bottom - r.top;
173  winWidth = r.right - r.left;
174  }
175  SetWindowPos( win32.hWnd, HWND_TOP, 0, 0, winWidth, winHeight, SWP_SHOWWINDOW );
176 #endif
177 }
178 
179 /*
180 ================
181 OutlineNormalMap
182 
183 Puts a single pixel border around all non-empty pixels
184 Does NOT copy the alpha channel, so it can be used as
185 an alpha test map.
186 ================
187 */
188 static void OutlineNormalMap( byte *data, int width, int height, int emptyR, int emptyG, int emptyB ) {
189  byte *orig;
190  int i, j, k, l;
191  idVec3 normal;
192  byte *out;
193 
194  orig = (byte *)Mem_Alloc( width * height * 4 );
195  memcpy( orig, data, width * height * 4 );
196 
197  for ( i = 0 ; i < width ; i++ ) {
198  for ( j = 0 ; j < height ; j++ ) {
199  out = data + ( j * width + i ) * 4;
200  if ( out[0] != emptyR || out[1] != emptyG || out[2] != emptyB ) {
201  continue;
202  }
203 
204  normal = vec3_origin;
205  for ( k = -1 ; k < 2 ; k++ ) {
206  for ( l = -1 ; l < 2 ; l++ ) {
207  byte *in;
208 
209  in = orig + ( ((j+l)&(height-1))*width + ((i+k)&(width-1)) ) * 4;
210 
211  if ( in[0] == emptyR && in[1] == emptyG && in[2] == emptyB ) {
212  continue;
213  }
214 
215  normal[0] += ( in[0] - 128 );
216  normal[1] += ( in[1] - 128 );
217  normal[2] += ( in[2] - 128 );
218  }
219  }
220 
221  if ( normal.Normalize() < 0.5 ) {
222  continue; // no valid samples
223  }
224 
225  out[0] = 128 + 127 * normal[0];
226  out[1] = 128 + 127 * normal[1];
227  out[2] = 128 + 127 * normal[2];
228  }
229  }
230 
231  Mem_Free( orig );
232 }
233 
234 /*
235 ================
236 OutlineColorMap
237 
238 Puts a single pixel border around all non-empty pixels
239 Does NOT copy the alpha channel, so it can be used as
240 an alpha test map.
241 ================
242 */
243 static void OutlineColorMap( byte *data, int width, int height, int emptyR, int emptyG, int emptyB ) {
244  byte *orig;
245  int i, j, k, l;
246  idVec3 normal;
247  byte *out;
248 
249  orig = (byte *)Mem_Alloc( width * height * 4 );
250  memcpy( orig, data, width * height * 4 );
251 
252  for ( i = 0 ; i < width ; i++ ) {
253  for ( j = 0 ; j < height ; j++ ) {
254  out = data + ( j * width + i ) * 4;
255  if ( out[0] != emptyR || out[1] != emptyG || out[2] != emptyB ) {
256  continue;
257  }
258 
259  normal = vec3_origin;
260  int count = 0;
261  for ( k = -1 ; k < 2 ; k++ ) {
262  for ( l = -1 ; l < 2 ; l++ ) {
263  byte *in;
264 
265  in = orig + ( ((j+l)&(height-1))*width + ((i+k)&(width-1)) ) * 4;
266 
267  if ( in[0] == emptyR && in[1] == emptyG && in[2] == emptyB ) {
268  continue;
269  }
270 
271  normal[0] += in[0];
272  normal[1] += in[1];
273  normal[2] += in[2];
274  count++;
275  }
276  }
277 
278  if ( !count ) {
279  continue;
280  }
281  normal *= (1.0 / count );
282 
283  out[0] = normal[0];
284  out[1] = normal[1];
285  out[2] = normal[2];
286  }
287  }
288 
289  Mem_Free( orig );
290 }
291 
292 
293 
294 /*
295 ================
296 FreeTriHash
297 ================
298 */
299 static void FreeTriHash( triHash_t *hash ) {
300  for ( int i = 0 ; i < hash->numLinkBlocks ; i++ ) {
301  Mem_Free( hash->linkBlocks[i] );
302  }
303  Mem_Free( hash );
304 }
305 
306 /*
307 ================
308 CreateTriHash
309 ================
310 */
311 static triHash_t *CreateTriHash( const srfTriangles_t *highMesh ) {
312  triHash_t *hash;
313  int i, j, k, l;
314  idBounds bounds, triBounds;
315  int iBounds[2][3];
316  int maxLinks, numLinks;
317 
318  hash = (triHash_t *)Mem_Alloc( sizeof( *hash ) );
319  memset( hash, 0, sizeof( *hash ) );
320 
321  // find the bounding volume for the mesh
322  bounds.Clear();
323  for ( i = 0 ; i < highMesh->numVerts ; i++ ) {
324  bounds.AddPoint( highMesh->verts[i].xyz );
325  }
326 
327  hash->bounds = bounds;
328 
329  // divide each axis as needed
330  for ( i = 0 ; i < 3 ; i++ ) {
331  hash->binSize[i] = ( bounds[1][i] - bounds[0][i] ) / HASH_AXIS_BINS;
332  if ( hash->binSize[i] <= 0 ) {
333  common->FatalError( "CreateTriHash: bad bounds: (%f %f %f) to (%f %f %f)",
334  bounds[0][0],bounds[0][1],bounds[0][2],
335  bounds[1][0],bounds[1][1],bounds[1][2] );
336  }
337  }
338 
339  // a -1 link number terminated the link chain
340  memset( hash->binLinks, -1, sizeof( hash->binLinks ) );
341 
342  numLinks = 0;
343 
344  hash->linkBlocks[hash->numLinkBlocks] = (triLink_t *)Mem_Alloc( MAX_LINKS_PER_BLOCK * sizeof( triLink_t ) );
345  hash->numLinkBlocks++;
346  maxLinks = hash->numLinkBlocks * MAX_LINKS_PER_BLOCK;
347 
348  // for each triangle, place a triLink in each bin that might reference it
349  for ( i = 0 ; i < highMesh->numIndexes ; i+=3 ) {
350  // determine which hash bins the triangle will need to be in
351  triBounds.Clear();
352  for ( j = 0 ; j < 3 ; j++ ) {
353  triBounds.AddPoint( highMesh->verts[ highMesh->indexes[i+j] ].xyz );
354  }
355  for ( j = 0 ; j < 3 ; j++ ) {
356  iBounds[0][j] = ( triBounds[0][j] - hash->bounds[0][j] ) / hash->binSize[j];
357  iBounds[0][j] -= 0.001; // epsilon
358  if ( iBounds[0][j] < 0 ) {
359  iBounds[0][j] = 0;
360  } else if ( iBounds[0][j] >= HASH_AXIS_BINS ) {
361  iBounds[0][j] = HASH_AXIS_BINS-1;
362  }
363 
364  iBounds[1][j] = ( triBounds[1][j] - hash->bounds[0][j] ) / hash->binSize[j];
365  iBounds[0][j] += 0.001; // epsilon
366  if ( iBounds[1][j] < 0 ) {
367  iBounds[1][j] = 0;
368  } else if ( iBounds[1][j] >= HASH_AXIS_BINS ) {
369  iBounds[1][j] = HASH_AXIS_BINS-1;
370  }
371  }
372 
373  // add the links
374  for ( j = iBounds[0][0] ; j <= iBounds[1][0] ; j++ ) {
375  for ( k = iBounds[0][1] ; k <= iBounds[1][1] ; k++ ) {
376  for ( l = iBounds[0][2] ; l <= iBounds[1][2] ; l++ ) {
377  if ( numLinks == maxLinks ) {
378  hash->linkBlocks[hash->numLinkBlocks] = (triLink_t *)Mem_Alloc( MAX_LINKS_PER_BLOCK * sizeof( triLink_t ) );
379  hash->numLinkBlocks++;
380  maxLinks = hash->numLinkBlocks * MAX_LINKS_PER_BLOCK;
381  }
382 
383  triLink_t *link = &hash->linkBlocks[ numLinks / MAX_LINKS_PER_BLOCK ][ numLinks % MAX_LINKS_PER_BLOCK ];
384  link->faceNum = i / 3;
385  link->nextLink = hash->binLinks[j][k][l].triLink;
386  hash->binLinks[j][k][l].triLink = numLinks;
387  numLinks++;
388  }
389  }
390  }
391  }
392 
393  common->Printf( "%i triangles made %i links\n", highMesh->numIndexes / 3, numLinks );
394 
395  return hash;
396 }
397 
398 
399 /*
400 =================
401 TraceToMeshFace
402 
403 Returns the distance from the point to the intersection, or DIST_NO_INTERSECTION
404 =================
405 */
406 #define DIST_NO_INTERSECTION -999999999.0f
407 static float TraceToMeshFace( const srfTriangles_t *highMesh, int faceNum,
408  float minDist, float maxDist,
409  const idVec3 &point, const idVec3 &normal, idVec3 &sampledNormal,
410  byte sampledColor[4] ) {
411  int j;
412  float dist;
413  const idVec3 *v[3];
414  const idPlane *plane;
415  idVec3 edge;
416  float d;
417  idVec3 dir[3];
418  float baseArea;
419  float bary[3];
420  idVec3 testVert;
421 
422  v[0] = &highMesh->verts[ highMesh->indexes[ faceNum * 3 + 0 ] ].xyz;
423  v[1] = &highMesh->verts[ highMesh->indexes[ faceNum * 3 + 1 ] ].xyz;
424  v[2] = &highMesh->verts[ highMesh->indexes[ faceNum * 3 + 2 ] ].xyz;
425 
426  plane = highMesh->facePlanes + faceNum;
427 
428  // only test against planes facing the same direction as our normal
429  d = plane->Normal() * normal;
430  if ( d <= 0.0001f ) {
431  return DIST_NO_INTERSECTION;
432  }
433 
434  // find the point of impact on the plane
435  dist = plane->Distance( point );
436  dist /= -d;
437 
438  testVert = point + dist * normal;
439 
440  // if this would be beyond our requested trace distance,
441  // don't even check it
442  if ( dist > maxDist ) {
443  return DIST_NO_INTERSECTION;
444  }
445 
446  if ( dist < minDist ) {
447  return DIST_NO_INTERSECTION;
448  }
449 
450  // if normal is inside all edge planes, this face is hit
451  VectorSubtract( *v[0], point, dir[0] );
452  VectorSubtract( *v[1], point, dir[1] );
453  edge = dir[0].Cross( dir[1] );
454  d = DotProduct( normal, edge );
455  if ( d > 0.0f ) {
456  return DIST_NO_INTERSECTION;
457  }
458  VectorSubtract( *v[2], point, dir[2] );
459  edge = dir[1].Cross( dir[2] );
460  d = DotProduct( normal, edge );
461  if ( d > 0.0f ) {
462  return DIST_NO_INTERSECTION;
463  }
464  edge = dir[2].Cross( dir[0] );
465  d = DotProduct( normal, edge );
466  if ( d > 0.0f ) {
467  return DIST_NO_INTERSECTION;
468  }
469 
470  // calculate barycentric coordinates of the impact point
471  // on the high poly triangle
472  bary[0] = idWinding::TriangleArea( testVert, *v[1], *v[2] );
473  bary[1] = idWinding::TriangleArea( *v[0], testVert, *v[2] );
474  bary[2] = idWinding::TriangleArea( *v[0], *v[1], testVert );
475 
476  baseArea = idWinding::TriangleArea( *v[0], *v[1], *v[2] );
477  bary[0] /= baseArea;
478  bary[1] /= baseArea;
479  bary[2] /= baseArea;
480 
481  if ( bary[0] + bary[1] + bary[2] > 1.1 ) {
482  bary[0] = bary[0];
483  return DIST_NO_INTERSECTION;
484  }
485 
486  // triangularly interpolate the normals to the sample point
487  sampledNormal = vec3_origin;
488  for ( j = 0 ; j < 3 ; j++ ) {
489  sampledNormal += bary[j] * highMesh->verts[ highMesh->indexes[ faceNum * 3 + j ] ].normal;
490  }
491  sampledNormal.Normalize();
492 
493  sampledColor[0] = sampledColor[1] = sampledColor[2] = sampledColor[3] = 0;
494  for ( int i = 0 ; i < 4 ; i++ ) {
495  float color = 0.0f;
496  for ( j = 0 ; j < 3 ; j++ ) {
497  color += bary[j] * highMesh->verts[ highMesh->indexes[ faceNum * 3 + j ] ].color[i];
498  }
499  sampledColor[i] = color;
500  }
501  return dist;
502 }
503 
504 
505 /*
506 ================
507 SampleHighMesh
508 
509 Find the best surface normal in the high poly mesh
510 for a ray coming from the surface of the low poly mesh
511 
512 Returns false if the trace doesn't hit anything
513 ================
514 */
515 static bool SampleHighMesh( const renderBump_t *rb,
516  const idVec3 &point, const idVec3 &direction, idVec3 &sampledNormal,
517  byte sampledColor[4] ) {
518  idVec3 p;
519  binLink_t *bl;
520  int linkNum;
521  int faceNum;
522  float dist, bestDist;
523  int block[3];
524  float maxDist;
525  int c_hits;
526  int i;
527  idVec3 normal;
528 
529  // we allow non-normalized directions on input
530  normal = direction;
531  normal.Normalize();
532 
533  // increment our uniqueness counter (FIXME: make thread safe?)
534  rayNumber++;
535 
536  // the max distance will be the traceFrac times the longest axis of the high poly model
537  bestDist = -rb->traceDist;
538  maxDist = rb->traceDist;
539 
540  sampledNormal = vec3_origin;
541 
542  c_hits = 0;
543 
544  // this is a pretty damn lazy way to walk through a 3D grid, and has a (very slight)
545  // chance of missing a triangle in a corner crossing case
546 #define RAY_STEPS 100
547  for ( i = 0 ; i < RAY_STEPS ; i++ ) {
548  p = point - rb->hash->bounds[0] + normal * ( -1.0 + 2.0 * i / RAY_STEPS ) * rb->traceDist;
549 
550  block[0] = floor( p[0] / rb->hash->binSize[0] );
551  block[1] = floor( p[1] / rb->hash->binSize[1] );
552  block[2] = floor( p[2] / rb->hash->binSize[2] );
553 
554  if ( block[0] < 0 || block[0] >= HASH_AXIS_BINS ) {
555  continue;
556  }
557  if ( block[1] < 0 || block[1] >= HASH_AXIS_BINS ) {
558  continue;
559  }
560  if ( block[2] < 0 || block[2] >= HASH_AXIS_BINS ) {
561  continue;
562  }
563 
564  // FIXME: casting away const
565  bl = (binLink_t *)&rb->hash->binLinks[block[0]][block[1]][block[2]];
566  if ( bl->rayNumber == rayNumber ) {
567  continue; // already tested this block
568  }
569  bl->rayNumber = rayNumber;
570  linkNum = bl->triLink;
571  triLink_t *link;
572  for ( ; linkNum != -1 ; linkNum = link->nextLink ) {
573  link = &rb->hash->linkBlocks[ linkNum / MAX_LINKS_PER_BLOCK ][ linkNum % MAX_LINKS_PER_BLOCK ];
574 
575  faceNum = link->faceNum;
576  dist = TraceToMeshFace( rb->mesh, faceNum,
577  bestDist, maxDist, point, normal, sampledNormal, sampledColor );
578  if ( dist == DIST_NO_INTERSECTION ) {
579  continue;
580  }
581 
582  c_hits++;
583  // continue looking for a better match
584  bestDist = dist;
585  }
586  }
587 
588  return (bool)( bestDist > -rb->traceDist );
589 }
590 
591 /*
592 =============
593 TriTextureArea
594 
595 This may be negatove
596 =============
597 */
598 static float TriTextureArea( const float a[2], const float b[2], const float c[2] ) {
599  idVec3 d1, d2;
600  idVec3 cross;
601  float area;
602 
603  d1[0] = b[0] - a[0];
604  d1[1] = b[1] - a[1];
605  d1[2] = 0;
606 
607  d2[0] = c[0] - a[0];
608  d2[1] = c[1] - a[1];
609  d2[2] = 0;
610 
611  cross = d1.Cross( d2 );
612  area = 0.5 * cross.Length();
613 
614  if ( cross[2] < 0 ) {
615  return -area;
616  } else {
617  return area;
618  }
619 }
620 
621 /*
622 ================
623 RasterizeTriangle
624 
625 It is ok for the texcoords to wrap around, the rasterization
626 will deal with it properly.
627 ================
628 */
629 static void RasterizeTriangle( const srfTriangles_t *lowMesh, const idVec3 *lowMeshNormals, int lowFaceNum,
630  renderBump_t *rb ) {
631  int i, j, k;
632  float bounds[2][2];
633  float ibounds[2][2];
634  float verts[3][2];
635  float testVert[2];
636  float bary[3];
637  byte *localDest, *globalDest, *colorDest;
638  float edge[3][3];
639  idVec3 sampledNormal;
640  byte sampledColor[4];
641  idVec3 point, normal, traceNormal, tangents[2];
642  float baseArea, totalArea;
643  int r, g, b;
644  idVec3 localNormal;
645 
646  // this is a brain-dead rasterizer, but compared to the ray trace,
647  // nothing we do here is going to matter performance-wise
648 
649  // adjust for resolution and texel centers
650  verts[0][0] = lowMesh->verts[ lowMesh->indexes[lowFaceNum*3+0] ].st[0] * rb->width - 0.5;
651  verts[1][0] = lowMesh->verts[ lowMesh->indexes[lowFaceNum*3+1] ].st[0] * rb->width - 0.5;
652  verts[2][0] = lowMesh->verts[ lowMesh->indexes[lowFaceNum*3+2] ].st[0] * rb->width - 0.5;
653  verts[0][1] = lowMesh->verts[ lowMesh->indexes[lowFaceNum*3+0] ].st[1] * rb->height - 0.5;
654  verts[1][1] = lowMesh->verts[ lowMesh->indexes[lowFaceNum*3+1] ].st[1] * rb->height - 0.5;
655  verts[2][1] = lowMesh->verts[ lowMesh->indexes[lowFaceNum*3+2] ].st[1] * rb->height - 0.5;
656 
657  // find the texcoord bounding box
658  bounds[0][0] = 99999;
659  bounds[0][1] = 99999;
660  bounds[1][0] = -99999;
661  bounds[1][1] = -99999;
662  for ( i = 0 ; i < 2 ; i++ ) {
663  for ( j = 0 ; j < 3 ; j++ ) {
664  if ( verts[j][i] < bounds[0][i] ) {
665  bounds[0][i] = verts[j][i];
666  }
667  if ( verts[j][i] > bounds[1][i] ) {
668  bounds[1][i] = verts[j][i];
669  }
670  }
671  }
672 
673  // we intentionally rasterize somewhat outside the triangles, so
674  // the bilerp support texels (which may be anti-aliased down)
675  // are not just duplications of what is on the interior
676  const float edgeOverlap = 4.0;
677 
678  ibounds[0][0] = floor( bounds[0][0] - edgeOverlap );
679  ibounds[1][0] = ceil( bounds[1][0] + edgeOverlap );
680  ibounds[0][1] = floor( bounds[0][1] - edgeOverlap );
681  ibounds[1][1] = ceil( bounds[1][1] + edgeOverlap );
682 
683  // calculate edge vectors
684  for ( i = 0 ; i < 3 ; i++ ) {
685  float *v1, *v2;
686 
687  v1 = verts[i];
688  v2 = verts[(i+1)%3];
689 
690  edge[i][0] = v2[1] - v1[1];
691  edge[i][1] = v1[0] - v2[0];
692  float len = sqrt( edge[i][0] * edge[i][0] + edge[i][1] * edge[i][1] );
693  edge[i][0] /= len;
694  edge[i][1] /= len;
695  edge[i][2] = -( v1[0] * edge[i][0] + v1[1] * edge[i][1] );
696  }
697 
698  // itterate over the bounding box, testing against edge vectors
699  for ( i = ibounds[0][1] ; i < ibounds[1][1] ; i++ ) {
700  for ( j = ibounds[0][0] ; j < ibounds[1][0] ; j++ ) {
701  float dists[3];
702 
703  k = ( ( i & (rb->height-1) ) * rb->width + ( j & (rb->width-1) ) ) * 4;
704  colorDest = &rb->colorPic[k];
705  localDest = &rb->localPic[k];
706  globalDest = &rb->globalPic[k];
707 
708 #define SKIP_MIRRORS
709 
710  float *edgeDistance = &rb->edgeDistances[k/4];
711 #ifdef SKIP_MIRRORS
712  // if this texel has already been filled by a true interior pixel, don't overwrite it
713  if ( *edgeDistance == 0 ) {
714  continue;
715  }
716 #endif
717 
718  // check against the three edges to see if the pixel is inside the triangle
719  for ( k = 0 ; k < 3 ; k++ ) {
720  float v;
721 
722  v = i * edge[k][1] + j * edge[k][0] + edge[k][2];
723  dists[k] = v;
724  }
725 
726  // the edge polarities might be either way
727  if ( ! ( ( dists[0] >= -edgeOverlap && dists[1] >= -edgeOverlap && dists[2] >= -edgeOverlap )
728  || ( dists[0] <= edgeOverlap && dists[1] <= edgeOverlap && dists[2] <= edgeOverlap ) ) ) {
729  continue;
730  }
731 
732  bool edgeTexel;
733 
734  if ( ( dists[0] >= 0 && dists[1] >= 0 && dists[2] >= 0 )
735  || ( dists[0] <= 0 && dists[1] <= 0 && dists[2] <= 0 ) ) {
736  edgeTexel = false;
737  } else {
738  edgeTexel = true;
739 #ifdef SKIP_MIRRORS
740  // if this texel has already been filled by another edge pixel, don't overwrite it
741  if ( *edgeDistance == 1 ) {
742  continue;
743  }
744 #endif
745  }
746 
747  // calculate the barycentric coordinates in the triangle for this sample
748  testVert[0] = j;
749  testVert[1] = i;
750 
751  baseArea = TriTextureArea( verts[0], verts[1], verts[2] );
752  bary[0] = TriTextureArea( testVert, verts[1], verts[2] ) / baseArea;
753  bary[1] = TriTextureArea( verts[0], testVert, verts[2] ) / baseArea;
754  bary[2] = TriTextureArea( verts[0], verts[1], testVert ) / baseArea;
755 
756  totalArea = bary[0] + bary[1] + bary[2];
757  if ( totalArea < 0.99 || totalArea > 1.01 ) {
758  continue; // should never happen
759  }
760 
761  // calculate the interpolated xyz, normal, and tangents of this sample
762  point = vec3_origin;
763  traceNormal = vec3_origin;
764  normal = vec3_origin;
765  tangents[0] = vec3_origin;
766  tangents[1] = vec3_origin;
767  for ( k = 0 ; k < 3 ; k++ ) {
768  int index;
769 
770  index = lowMesh->indexes[lowFaceNum*3+k];
771  point += bary[k] * lowMesh->verts[ index ].xyz;
772 
773  // traceNormal will differ from normal if the surface uses unsmoothedTangents
774  traceNormal += bary[k] * lowMeshNormals[ index ];
775 
776  normal += bary[k] * lowMesh->verts[ index ].normal;
777  tangents[0] += bary[k] * lowMesh->verts[ index ].tangents[0];
778  tangents[1] += bary[k] * lowMesh->verts[ index ].tangents[1];
779  }
780 
781 #if 0
782  // this doesn't seem to make much difference
783  // an argument can be made that these should not be normalized, because the interpolation
784  // of the light position at rasterization time will be linear, not spherical
785  normal.Normalize();
786  tangents[0].Normalize();
787  tangents[1].Normalize();
788 #endif
789 
790  // find the best triangle in the high poly model for this
791  // sampledNormal will normalized
792  if ( !SampleHighMesh( rb, point, traceNormal, sampledNormal, sampledColor ) ) {
793 #if 0
794  // put bright red where all traces missed for debugging.
795  // for production use, it is better to leave it blank so
796  // the outlining fills it in
797  globalDest[0] = 255;
798  globalDest[1] = 0;
799  globalDest[2] = 0;
800  globalDest[3] = 255;
801 
802  localDest[0] = 255;
803  localDest[1] = 0;
804  localDest[2] = 0;
805  localDest[3] = 255;
806 #endif
807  continue;
808  }
809 
810 
811  // mark whether this is an interior or edge texel
812  *edgeDistance = ( edgeTexel ? 1.0 : 0 );
813 
814  // fill the object space normal map spot
815  r = 128 + 127 * sampledNormal[0];
816  g = 128 + 127 * sampledNormal[1];
817  b = 128 + 127 * sampledNormal[2];
818 
819  globalDest[0] = r;
820  globalDest[1] = g;
821  globalDest[2] = b;
822  globalDest[3] = 255;
823 
824  // transform to local tangent space
825  idMat3 mat;
826  mat[0] = tangents[0];
827  mat[1] = tangents[1];
828  mat[2] = normal;
829  mat.InverseSelf();
830  localNormal = mat * sampledNormal;
831 
832  localNormal.Normalize();
833 
834 
835  r = 128 + 127 * localNormal[0];
836  g = 128 + 127 * localNormal[1];
837  b = 128 + 127 * localNormal[2];
838 
839  localDest[0] = r;
840  localDest[1] = g;
841  localDest[2] = b;
842  localDest[3] = 255;
843 
844  colorDest[0] = sampledColor[0];
845  colorDest[1] = sampledColor[1];
846  colorDest[2] = sampledColor[2];
847  colorDest[3] = sampledColor[3];
848  }
849  }
850 }
851 
852 /*
853 ================
854 CombineModelSurfaces
855 
856 Frees the model and returns a new model with all triangles combined
857 into one surface
858 ================
859 */
860 static idRenderModel *CombineModelSurfaces( idRenderModel *model ) {
861  int totalVerts;
862  int totalIndexes;
863  int numIndexes;
864  int numVerts;
865  int i, j;
866 
867  totalVerts = 0;
868  totalIndexes = 0;
869 
870  for ( i = 0 ; i < model->NumSurfaces() ; i++ ) {
871  const modelSurface_t *surf = model->Surface(i);
872 
873  totalVerts += surf->geometry->numVerts;
874  totalIndexes += surf->geometry->numIndexes;
875  }
876 
878  R_AllocStaticTriSurfVerts( newTri, totalVerts );
879  R_AllocStaticTriSurfIndexes( newTri, totalIndexes );
880 
881  newTri->numVerts = totalVerts;
882  newTri->numIndexes = totalIndexes;
883 
884  newTri->bounds.Clear();
885 
886  idDrawVert *verts = newTri->verts;
887  glIndex_t *indexes = newTri->indexes;
888  numIndexes = 0;
889  numVerts = 0;
890  for ( i = 0 ; i < model->NumSurfaces() ; i++ ) {
891  const modelSurface_t *surf = model->Surface(i);
892  const srfTriangles_t *tri = surf->geometry;
893 
894  memcpy( verts + numVerts, tri->verts, tri->numVerts * sizeof( tri->verts[0] ) );
895  for ( j = 0 ; j < tri->numIndexes ; j++ ) {
896  indexes[numIndexes+j] = numVerts + tri->indexes[j];
897  }
898  newTri->bounds.AddBounds( tri->bounds );
899  numIndexes += tri->numIndexes;
900  numVerts += tri->numVerts;
901  }
902 
903  modelSurface_t surf;
904 
905  surf.id = 0;
906  surf.geometry = newTri;
907  surf.shader = tr.defaultMaterial;
908 
910  newModel->AddSurface( surf );
911 
912  renderModelManager->FreeModel( model );
913 
914  return newModel;
915 }
916 
917 /*
918 ==============
919 RenderBumpTriangles
920 
921 ==============
922 */
923 static void RenderBumpTriangles( srfTriangles_t *lowMesh, renderBump_t *rb ) {
924  int i, j;
925 
926  RB_SetGL2D();
927 
928  qglDisable( GL_CULL_FACE );
929 
930  qglColor3f( 1, 1, 1 );
931 
932  qglMatrixMode( GL_PROJECTION );
933  qglLoadIdentity();
934  qglOrtho( 0, 1, 1, 0, -1, 1 );
935  qglDisable( GL_BLEND );
936  qglMatrixMode( GL_MODELVIEW );
937  qglLoadIdentity();
938 
939  qglDisable( GL_DEPTH_TEST );
940 
941  qglClearColor(1,0,0,1);
942  qglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
943 
944  qglColor3f( 1, 1, 1 );
945 
946  // create smoothed normals for the surface, which might be
947  // different than the normals at the vertexes if the
948  // surface uses unsmoothedNormals, which only takes the
949  // normal from a single triangle. We need properly smoothed
950  // normals to make sure that the traces always go off normal
951  // to the true surface.
952  idVec3 *lowMeshNormals = (idVec3 *)Mem_ClearedAlloc( lowMesh->numVerts * sizeof( *lowMeshNormals ) );
953  R_DeriveFacePlanes( lowMesh );
954  R_CreateSilIndexes( lowMesh ); // recreate, merging the mirrored verts back together
955  const idPlane *planes = lowMesh->facePlanes;
956  for ( i = 0 ; i < lowMesh->numIndexes ; i += 3, planes++ ) {
957  for ( j = 0 ; j < 3 ; j++ ) {
958  int index;
959 
960  index = lowMesh->silIndexes[i+j];
961  lowMeshNormals[index] += (*planes).Normal();
962  }
963  }
964  // normalize and replicate from silIndexes to all indexes
965  for ( i = 0 ; i < lowMesh->numIndexes ; i++ ) {
966  lowMeshNormals[lowMesh->indexes[i]] = lowMeshNormals[lowMesh->silIndexes[i]];
967  lowMeshNormals[lowMesh->indexes[i]].Normalize();
968  }
969 
970 
971  // rasterize each low poly face
972  for ( j = 0 ; j < lowMesh->numIndexes ; j+=3 ) {
973  // pump the event loop so the window can be dragged around
975 
976  RasterizeTriangle( lowMesh, lowMeshNormals, j/3, rb );
977 
978  qglClearColor(1,0,0,1);
979  qglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
980  qglRasterPos2f( 0, 1 );
981  qglPixelZoom( glConfig.vidWidth / (float)rb->width, glConfig.vidHeight / (float)rb->height );
982  qglDrawPixels( rb->width, rb->height, GL_RGBA, GL_UNSIGNED_BYTE, rb->localPic );
983  qglPixelZoom( 1, 1 );
984  qglFlush();
986  }
987 
988  Mem_Free( lowMeshNormals );
989 }
990 
991 
992 /*
993 ==============
994 WriteRenderBump
995 
996 ==============
997 */
998 static void WriteRenderBump( renderBump_t *rb, int outLinePixels ) {
999  int width, height;
1000  int i;
1001  idStr filename;
1002 
1004 
1005  FreeTriHash( rb->hash );
1006 
1007  width = rb->width;
1008  height = rb->height;
1009 
1010 #if 0
1011  // save the non-outlined version
1012  filename = source;
1013  filename.setFileExtension();
1014  filename.append( "_nooutline.tga" );
1015  common->Printf( "writing %s\n", filename.c_str() );
1016  WriteTGA( filename, globalPic, width, height );
1017 #endif
1018 
1019  // outline the image several times to help bilinear filtering across disconnected
1020  // edges, and mip-mapping
1021  for ( i = 0 ; i < outLinePixels ; i++ ) {
1022  OutlineNormalMap( rb->localPic, width, height, 128, 128, 128 );
1023  OutlineNormalMap( rb->globalPic, width, height, 128, 128, 128 );
1024  OutlineColorMap( rb->colorPic, width, height, 128, 128, 128 );
1025  }
1026 
1027  // filter down if we are anti-aliasing
1028  for ( i = 0 ; i < rb->antiAlias ; i++ ) {
1029  byte *old;
1030 
1031  old = rb->localPic;
1032  rb->localPic = R_MipMap( rb->localPic, width, height, false );
1033  Mem_Free( old );
1034 
1035  old = rb->globalPic;
1036  rb->globalPic = R_MipMap( rb->globalPic, width, height, false );
1037  Mem_Free( old );
1038 
1039  old = rb->colorPic;
1040  rb->colorPic = R_MipMap( rb->colorPic, width, height, false );
1041  Mem_Free( old );
1042 
1043  width >>= 1;
1044  height >>= 1;
1045  }
1046 
1047  // write out the local map
1048  filename = rb->outputName;
1049  filename.SetFileExtension( ".tga" );
1050  common->Printf( "writing %s (%i,%i)\n", filename.c_str(), width, height );
1051  R_WriteTGA( filename, rb->localPic, width, height );
1052 
1053  if ( rb->saveGlobalMap ) {
1054  filename = rb->outputName;
1055  filename.StripFileExtension();
1056  filename.Append( "_global.tga" );
1057  common->Printf( "writing %s (%i,%i)\n", filename.c_str(), width, height );
1058  R_WriteTGA( filename, rb->globalPic, width, height );
1059  }
1060 
1061  if ( rb->saveColorMap ) {
1062  filename = rb->outputName;
1063  filename.StripFileExtension();
1064  filename.Append( "_color.tga" );
1065  common->Printf( "writing %s (%i,%i)\n", filename.c_str(), width, height );
1066  R_WriteTGA( filename, rb->colorPic, width, height );
1067  }
1068 
1069  Mem_Free( rb->localPic );
1070  Mem_Free( rb->globalPic );
1071  Mem_Free( rb->colorPic );
1072  Mem_Free( rb->edgeDistances );
1073 }
1074 
1075 /*
1076 ===============
1077 InitRenderBump
1078 ===============
1079 */
1080 static void InitRenderBump( renderBump_t *rb ) {
1081  srfTriangles_t *mesh;
1082  idBounds bounds;
1083  int i, c;
1084 
1085  // load the ase file
1086  common->Printf( "loading %s...\n", rb->highName );
1087 
1090  if ( !rb->highModel ) {
1091  common->Error( "failed to load %s", rb->highName );
1092  }
1093 
1094  // combine the high poly model into a single polyset
1095  if ( rb->highModel->NumSurfaces() != 1 ) {
1096  rb->highModel = CombineModelSurfaces( rb->highModel );
1097  }
1098 
1099  const modelSurface_t *surf = rb->highModel->Surface( 0 );
1100  mesh = surf->geometry;
1101 
1102  rb->mesh = mesh;
1103 
1104  R_DeriveFacePlanes( mesh );
1105 
1106  // create a face hash table to accelerate the tracing
1107  rb->hash = CreateTriHash( mesh );
1108 
1109  // bound the entire file
1110  R_BoundTriSurf( mesh );
1111  bounds = mesh->bounds;
1112 
1113  // the traceDist will be the traceFrac times the larges bounds axis
1114  rb->traceDist = 0;
1115  for ( i = 0 ; i < 3 ; i++ ) {
1116  float d;
1117 
1118  d = rb->traceFrac * ( bounds[1][i] - bounds[0][i] );
1119  if ( d > rb->traceDist ) {
1120  rb->traceDist = d;
1121  }
1122  }
1123  common->Printf( "trace fraction %4.2f = %6.2f model units\n", rb->traceFrac, rb->traceDist );
1124 
1125  c = rb->width * rb->height * 4;
1126 
1127  // local normal map
1128  rb->localPic = (byte *)Mem_Alloc( c );
1129 
1130  // global (object space, not surface space) normal map
1131  rb->globalPic = (byte *)Mem_Alloc( c );
1132 
1133  // color pic for artist reference
1134  rb->colorPic = (byte *)Mem_Alloc( c );
1135 
1136  // edgeDistance for marking outside-the-triangle traces
1137  rb->edgeDistances = (float *)Mem_Alloc( c );
1138 
1139  for ( i = 0 ; i < c ; i+=4 ) {
1140  rb->localPic[i+0] = 128;
1141  rb->localPic[i+1] = 128;
1142  rb->localPic[i+2] = 128;
1143  rb->localPic[i+3] = 0; // the artists use this for masking traced pixels sometimes
1144 
1145  rb->globalPic[i+0] = 128;
1146  rb->globalPic[i+1] = 128;
1147  rb->globalPic[i+2] = 128;
1148  rb->globalPic[i+3] = 0;
1149 
1150  rb->colorPic[i+0] = 128;
1151  rb->colorPic[i+1] = 128;
1152  rb->colorPic[i+2] = 128;
1153  rb->colorPic[i+3] = 0;
1154 
1155  rb->edgeDistances[i/4] = -1; // not traced yet
1156  }
1157 
1158 }
1159 
1160 /*
1161 ==============
1162 RenderBump_f
1163 
1164 ==============
1165 */
1166 void RenderBump_f( const idCmdArgs &args ) {
1167  idRenderModel *lowPoly;
1168  idStr source;
1169  int i, j;
1170  const char *cmdLine;
1171  int numRenderBumps;
1172  renderBump_t *renderBumps, *rb;
1173  renderBump_t opt;
1174  int startTime, endTime;
1175 
1176  // update the screen as we print
1177  common->SetRefreshOnPrint( true );
1178 
1179  // there should be a single parameter, the filename for a game loadable low-poly model
1180  if ( args.Argc() != 2 ) {
1181  common->Error( "Usage: renderbump <lowPolyModel>" );
1182  }
1183 
1184  common->Printf( "----- Renderbump %s -----\n", args.Argv( 1 ) );
1185 
1186  startTime = Sys_Milliseconds();
1187 
1188  // get the lowPoly model
1189  source = args.Argv( 1 );
1190  lowPoly = renderModelManager->CheckModel( source );
1191  if ( !lowPoly ) {
1192  common->Error( "Can't load model %s", source.c_str() );
1193  }
1194 
1195  renderBumps = (renderBump_t *)R_StaticAlloc( lowPoly->NumSurfaces() * sizeof( *renderBumps ) );
1196  numRenderBumps = 0;
1197  for ( i = 0 ; i < lowPoly->NumSurfaces() ; i++ ) {
1198  const modelSurface_t *ms = lowPoly->Surface( i );
1199 
1200  // default options
1201  memset( &opt, 0, sizeof( opt ) );
1202  opt.width = 512;
1203  opt.height = 512;
1204  opt.antiAlias = 1;
1205  opt.outline = 8;
1206  opt.traceFrac = 0.05f;
1207 
1208  // parse the renderbump parameters for this surface
1209  cmdLine = ms->shader->GetRenderBump();
1210 
1211  common->Printf( "surface %i, shader %s\nrenderBump = %s ", i,
1212  ms->shader->GetName(), cmdLine );
1213 
1214  if ( !ms->geometry ) {
1215  common->Printf( "(no geometry)\n" );
1216  continue;
1217  }
1218 
1219  idCmdArgs localArgs;
1220  localArgs.TokenizeString( cmdLine, false );
1221 
1222  if ( localArgs.Argc() < 2 ) {
1223  common->Printf( "(no action)\n" );
1224  continue;
1225  }
1226 
1227  common->Printf( "(rendering)\n" );
1228 
1229  for ( j = 0 ; j < localArgs.Argc() - 2; j++ ) {
1230  const char *s;
1231 
1232  s = localArgs.Argv( j );
1233  if ( s[0] == '-' ) {
1234  j++;
1235  s = localArgs.Argv( j );
1236  if ( s[0] == '\0' ) {
1237  continue;
1238  }
1239  }
1240 
1241  if ( !idStr::Icmp( s, "size" ) ) {
1242  if ( j + 2 >= localArgs.Argc() ) {
1243  j = localArgs.Argc();
1244  break;
1245  }
1246  opt.width = atoi( localArgs.Argv( j + 1 ) );
1247  opt.height = atoi( localArgs.Argv( j + 2 ) );
1248  j += 2;
1249  } else if ( !idStr::Icmp( s, "trace" ) ) {
1250  opt.traceFrac = atof( localArgs.Argv( j + 1 ) );
1251  j += 1;
1252  } else if ( !idStr::Icmp( s, "globalMap" ) ) {
1253  opt.saveGlobalMap = true;
1254  } else if ( !idStr::Icmp( s, "colorMap" ) ) {
1255  opt.saveColorMap = true;
1256  } else if ( !idStr::Icmp( s, "outline" ) ) {
1257  opt.outline = atoi( localArgs.Argv( j + 1 ) );
1258  j += 1;
1259  } else if ( !idStr::Icmp( s, "aa" ) ) {
1260  opt.antiAlias = atoi( localArgs.Argv( j + 1 ) );
1261  j += 1;
1262  } else {
1263  common->Printf( "WARNING: Unknown option \"%s\"\n", s );
1264  break;
1265  }
1266  }
1267 
1268  if ( j != ( localArgs.Argc() - 2 ) ) {
1269  common->Error( "usage: renderBump [-size width height] [-aa <1-2>] [globalMap] [colorMap] [-trace <0.01 - 1.0>] normalMapImageFile highPolyAseFile" );
1270  }
1271  idStr::Copynz( opt.outputName, localArgs.Argv( j ), sizeof( opt.outputName ) );
1272  idStr::Copynz( opt.highName, localArgs.Argv( j + 1 ), sizeof( opt.highName ) );
1273 
1274  // adjust size for anti-aliasing
1275  opt.width <<= opt.antiAlias;
1276  opt.height <<= opt.antiAlias;
1277 
1278  // see if we already have a renderbump going for another surface that this should use
1279  for ( j = 0 ; j < numRenderBumps ; j++ ) {
1280  rb = &renderBumps[j];
1281 
1282  if ( idStr::Icmp( rb->outputName, opt.outputName ) ) {
1283  continue;
1284  }
1285  // all the other parameters must match, or it is an error
1286  if ( idStr::Icmp( rb->highName, opt.highName) || rb->width != opt.width ||
1287  rb->height != opt.height || rb->antiAlias != opt.antiAlias ||
1288  rb->traceFrac != opt.traceFrac ) {
1289  common->Error( "mismatched renderbump parameters on image %s", rb->outputName );
1290  continue;
1291  }
1292 
1293  // saveGlobalMap will be a sticky option
1294  rb->saveGlobalMap = rb->saveGlobalMap | opt.saveGlobalMap;
1295  break;
1296  }
1297 
1298  // create a new renderbump if needed
1299  if ( j == numRenderBumps ) {
1300  numRenderBumps++;
1301  rb = &renderBumps[j];
1302  *rb = opt;
1303 
1304  InitRenderBump( rb );
1305  }
1306 
1307  // render the triangles for this surface
1308  RenderBumpTriangles( ms->geometry, rb );
1309  }
1310 
1311  //
1312  // anti-alias and write out all renderbumps that we have completed
1313  //
1314  for ( i = 0 ; i < numRenderBumps ; i++ ) {
1315  WriteRenderBump( &renderBumps[i], opt.outline << opt.antiAlias );
1316  }
1317 
1318  R_StaticFree( renderBumps );
1319 
1320  endTime = Sys_Milliseconds();
1321  common->Printf( "%5.2f seconds for renderBump\n", ( endTime - startTime ) / 1000.0 );
1322  common->Printf( "---------- RenderBump Completed ----------\n" );
1323 
1324  // stop updating the screen as we print
1325  common->SetRefreshOnPrint( false );
1326 }
1327 
1328 
1329 
1330 /*
1331 ==================================================================================
1332 
1333 FLAT
1334 
1335 The flat case is trivial, and accomplished with hardware rendering
1336 
1337 ==================================================================================
1338 */
1339 
1340 
1341 /*
1342 ==============
1343 RenderBumpFlat_f
1344 
1345 ==============
1346 */
1347 void RenderBumpFlat_f( const idCmdArgs &args ) {
1348  int width, height;
1349  idStr source;
1350  int i;
1351  idBounds bounds;
1352  srfTriangles_t *mesh;
1353  float boundsScale;
1354 
1355  // update the screen as we print
1356  common->SetRefreshOnPrint( true );
1357 
1358  width = height = 256;
1359  boundsScale = 0;
1360 
1361  // check options
1362  for ( i = 1 ; i < args.Argc() - 1; i++ ) {
1363  const char *s;
1364 
1365  s = args.Argv( i );
1366  if ( s[0] == '-' ) {
1367  i++;
1368  s = args.Argv( i );
1369  }
1370 
1371  if ( !idStr::Icmp( s, "size" ) ) {
1372  if ( i + 2 >= args.Argc() ) {
1373  i = args.Argc();
1374  break;
1375  }
1376  width = atoi( args.Argv( i + 1 ) );
1377  height = atoi( args.Argv( i + 2 ) );
1378  i += 2;
1379  } else {
1380  common->Printf( "WARNING: Unknown option \"%s\"\n", s );
1381  break;
1382  }
1383  }
1384 
1385  if ( i != ( args.Argc() - 1 ) ) {
1386  common->Error( "usage: renderBumpFlat [-size width height] asefile" );
1387  }
1388 
1389  common->Printf( "Final image size: %i, %i\n", width, height );
1390 
1391  // load the source in "fastload" mode, because we don't
1392  // need tangent and shadow information
1393  source = args.Argv( i );
1394 
1395  idRenderModel *highPolyModel = renderModelManager->AllocModel();
1396 
1397  highPolyModel->PartialInitFromFile( source );
1398 
1399  if ( highPolyModel->IsDefaultModel() ) {
1400  common->Error( "failed to load %s", source.c_str() );
1401  }
1402 
1403  // combine the high poly model into a single polyset
1404  if ( highPolyModel->NumSurfaces() != 1 ) {
1405  highPolyModel = CombineModelSurfaces( highPolyModel );
1406  }
1407 
1408  // create normals if not present in file
1409  const modelSurface_t *surf = highPolyModel->Surface( 0 );
1410  mesh = surf->geometry;
1411 
1412  // bound the entire file
1413  R_BoundTriSurf( mesh );
1414  bounds = mesh->bounds;
1415 
1416  SaveWindow();
1417  ResizeWindow( width, height );
1418 
1419  // for small images, the viewport may be less than the minimum window
1420  qglViewport( 0, 0, width, height );
1421 
1422  qglEnable( GL_CULL_FACE );
1423  qglCullFace( GL_FRONT );
1424  qglDisable( GL_STENCIL_TEST );
1425  qglDisable( GL_SCISSOR_TEST );
1426  qglDisable( GL_ALPHA_TEST );
1427  qglDisable( GL_BLEND );
1428  qglEnable( GL_DEPTH_TEST );
1429  qglDisable( GL_TEXTURE_2D );
1430  qglDepthMask( GL_TRUE );
1431  qglDepthFunc( GL_LEQUAL );
1432 
1433  qglColor3f( 1, 1, 1 );
1434 
1435  qglMatrixMode( GL_PROJECTION );
1436  qglLoadIdentity();
1437  qglOrtho( bounds[0][0], bounds[1][0], bounds[0][2],
1438  bounds[1][2], -( bounds[0][1] - 1 ), -( bounds[1][1] + 1 ) );
1439 
1440  qglMatrixMode( GL_MODELVIEW );
1441  qglLoadIdentity();
1442 
1443  // flat maps are automatically anti-aliased
1444 
1445  idStr filename;
1446  int j, k, c;
1447  byte *buffer;
1448  int *sumBuffer, *colorSumBuffer;
1449  bool flat;
1450  int sample;
1451 
1452  sumBuffer = (int *)Mem_Alloc( width * height * 4 * 4 );
1453  memset( sumBuffer, 0, width * height * 4 * 4 );
1454  buffer = (byte *)Mem_Alloc( width * height * 4 );
1455 
1456  colorSumBuffer = (int *)Mem_Alloc( width * height * 4 * 4 );
1457  memset( sumBuffer, 0, width * height * 4 * 4 );
1458 
1459  flat = false;
1460 //flat = true;
1461 
1462  for ( sample = 0 ; sample < 16 ; sample++ ) {
1463  float xOff, yOff;
1464 
1465  xOff = ( ( sample & 3 ) / 4.0 ) * ( bounds[1][0] - bounds[0][0] ) / width;
1466  yOff = ( ( sample / 4 ) / 4.0 ) * ( bounds[1][2] - bounds[0][2] ) / height;
1467 
1468  for ( int colorPass = 0 ; colorPass < 2 ; colorPass++ ) {
1469  qglClearColor(0.5,0.5,0.5,0);
1470  qglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
1471 
1472  qglBegin( GL_TRIANGLES );
1473  for ( i = 0 ; i < highPolyModel->NumSurfaces() ; i++ ) {
1474  const modelSurface_t *surf = highPolyModel->Surface( i );
1475 
1476  mesh = surf->geometry;
1477 
1478  if ( colorPass ) {
1479  // just render the surface color for artist visualization
1480  for ( j = 0 ; j < mesh->numIndexes ; j+=3 ) {
1481  for ( k = 0 ; k < 3 ; k++ ) {
1482  int v;
1483  float *a;
1484 
1485  v = mesh->indexes[j+k];
1486  qglColor3ubv( mesh->verts[v].color );
1487  a = mesh->verts[v].xyz.ToFloatPtr();
1488  qglVertex3f( a[0] + xOff, a[2] + yOff, a[1] );
1489  }
1490  }
1491  } else {
1492  // render as normal map
1493  // we can either flat shade from the plane,
1494  // or smooth shade from the vertex normals
1495  for ( j = 0 ; j < mesh->numIndexes ; j+=3 ) {
1496  if ( flat ) {
1497  idPlane plane;
1498  idVec3 *a, *b, *c;
1499  int v1, v2, v3;
1500 
1501  v1 = mesh->indexes[j+0];
1502  v2 = mesh->indexes[j+1];
1503  v3 = mesh->indexes[j+2];
1504 
1505  a = &mesh->verts[ v1 ].xyz;
1506  b = &mesh->verts[ v2 ].xyz;
1507  c = &mesh->verts[ v3 ].xyz;
1508 
1509  plane.FromPoints( *a, *b, *c );
1510 
1511  // NULLNORMAL is used by the artists to force an area to reflect no
1512  // light at all
1513  if ( surf->shader->GetSurfaceFlags() & SURF_NULLNORMAL ) {
1514  qglColor3f( 0.5, 0.5, 0.5 );
1515  } else {
1516  qglColor3f( 0.5 + 0.5*plane[0], 0.5 - 0.5*plane[2], 0.5 - 0.5*plane[1] );
1517  }
1518 
1519  qglVertex3f( (*a)[0] + xOff, (*a)[2] + yOff, (*a)[1] );
1520  qglVertex3f( (*b)[0] + xOff, (*b)[2] + yOff, (*b)[1] );
1521  qglVertex3f( (*c)[0] + xOff, (*c)[2] + yOff, (*c)[1] );
1522  } else {
1523  for ( k = 0 ; k < 3 ; k++ ) {
1524  int v;
1525  float *n;
1526  float *a;
1527 
1528  v = mesh->indexes[j+k];
1529  n = mesh->verts[v].normal.ToFloatPtr();
1530 
1531  // NULLNORMAL is used by the artists to force an area to reflect no
1532  // light at all
1533  if ( surf->shader->GetSurfaceFlags() & SURF_NULLNORMAL ) {
1534  qglColor3f( 0.5, 0.5, 0.5 );
1535  } else {
1536  // we are going to flip the normal Z direction
1537  qglColor3f( 0.5 + 0.5*n[0], 0.5 - 0.5*n[2], 0.5 - 0.5*n[1] );
1538  }
1539 
1540  a = mesh->verts[v].xyz.ToFloatPtr();
1541  qglVertex3f( a[0] + xOff, a[2] + yOff, a[1] );
1542  }
1543  }
1544  }
1545  }
1546  }
1547 
1548  qglEnd();
1549  qglFlush();
1551  qglReadPixels( 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer );
1552 
1553  if ( colorPass ) {
1554  // add to the sum buffer
1555  for ( i = 0 ; i < c ; i++ ) {
1556  colorSumBuffer[i*4+0] += buffer[i*4+0];
1557  colorSumBuffer[i*4+1] += buffer[i*4+1];
1558  colorSumBuffer[i*4+2] += buffer[i*4+2];
1559  colorSumBuffer[i*4+3] += buffer[i*4+3];
1560  }
1561  } else {
1562  // normalize
1563  c = width * height;
1564  for ( i = 0 ; i < c ; i++ ) {
1565  idVec3 v;
1566 
1567  v[0] = ( buffer[i*4+0] - 128 ) / 127.0;
1568  v[1] = ( buffer[i*4+1] - 128 ) / 127.0;
1569  v[2] = ( buffer[i*4+2] - 128 ) / 127.0;
1570 
1571  v.Normalize();
1572 
1573  buffer[i*4+0] = 128 + 127 * v[0];
1574  buffer[i*4+1] = 128 + 127 * v[1];
1575  buffer[i*4+2] = 128 + 127 * v[2];
1576  }
1577 
1578  // outline into non-drawn areas
1579  for ( i = 0 ; i < 8 ; i++ ) {
1580  OutlineNormalMap( buffer, width, height, 128, 128, 128 );
1581  }
1582 
1583  // add to the sum buffer
1584  for ( i = 0 ; i < c ; i++ ) {
1585  sumBuffer[i*4+0] += buffer[i*4+0];
1586  sumBuffer[i*4+1] += buffer[i*4+1];
1587  sumBuffer[i*4+2] += buffer[i*4+2];
1588  sumBuffer[i*4+3] += buffer[i*4+3];
1589  }
1590  }
1591  }
1592  }
1593 
1594  c = width * height;
1595 
1596  // save out the color map
1597  for ( i = 0 ; i < c ; i++ ) {
1598  buffer[i*4+0] = colorSumBuffer[i*4+0] / 16;
1599  buffer[i*4+1] = colorSumBuffer[i*4+1] / 16;
1600  buffer[i*4+2] = colorSumBuffer[i*4+2] / 16;
1601  buffer[i*4+3] = colorSumBuffer[i*4+3] / 16;
1602  }
1603  filename = source;
1604  filename.StripFileExtension();
1605  filename.Append( "_color.tga" );
1606  R_VerticalFlip( buffer, width, height );
1607  R_WriteTGA( filename, buffer, width, height );
1608 
1609  // save out the local map
1610  // scale the sum buffer back down to the sample buffer
1611  // we allow this to denormalize
1612  for ( i = 0 ; i < c ; i++ ) {
1613  buffer[i*4+0] = sumBuffer[i*4+0] / 16;
1614  buffer[i*4+1] = sumBuffer[i*4+1] / 16;
1615  buffer[i*4+2] = sumBuffer[i*4+2] / 16;
1616  buffer[i*4+3] = sumBuffer[i*4+3] / 16;
1617  }
1618 
1619  filename = source;
1620  filename.StripFileExtension();
1621  filename.Append( "_local.tga" );
1622  common->Printf( "writing %s (%i,%i)\n", filename.c_str(), width, height );
1623  R_VerticalFlip( buffer, width, height );
1624  R_WriteTGA( filename, buffer, width, height );
1625 
1626 
1627  // free the model
1628  renderModelManager->FreeModel( highPolyModel );
1629 
1630  // free our work buffer
1631  Mem_Free( buffer );
1632  Mem_Free( sumBuffer );
1633  Mem_Free( colorSumBuffer );
1634 
1635  RestoreWindow();
1636 
1637  // stop updating the screen as we print
1638  common->SetRefreshOnPrint( false );
1639 
1640  common->Error( "Completed." );
1641 }
#define MAX_LINK_BLOCKS
Definition: renderbump.cpp:77
GLubyte g
Definition: glext.h:4662
byte color[4]
Definition: MegaTexture.cpp:54
#define qglDepthMask
Definition: qgl_linked.h:90
bool AddBounds(const idBounds &a)
Definition: Bounds.h:255
float Normalize(void)
Definition: Vector.h:646
idBounds bounds
Definition: renderbump.cpp:79
idStr & SetFileExtension(const char *extension)
Definition: Str.cpp:743
void cross(float a[], float b[], float c[])
Definition: Model_lwo.cpp:3889
virtual void PartialInitFromFile(const char *fileName)=0
bool FromPoints(const idVec3 &p1, const idVec3 &p2, const idVec3 &p3, bool fixDegenerate=true)
Definition: Plane.h:279
const idVec3 & Normal(void) const
Definition: Plane.h:239
#define qglDisable
Definition: qgl_linked.h:92
glIndex_t * silIndexes
Definition: Model.h:104
int numVerts
Definition: Model.h:98
triHash_t * hash
Definition: renderbump.cpp:102
#define MAX_LINKS_PER_BLOCK
Definition: renderbump.cpp:76
const GLdouble * v
Definition: glext.h:2936
HGLRC hGLRC
Definition: win_local.h:122
#define qglClearColor
Definition: qgl_linked.h:41
#define qglCullFace
Definition: qgl_linked.h:86
const float * ToFloatPtr(void) const
Definition: Vector.h:719
#define VectorSubtract(a, b, c)
Definition: Vector.h:1995
float Distance(const idVec3 &v) const
Definition: Plane.h:324
byte * localPic
Definition: renderbump.cpp:89
idVec3 xyz
Definition: DrawVert.h:42
#define MAX_QPATH
Definition: renderbump.cpp:59
idRenderModel * highModel
Definition: renderbump.cpp:101
GLenum GLsizei n
Definition: glext.h:3705
int Sys_Milliseconds(void)
idVec3 tangents[2]
Definition: DrawVert.h:45
byte * colorPic
Definition: renderbump.cpp:91
const idMaterial * shader
Definition: Model.h:146
bool InverseSelf(void)
Definition: Matrix.cpp:333
#define qglBegin
Definition: qgl_linked.h:33
virtual void AddSurface(modelSurface_t surface)=0
byte * R_MipMap(const byte *in, int width, int height, bool preserveBorder)
const char * GetName(void) const
Definition: DeclManager.h:140
Definition: Vector.h:316
idPlane * facePlanes
Definition: Model.h:115
void RenderBump_f(const idCmdArgs &args)
#define qglReadPixels
Definition: qgl_linked.h:266
void Clear(void)
Definition: Bounds.h:201
bool isFullscreen
Definition: RenderSystem.h:90
int numLinkBlocks
Definition: renderbump.cpp:81
srfTriangles_t * R_AllocStaticTriSurf(void)
Definition: tr_trisurf.cpp:523
GLdouble s
Definition: glext.h:2935
#define qglViewport
Definition: qgl_linked.h:364
GLenum GLsizei len
Definition: glext.h:3472
void Sys_GenerateEvents(void)
Definition: posix_main.cpp:980
idVec3 Cross(const idVec3 &a) const
Definition: Vector.h:619
#define qglColor3ubv
Definition: qgl_linked.h:57
#define qglPixelZoom
Definition: qgl_linked.h:227
int i
Definition: process.py:33
#define qglEnable
Definition: qgl_linked.h:101
int Icmp(const char *text) const
Definition: Str.h:667
float traceFrac
Definition: renderbump.cpp:98
list l
Definition: prepare.py:17
#define HASH_AXIS_BINS
Definition: renderbump.cpp:64
GLsizei GLsizei GLcharARB * source
Definition: glext.h:3633
bool AddPoint(const idVec3 &v)
Definition: Bounds.h:226
GLfloat GLfloat GLfloat v2
Definition: glext.h:3608
idVec2 st
Definition: DrawVert.h:43
GLuint GLuint GLsizei count
Definition: glext.h:2845
float binSize[3]
Definition: renderbump.cpp:80
const char * GetRenderBump() const
Definition: Material.h:485
GLuint index
Definition: glext.h:3476
const GLubyte * c
Definition: glext.h:4677
float Length(void) const
Definition: Vector.h:631
idStr & StripFileExtension(void)
Definition: Str.cpp:757
#define DIST_NO_INTERSECTION
Definition: renderbump.cpp:406
idVec3 vec3_origin(0.0f, 0.0f, 0.0f)
char outputName[MAX_QPATH]
Definition: renderbump.cpp:87
idBounds bounds
Definition: Model.h:87
idCommon * common
Definition: Common.cpp:206
static float TriangleArea(const idVec3 &a, const idVec3 &b, const idVec3 &c)
Definition: Winding.cpp:1446
void RenderBumpFlat_f(const idCmdArgs &args)
srfTriangles_t * geometry
Definition: Model.h:147
void R_DeriveFacePlanes(srfTriangles_t *tri)
Definition: tr_trisurf.cpp:827
int glIndex_t
Definition: Model.h:52
GLsizei GLsizei GLenum GLenum const GLvoid * data
Definition: glext.h:2853
float * edgeDistances
Definition: renderbump.cpp:92
GLuint buffer
Definition: glext.h:3108
#define qglOrtho
Definition: qgl_linked.h:218
#define qglEnd
Definition: qgl_linked.h:103
virtual const modelSurface_t * Surface(int surfaceNum) const =0
#define qglColor3f
Definition: qgl_linked.h:50
virtual void virtual void FatalError(const char *fmt,...) id_attribute((format(printf
#define RAY_STEPS
Definition: Plane.h:71
binLink_t binLinks[HASH_AXIS_BINS][HASH_AXIS_BINS][HASH_AXIS_BINS]
Definition: renderbump.cpp:83
static void Copynz(char *dest, const char *src, int destsize)
Definition: Str.cpp:1376
const int GetSurfaceFlags(void) const
Definition: Material.h:500
void R_WriteTGA(const char *filename, const byte *data, int width, int height, bool flipVertical=false)
Definition: Image_files.cpp:85
int Argc(void) const
Definition: CmdArgs.h:48
void Mem_Free(void *ptr)
Definition: Heap.cpp:1087
idVec3 normal
Definition: DrawVert.h:44
GLenum GLsizei width
Definition: glext.h:2846
GLubyte GLubyte GLubyte a
Definition: glext.h:4662
virtual void Printf(const char *fmt,...) id_attribute((format(printf
void R_BoundTriSurf(srfTriangles_t *tri)
Definition: tr_trisurf.cpp:704
char highName[MAX_QPATH]
Definition: renderbump.cpp:88
virtual void FreeModel(idRenderModel *model)=0
GLfloat GLfloat v1
Definition: glext.h:3607
GLenum GLsizei GLsizei height
Definition: glext.h:2856
bool saveGlobalMap
Definition: renderbump.cpp:96
GLubyte GLubyte b
Definition: glext.h:4662
void TokenizeString(const char *text, bool keepAsStrings)
Definition: CmdArgs.cpp:106
#define qglFlush
Definition: qgl_linked.h:119
GLfloat GLfloat GLfloat GLfloat v3
Definition: glext.h:3609
GLdouble GLdouble GLdouble r
Definition: glext.h:2951
idRenderModelManager * renderModelManager
Definition: Matrix.h:333
virtual bool IsDefaultModel() const =0
void RB_SetGL2D(void)
Definition: tr_backend.cpp:423
glconfig_t glConfig
tuple f
Definition: idal.py:89
void R_VerticalFlip(byte *data, int width, int height)
GLuint in
Definition: glext.h:5388
void Append(const char a)
Definition: Str.h:729
unsigned char byte
Definition: Lib.h:75
void * R_StaticAlloc(int bytes)
Definition: tr_main.cpp:301
#define qglClear
Definition: qgl_linked.h:39
Definition: Str.h:116
#define qglDrawPixels
Definition: qgl_linked.h:97
triLink_t * linkBlocks[MAX_LINK_BLOCKS]
Definition: renderbump.cpp:82
byte color[4]
Definition: DrawVert.h:46
void * Mem_ClearedAlloc(const int size)
Definition: Heap.cpp:1149
const char * c_str(void) const
Definition: Str.h:487
#define FALSE
Definition: mprintf.c:70
#define DotProduct(a, b)
Definition: Vector.h:1994
glIndex_t * indexes
Definition: Model.h:102
float traceDist
Definition: renderbump.cpp:99
idRenderSystemLocal tr
#define qglVertex3f
Definition: qgl_linked.h:349
const char * Argv(int arg) const
Definition: CmdArgs.h:50
void R_AllocStaticTriSurfIndexes(srfTriangles_t *tri, int numIndexes)
Definition: tr_trisurf.cpp:565
void * Mem_Alloc(const int size)
Definition: Heap.cpp:1067
#define qglLoadIdentity
Definition: qgl_linked.h:186
byte * globalPic
Definition: renderbump.cpp:90
GLint j
Definition: qgl.h:264
virtual idRenderModel * CheckModel(const char *modelName)=0
int numIndexes
Definition: Model.h:101
virtual void SetRefreshOnPrint(bool set)=0
if(!ValidDisplayID(prefInfo.prefDisplayID)) prefInfo.prefDisplayID
#define WINDOW_STYLE
Definition: win_local.h:61
Win32Vars_t win32
Definition: win_main.cpp:65
void R_StaticFree(void *data)
Definition: tr_main.cpp:335
void R_CreateSilIndexes(srfTriangles_t *tri)
Definition: tr_trisurf.cpp:765
virtual void Error(const char *fmt,...) id_attribute((format(printf
GLfloat GLfloat p
Definition: glext.h:4674
void R_AllocStaticTriSurfVerts(srfTriangles_t *tri, int numVerts)
Definition: tr_trisurf.cpp:555
bool saveColorMap
Definition: renderbump.cpp:97
#define qglMatrixMode
Definition: qgl_linked.h:203
void GLimp_SwapBuffers(void)
Definition: dedicated.cpp:85
srfTriangles_t * mesh
Definition: renderbump.cpp:100
const idMaterial * defaultMaterial
Definition: tr_local.h:779
idDrawVert * verts
Definition: Model.h:99
#define qglDepthFunc
Definition: qgl_linked.h:89
#define qglRasterPos2f
Definition: qgl_linked.h:243
virtual idRenderModel * AllocModel()=0
virtual int NumSurfaces() const =0