tjunction.c (12926B)
1 /* 2 =========================================================================== 3 Copyright (C) 1999-2005 Id Software, Inc. 4 5 This file is part of Quake III Arena source code. 6 7 Quake III Arena source code is free software; you can redistribute it 8 and/or modify it under the terms of the GNU General Public License as 9 published by the Free Software Foundation; either version 2 of the License, 10 or (at your option) any later version. 11 12 Quake III Arena source code is distributed in the hope that it will be 13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with Foobar; if not, write to the Free Software 19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 =========================================================================== 21 */ 22 #include "qbsp.h" 23 24 typedef struct edgePoint_s { 25 float intercept; 26 vec3_t xyz; 27 struct edgePoint_s *prev, *next; 28 } edgePoint_t; 29 30 typedef struct edgeLine_s { 31 vec3_t normal1; 32 float dist1; 33 34 vec3_t normal2; 35 float dist2; 36 37 vec3_t origin; 38 vec3_t dir; 39 40 edgePoint_t chain; // unused element of doubly linked list 41 } edgeLine_t; 42 43 typedef struct { 44 float length; 45 drawVert_t *dv[2]; 46 } originalEdge_t; 47 48 #define MAX_ORIGINAL_EDGES 0x10000 49 originalEdge_t originalEdges[MAX_ORIGINAL_EDGES]; 50 int numOriginalEdges; 51 52 53 #define MAX_EDGE_LINES 0x10000 54 edgeLine_t edgeLines[MAX_EDGE_LINES]; 55 int numEdgeLines; 56 57 int c_degenerateEdges; 58 int c_addedVerts; 59 int c_totalVerts; 60 61 int c_natural, c_rotate, c_cant; 62 63 // these should be whatever epsilon we actually expect, 64 // plus SNAP_INT_TO_FLOAT 65 #define LINE_POSITION_EPSILON 0.25 66 #define POINT_ON_LINE_EPSILON 0.25 67 68 /* 69 ==================== 70 InsertPointOnEdge 71 ==================== 72 */ 73 void InsertPointOnEdge( vec3_t v, edgeLine_t *e ) { 74 vec3_t delta; 75 float d; 76 edgePoint_t *p, *scan; 77 78 VectorSubtract( v, e->origin, delta ); 79 d = DotProduct( delta, e->dir ); 80 81 p = malloc( sizeof(edgePoint_t) ); 82 p->intercept = d; 83 VectorCopy( v, p->xyz ); 84 85 if ( e->chain.next == &e->chain ) { 86 e->chain.next = e->chain.prev = p; 87 p->next = p->prev = &e->chain; 88 return; 89 } 90 91 scan = e->chain.next; 92 for ( ; scan != &e->chain ; scan = scan->next ) { 93 d = p->intercept - scan->intercept; 94 if ( d > -LINE_POSITION_EPSILON && d < LINE_POSITION_EPSILON ) { 95 free( p ); 96 return; // the point is already set 97 } 98 99 if ( p->intercept < scan->intercept ) { 100 // insert here 101 p->prev = scan->prev; 102 p->next = scan; 103 scan->prev->next = p; 104 scan->prev = p; 105 return; 106 } 107 } 108 109 // add at the end 110 p->prev = scan->prev; 111 p->next = scan; 112 scan->prev->next = p; 113 scan->prev = p; 114 } 115 116 117 /* 118 ==================== 119 AddEdge 120 ==================== 121 */ 122 int AddEdge( vec3_t v1, vec3_t v2, qboolean createNonAxial ) { 123 int i; 124 edgeLine_t *e; 125 float d; 126 vec3_t dir; 127 128 VectorSubtract( v2, v1, dir ); 129 d = VectorNormalize( dir, dir ); 130 if ( d < 0.1 ) { 131 // if we added a 0 length vector, it would make degenerate planes 132 c_degenerateEdges++; 133 return -1; 134 } 135 136 if ( !createNonAxial ) { 137 if ( fabs( dir[0] + dir[1] + dir[2] ) != 1.0 ) { 138 if ( numOriginalEdges == MAX_ORIGINAL_EDGES ) { 139 Error( "MAX_ORIGINAL_EDGES" ); 140 } 141 originalEdges[ numOriginalEdges ].dv[0] = (drawVert_t *)v1; 142 originalEdges[ numOriginalEdges ].dv[1] = (drawVert_t *)v2; 143 originalEdges[ numOriginalEdges ].length = d; 144 numOriginalEdges++; 145 return -1; 146 } 147 } 148 149 for ( i = 0 ; i < numEdgeLines ; i++ ) { 150 e = &edgeLines[i]; 151 152 d = DotProduct( v1, e->normal1 ) - e->dist1; 153 if ( d < -POINT_ON_LINE_EPSILON || d > POINT_ON_LINE_EPSILON ) { 154 continue; 155 } 156 d = DotProduct( v1, e->normal2 ) - e->dist2; 157 if ( d < -POINT_ON_LINE_EPSILON || d > POINT_ON_LINE_EPSILON ) { 158 continue; 159 } 160 161 d = DotProduct( v2, e->normal1 ) - e->dist1; 162 if ( d < -POINT_ON_LINE_EPSILON || d > POINT_ON_LINE_EPSILON ) { 163 continue; 164 } 165 d = DotProduct( v2, e->normal2 ) - e->dist2; 166 if ( d < -POINT_ON_LINE_EPSILON || d > POINT_ON_LINE_EPSILON ) { 167 continue; 168 } 169 170 // this is the edge 171 InsertPointOnEdge( v1, e ); 172 InsertPointOnEdge( v2, e ); 173 return i; 174 } 175 176 // create a new edge 177 if ( numEdgeLines >= MAX_EDGE_LINES ) { 178 Error( "MAX_EDGE_LINES" ); 179 } 180 181 e = &edgeLines[ numEdgeLines ]; 182 numEdgeLines++; 183 184 e->chain.next = e->chain.prev = &e->chain; 185 186 VectorCopy( v1, e->origin ); 187 VectorCopy( dir, e->dir ); 188 189 MakeNormalVectors( e->dir, e->normal1, e->normal2 ); 190 e->dist1 = DotProduct( e->origin, e->normal1 ); 191 e->dist2 = DotProduct( e->origin, e->normal2 ); 192 193 InsertPointOnEdge( v1, e ); 194 InsertPointOnEdge( v2, e ); 195 196 return numEdgeLines - 1; 197 } 198 199 /* 200 ==================== 201 AddSurfaceEdges 202 ==================== 203 */ 204 void AddSurfaceEdges( mapDrawSurface_t *ds ) { 205 int i; 206 207 for ( i = 0 ; i < ds->numVerts ; i++ ) { 208 // save the edge number in the lightmap field 209 // so we don't need to look it up again 210 ds->verts[i].lightmap[0] = 211 AddEdge( ds->verts[i].xyz, ds->verts[(i+1) % ds->numVerts].xyz, qfalse ); 212 } 213 } 214 215 /* 216 ================ 217 ColinearEdge 218 ================ 219 */ 220 qboolean ColinearEdge( vec3_t v1, vec3_t v2, vec3_t v3 ) { 221 vec3_t midpoint, dir, offset, on; 222 float d; 223 224 VectorSubtract( v2, v1, midpoint ); 225 VectorSubtract( v3, v1, dir ); 226 d = VectorNormalize( dir, dir ); 227 if ( d == 0 ) { 228 return qfalse; // degenerate 229 } 230 231 d = DotProduct( midpoint, dir ); 232 VectorScale( dir, d, on ); 233 VectorSubtract( midpoint, on, offset ); 234 d = VectorLength ( offset ); 235 236 if ( d < 0.1 ) { 237 return qtrue; 238 } 239 240 return qfalse; 241 } 242 243 /* 244 ==================== 245 AddPatchEdges 246 247 Add colinear border edges, which will fix some classes of patch to 248 brush tjunctions 249 ==================== 250 */ 251 void AddPatchEdges( mapDrawSurface_t *ds ) { 252 int i; 253 float *v1, *v2, *v3; 254 255 for ( i = 0 ; i < ds->patchWidth - 2; i+=2 ) { 256 v1 = ds->verts[ i ].xyz; 257 v2 = ds->verts[ i + 1 ].xyz; 258 v3 = ds->verts[ i + 2 ].xyz; 259 260 // if v2 is the midpoint of v1 to v3, add an edge from v1 to v3 261 if ( ColinearEdge( v1, v2, v3 ) ) { 262 AddEdge( v1, v3, qfalse ); 263 } 264 265 v1 = ds->verts[ ( ds->patchHeight - 1 ) * ds->patchWidth + i ].xyz; 266 v2 = ds->verts[ ( ds->patchHeight - 1 ) * ds->patchWidth + i + 1 ].xyz; 267 v3 = ds->verts[ ( ds->patchHeight - 1 ) * ds->patchWidth + i + 2 ].xyz; 268 269 // if v2 is on the v1 to v3 line, add an edge from v1 to v3 270 if ( ColinearEdge( v1, v2, v3 ) ) { 271 AddEdge( v1, v3, qfalse ); 272 } 273 } 274 275 for ( i = 0 ; i < ds->patchHeight - 2 ; i+=2 ) { 276 v1 = ds->verts[ i * ds->patchWidth ].xyz; 277 v2 = ds->verts[ ( i + 1 ) * ds->patchWidth ].xyz; 278 v3 = ds->verts[ ( i + 2 ) * ds->patchWidth ].xyz; 279 280 // if v2 is the midpoint of v1 to v3, add an edge from v1 to v3 281 if ( ColinearEdge( v1, v2, v3 ) ) { 282 AddEdge( v1, v3, qfalse ); 283 } 284 285 v1 = ds->verts[ ( ds->patchWidth - 1 ) + i * ds->patchWidth ].xyz; 286 v2 = ds->verts[ ( ds->patchWidth - 1 ) + ( i + 1 ) * ds->patchWidth ].xyz; 287 v3 = ds->verts[ ( ds->patchWidth - 1 ) + ( i + 2 ) * ds->patchWidth ].xyz; 288 289 // if v2 is the midpoint of v1 to v3, add an edge from v1 to v3 290 if ( ColinearEdge( v1, v2, v3 ) ) { 291 AddEdge( v1, v3, qfalse ); 292 } 293 } 294 295 296 } 297 298 299 /* 300 ==================== 301 FixSurfaceJunctions 302 ==================== 303 */ 304 #define MAX_SURFACE_VERTS 256 305 void FixSurfaceJunctions( mapDrawSurface_t *ds ) { 306 int i, j, k; 307 edgeLine_t *e; 308 edgePoint_t *p; 309 int originalVerts; 310 int counts[MAX_SURFACE_VERTS]; 311 int originals[MAX_SURFACE_VERTS]; 312 int firstVert[MAX_SURFACE_VERTS]; 313 drawVert_t verts[MAX_SURFACE_VERTS], *v1, *v2; 314 int numVerts; 315 float start, end, frac; 316 vec3_t delta; 317 318 originalVerts = ds->numVerts; 319 320 numVerts = 0; 321 for ( i = 0 ; i < ds->numVerts ; i++ ) { 322 counts[i] = 0; 323 firstVert[i] = numVerts; 324 325 // copy first vert 326 if ( numVerts == MAX_SURFACE_VERTS ) { 327 Error( "MAX_SURFACE_VERTS" ); 328 } 329 verts[numVerts] = ds->verts[i]; 330 originals[numVerts] = i; 331 numVerts++; 332 333 // check to see if there are any t junctions before the next vert 334 v1 = &ds->verts[i]; 335 v2 = &ds->verts[ (i+1) % ds->numVerts ]; 336 337 j = (int)ds->verts[i].lightmap[0]; 338 if ( j == -1 ) { 339 continue; // degenerate edge 340 } 341 e = &edgeLines[ j ]; 342 343 VectorSubtract( v1->xyz, e->origin, delta ); 344 start = DotProduct( delta, e->dir ); 345 346 VectorSubtract( v2->xyz, e->origin, delta ); 347 end = DotProduct( delta, e->dir ); 348 349 350 if ( start < end ) { 351 p = e->chain.next; 352 } else { 353 p = e->chain.prev; 354 } 355 356 for ( ; p != &e->chain ; ) { 357 if ( start < end ) { 358 if ( p->intercept > end - ON_EPSILON ) { 359 break; 360 } 361 } else { 362 if ( p->intercept < end + ON_EPSILON ) { 363 break; 364 } 365 } 366 367 if ( 368 ( start < end && p->intercept > start + ON_EPSILON ) || 369 ( start > end && p->intercept < start - ON_EPSILON ) ) { 370 // insert this point 371 if ( numVerts == MAX_SURFACE_VERTS ) { 372 Error( "MAX_SURFACE_VERTS" ); 373 } 374 375 // take the exact intercept point 376 VectorCopy( p->xyz, verts[ numVerts ].xyz ); 377 378 // copy the normal 379 VectorCopy( v1->normal, verts[ numVerts ].normal ); 380 381 // interpolate the texture coordinates 382 frac = ( p->intercept - start ) / ( end - start ); 383 for ( j = 0 ; j < 2 ; j++ ) { 384 verts[ numVerts ].st[j] = v1->st[j] + 385 frac * ( v2->st[j] - v1->st[j] ); 386 } 387 originals[numVerts] = i; 388 numVerts++; 389 counts[i]++; 390 } 391 392 if ( start < end ) { 393 p = p->next; 394 } else { 395 p = p->prev; 396 } 397 } 398 } 399 400 c_addedVerts += numVerts - ds->numVerts; 401 c_totalVerts += numVerts; 402 403 404 // FIXME: check to see if the entire surface degenerated 405 // after snapping 406 407 // rotate the points so that the initial vertex is between 408 // two non-subdivided edges 409 for ( i = 0 ; i < numVerts ; i++ ) { 410 if ( originals[ (i+1) % numVerts ] == originals[ i ] ) { 411 continue; 412 } 413 j = (i + numVerts - 1 ) % numVerts; 414 k = (i + numVerts - 2 ) % numVerts; 415 if ( originals[ j ] == originals[ k ] ) { 416 continue; 417 } 418 break; 419 } 420 421 if ( i == 0 ) { 422 // fine the way it is 423 c_natural++; 424 425 ds->numVerts = numVerts; 426 ds->verts = malloc( numVerts * sizeof( *ds->verts ) ); 427 memcpy( ds->verts, verts, numVerts * sizeof( *ds->verts ) ); 428 429 return; 430 } 431 if ( i == numVerts ) { 432 // create a vertex in the middle to start the fan 433 c_cant++; 434 435 /* 436 memset ( &verts[numVerts], 0, sizeof( verts[numVerts] ) ); 437 for ( i = 0 ; i < numVerts ; i++ ) { 438 for ( j = 0 ; j < 10 ; j++ ) { 439 verts[numVerts].xyz[j] += verts[i].xyz[j]; 440 } 441 } 442 for ( j = 0 ; j < 10 ; j++ ) { 443 verts[numVerts].xyz[j] /= numVerts; 444 } 445 446 i = numVerts; 447 numVerts++; 448 */ 449 } else { 450 // just rotate the vertexes 451 c_rotate++; 452 453 } 454 455 ds->numVerts = numVerts; 456 ds->verts = malloc( numVerts * sizeof( *ds->verts ) ); 457 458 for ( j = 0 ; j < ds->numVerts ; j++ ) { 459 ds->verts[j] = verts[ ( j + i ) % ds->numVerts ]; 460 } 461 } 462 463 /* 464 ================ 465 EdgeCompare 466 ================ 467 */ 468 int EdgeCompare( const void *elem1, const void *elem2 ) { 469 float d1, d2; 470 471 d1 = ((originalEdge_t *)elem1)->length; 472 d2 = ((originalEdge_t *)elem2)->length; 473 474 if ( d1 < d2 ) { 475 return -1; 476 } 477 if ( d2 > d1 ) { 478 return 1; 479 } 480 return 0; 481 } 482 483 484 /* 485 ================ 486 FixTJunctions 487 488 Call after the surface list has been pruned, but before lightmap allocation 489 ================ 490 */ 491 void FixTJunctions( entity_t *ent ) { 492 int i; 493 mapDrawSurface_t *ds; 494 int axialEdgeLines; 495 originalEdge_t *e; 496 497 qprintf("----- FixTJunctions -----\n"); 498 499 numEdgeLines = 0; 500 numOriginalEdges = 0; 501 502 // add all the edges 503 // this actually creates axial edges, but it 504 // only creates originalEdge_t structures 505 // for non-axial edges 506 for ( i = ent->firstDrawSurf ; i < numMapDrawSurfs ; i++ ) { 507 ds = &mapDrawSurfs[i]; 508 if ( ds->patch ) { 509 AddPatchEdges( ds ); 510 } else if ( ds->shaderInfo->autosprite || ds->shaderInfo->notjunc || ds->miscModel ) { 511 // miscModels don't add tjunctions 512 } else { 513 AddSurfaceEdges( ds ); 514 } 515 } 516 517 axialEdgeLines = numEdgeLines; 518 519 // sort the non-axial edges by length 520 qsort( originalEdges, numOriginalEdges, sizeof(originalEdges[0]), EdgeCompare ); 521 522 // add the non-axial edges, longest first 523 // this gives the most accurate edge description 524 for ( i = 0 ; i < numOriginalEdges ; i++ ) { 525 e = &originalEdges[i]; 526 e->dv[0]->lightmap[0] = AddEdge( e->dv[0]->xyz, e->dv[1]->xyz, qtrue ); 527 } 528 529 qprintf( "%6i axial edge lines\n", axialEdgeLines ); 530 qprintf( "%6i non-axial edge lines\n", numEdgeLines - axialEdgeLines ); 531 qprintf( "%6i degenerate edges\n", c_degenerateEdges ); 532 533 // insert any needed vertexes 534 for ( i = ent->firstDrawSurf ; i < numMapDrawSurfs ; i++ ) { 535 ds = &mapDrawSurfs[i]; 536 if ( ds->patch ) { 537 continue; 538 } 539 if ( ds->shaderInfo->autosprite || ds->shaderInfo->notjunc || ds->miscModel ) { 540 continue; 541 } 542 543 FixSurfaceJunctions( ds ); 544 } 545 546 qprintf( "%6i verts added for tjunctions\n", c_addedVerts ); 547 qprintf( "%6i total verts\n", c_totalVerts ); 548 qprintf( "%6i naturally ordered\n", c_natural ); 549 qprintf( "%6i rotated orders\n", c_rotate ); 550 qprintf( "%6i can't order\n", c_cant ); 551 }