Richard Boegli's CnC_Generals_Zero_Hour Fork WIP
This is documentation of Richard Boegil's Zero Hour Fork
 
Loading...
Searching...
No Matches
vxllayer.cpp
Go to the documentation of this file.
1/*
2** Command & Conquer Generals Zero Hour(tm)
3** Copyright 2025 Electronic Arts Inc.
4**
5** This program is free software: you can redistribute it and/or modify
6** it under the terms of the GNU General Public License as published by
7** the Free Software Foundation, either version 3 of the License, or
8** (at your option) any later version.
9**
10** This program is distributed in the hope that it will be useful,
11** but WITHOUT ANY WARRANTY; without even the implied warranty of
12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13** GNU General Public License for more details.
14**
15** You should have received a copy of the GNU General Public License
16** along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/* $Header: /Commando/Code/Tools/max2w3d/vxllayer.cpp 4 10/28/97 6:08p Greg_h $ */
20/***********************************************************************************************
21 *** Confidential - Westwood Studios ***
22 ***********************************************************************************************
23 * *
24 * Project Name : Commando / G *
25 * *
26 * File Name : VXLLAYER.CPP *
27 * *
28 * Programmer : Greg Hjelstrom *
29 * *
30 * Start Date : 06/10/97 *
31 * *
32 * Last Update : June 10, 1997 [GH] *
33 * *
34 *---------------------------------------------------------------------------------------------*
35 * Functions: *
36 * VoxelLayerClass::VoxelLayerClass -- Constructor for VoxelLayerClass *
37 * VoxelLayerClass::Intersect_Triangle -- Intersect a triangle with the slab *
38 * VoxelLayerClass::Draw_Line -- Draw a line of voxels into the slab *
39 * VoxelLayerClass::Scan_Triangle -- Clip and scan-convert a triangle into the slab *
40 * clip_tri_to_slab -- Clips a triangle against a voxel slab *
41 * clip_poly -- clip a polygon against a single 3D plane *
42 * output -- Emit a vertex into a polygons vertex list *
43 * inside -- Test whether a point is in the front half-space of a plane *
44 * intersect -- compute intersection between a line and a plane *
45 * clear_scan_table -- clears the static scanline table *
46 * fixup_scan_table -- ensure all spans are left->right in order *
47 * scan_edge -- Scan convert an edge *
48 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
49
50
51#include "vxllayer.h"
52#include "plane.h"
53
54
55/***************************************************************************************
56** local types
57***************************************************************************************/
59{
60 Point3 Pos;
61 Point3 Bary;
62};
63
65{
67};
68
69
70/***************************************************************************************
71** static data
72***************************************************************************************/
73static scanstruct _scantab[256];
74
75const int LEFT = 0;
76const int RIGHT = 1;
77const float EMPTY_SPAN = -10000.0f;
78
79
80
81/***************************************************************************************
82** local functions
83***************************************************************************************/
84static void clip_tri_to_slab(
85 Point3 p0,
86 Point3 p1,
87 Point3 p2,
88 float z0,
89 float z1,
90 vertexstruct * outverts,
91 int * setnum);
92
93static void clip_poly(
94 vertexstruct * inverts,
95 int innum,
96 vertexstruct * outverts,
97 int * outnum,
98 const PlaneClass & clipplane);
99
100static void output(
101 const vertexstruct & outvert,
102 vertexstruct * poly,
103 int * numverts);
104
105static int inside(
106 const vertexstruct & p,
107 const PlaneClass & plane);
108
109static vertexstruct intersect(
110 const vertexstruct & p0,
111 const vertexstruct & p1,
112 const PlaneClass & plane);
113
114static void clear_scan_table(void);
115
116static void fixup_scan_table(
117 int y0,
118 int y1);
119
120static void scan_edge(
121 const vertexstruct & p0,
122 const vertexstruct & p1);
123
124
125
126
127
128
129
130/***********************************************************************************************
131 * VoxelLayerClass::VoxelLayerClass -- Constructor for VoxelLayerClass *
132 * *
133 * INPUT: *
134 * *
135 * OUTPUT: *
136 * *
137 * WARNINGS: *
138 * *
139 * HISTORY: *
140 * 06/10/1997 GH : Created. *
141 *=============================================================================================*/
143(
144 INodeListClass & object_list,
145 TimeValue time,
146 Matrix3 parenttm,
147 Point3 offset,
148 Point3 scale,
149 float slicez,
150 float sliceh,
151 int bmwidth,
152 int bmheight
153)
154{
155 unsigned i;
156
157 SliceZ = slicez;
158 SliceH = sliceh;
159 SliceZ0 = slicez - sliceh / 2;
160 SliceZ1 = slicez + sliceh / 2;
161
162 bitmap_width = bmwidth;
163 bitmap_height = bmheight;
164
165 // Initialize everything with zero
166 memset ( &(Solid[0][0]), 0, sizeof(Solid));
167
168 // Go through the list of objects and intersect them with the plane.
169 for ( i = 0; i < object_list.Num_Nodes(); i++ )
170 {
171 // Get relavent data from MAX
172 INode * inode = object_list[i];
173 Object * obj = inode->EvalWorldState(time).obj;
174 TriObject * tri = (TriObject *)obj->ConvertToType(time, triObjectClassID);
175 Mesh * mesh = &(tri->mesh);
176 Matrix3 objtm = inode->GetObjectTM(time);
177
178 // Compute a delta matrix which puts vertices into the parent space
179 Matrix3 delta = objtm * Inverse(parenttm);
180
181 // Loop through each face, intersecting it with the slice.
182 unsigned faces = mesh->getNumFaces();
183 for ( unsigned face_index = 0; face_index < faces; ++ face_index )
184 {
185 Face & face = mesh->faces [ face_index ];
186
187 // transform the vertices into the parent space
188 Point3 a = mesh->verts [ face.v[0] ] * delta;
189 Point3 b = mesh->verts [ face.v[1] ] * delta;
190 Point3 c = mesh->verts [ face.v[2] ] * delta;
191
192 // shift the vertices to the origin
193 a.x -= offset.x;
194 a.y -= offset.y;
195 b.x -= offset.x;
196 b.y -= offset.y;
197 c.x -= offset.x;
198 c.y -= offset.y;
199
200 // scale the vertices into the voxel grid
201 a.x *= scale.x;
202 a.y *= scale.y;
203 b.x *= scale.x;
204 b.y *= scale.y;
205 c.x *= scale.x;
206 c.y *= scale.y;
207
208// Intersect_Triangle ( a, b, c, SliceZ );
209 Scan_Triangle( a, b, c );
210 }
211 }
212}
213
214
215
216/***********************************************************************************************
217 * VoxelLayerClass::Intersect_Triangle -- Intersect a triangle with the slab *
218 * *
219 * INPUT: *
220 * *
221 * OUTPUT: *
222 * *
223 * WARNINGS: *
224 * *
225 * HISTORY: *
226 * 06/10/1997 GH : Created. *
227 *=============================================================================================*/
229(
230 Point3 a,
231 Point3 b,
232 Point3 c,
233 float z
234)
235{
236 double start_x, start_y, end_x, end_y;
237
238 // If the triangle is wholly above or below the intersection plane,
239 // it does not intersect with the plane.
240 if ( a.z < z && b.z < z && c.z < z )
241 return;
242
243 if ( a.z > z && b.z > z && c.z > z )
244 return;
245
246 // Find the upward intersection moving counterclockwise. This will be
247 // the start of the edge.
248 if ( a.z < z && b.z >= z )
249 {
250 start_x = a.x + (b.x - a.x) * (z - a.z) / (b.z - a.z);
251 start_y = a.y + (b.y - a.y) * (z - a.z) / (b.z - a.z);
252 }
253 else if ( b.z < z && c.z >= z )
254 {
255 start_x = b.x + (c.x - b.x) * (z - b.z) / (c.z - b.z);
256 start_y = b.y + (c.y - b.y) * (z - b.z) / (c.z - b.z);
257 }
258 else if ( c.z < z && a.z >= z )
259 {
260 start_x = c.x + (a.x - c.x) * (z - c.z) / (a.z - c.z);
261 start_y = c.y + (a.y - c.y) * (z - c.z) / (a.z - c.z);
262 }
263 else
264 {
265 return;
266 }
267
268 // Find the downward intersection moving counterclockwise. This is the end
269 // of the edge.
270 if ( a.z >= z && b.z < z )
271 {
272 end_x = a.x + (b.x - a.x) * (z - a.z) / (b.z - a.z);
273 end_y = a.y + (b.y - a.y) * (z - a.z) / (b.z - a.z);
274 }
275 else if ( b.z >= z && c.z < z )
276 {
277 end_x = b.x + (c.x - b.x) * (z - b.z) / (c.z - b.z);
278 end_y = b.y + (c.y - b.y) * (z - b.z) / (c.z - b.z);
279 }
280 else if ( c.z >= z && a.z < z )
281 {
282 end_x = c.x + (a.x - c.x) * (z - c.z) / (a.z - c.z);
283 end_y = c.y + (a.y - c.y) * (z - c.z) / (a.z - c.z);
284 }
285 else
286 {
287 return;
288 }
289
290 // Draw the edge into the bitmap.
291 Draw_Line(start_x, start_y, end_x, end_y);
292}
293
294
295/***********************************************************************************************
296 * VoxelLayerClass::Draw_Line -- Draw a line of voxels into the slab *
297 * *
298 * INPUT: *
299 * *
300 * OUTPUT: *
301 * *
302 * WARNINGS: *
303 * *
304 * HISTORY: *
305 * 06/10/1997 GH : Created. *
306 *=============================================================================================*/
308(
309 double x0,
310 double y0,
311 double x1,
312 double y1
313)
314{
315 // Fill in the squares containing the line's endpoints.
316 Add_Solid((int)x0, (int)y0);
317 Add_Solid((int)x1, (int)y1);
318
319 // Fill in the squares between the endpoints.
320 double delta_x = fabs (x1 - x0);
321 double delta_y = fabs (y1 - y0);
322
323 if ( delta_x > delta_y )
324 {
325 // This is an X-major line.
326 if ( x0 > x1 )
327 {
328 double temp = x0;
329 x0 = x1;
330 x1 = temp;
331
332 temp = y0;
333 y0 = y1;
334 y1 = temp;
335 }
336
337 double step_y = (y1 - y0) / delta_x;
338
339 double y = y0 + step_y * (floor (x0 + 1) - x0);
340
341 for ( int x = (int) x0; x < (int) x1; ++ x )
342 {
343 if ( (int) y >= 0 && (int) y < bitmap_height )
344 {
345 Add_Solid(x, (int)y);
346 Add_Solid(x + 1, (int)y);
347 }
348
349 y += step_y;
350 }
351 }
352 else
353 {
354 // This is a Y-major line.
355 if ( y0 > y1 )
356 {
357 double temp = x0;
358 x0 = x1;
359 x1 = temp;
360
361 temp = y0;
362 y0 = y1;
363 y1 = temp;
364 }
365
366 double step_x = (x1 - x0) / delta_y;
367
368 double x = x0 + step_x * (floor (y0 + 1) - y0);
369
370 for ( int y = (int) y0; y < (int) y1; ++ y )
371 {
372 if ( (int) x >= 0 && (int) x < 256 )
373 {
374 Add_Solid((int)x, y);
375 Add_Solid((int)x, y+1);
376 }
377 }
378 }
379}
380
381
382
383/***********************************************************************************************
384 * VoxelLayerClass::Scan_Triangle -- Clip and scan-convert a triangle into the slab *
385 * *
386 * INPUT: *
387 * *
388 * OUTPUT: *
389 * *
390 * WARNINGS: *
391 * *
392 * HISTORY: *
393 * 06/10/1997 GH : Created. *
394 *=============================================================================================*/
396(
397 Point3 p0,
398 Point3 p1,
399 Point3 p2
400)
401{
402 int i;
403
404 // check if the entire triangle is above or below the slab:
405 if (p0.z < SliceZ0 && p1.z < SliceZ0 && p2.z < SliceZ1) return;
406 if (p0.z > SliceZ1 && p1.z > SliceZ1 && p2.z > SliceZ1) return;
407
408 // clip the triangle to the slab
409 vertexstruct polyvert[8];
410 int numverts;
411
412 clip_tri_to_slab(p0,p1,p2,SliceZ0,SliceZ1,polyvert,&numverts);
413 if (numverts == 0) return;
414
415 // clear the scanline table, get y-extents of polygon
416 clear_scan_table();
417
418 float miny = polyvert[0].Pos.y;
419 float maxy = polyvert[0].Pos.y;
420
421 for (i=1; i<numverts; i++) {
422 if (polyvert[i].Pos.y < miny) miny = polyvert[i].Pos.y;
423 if (polyvert[i].Pos.y > maxy) maxy = polyvert[i].Pos.y;
424 }
425
426 // scanconvert the triangle
427 int start = numverts - 1;
428 for (i=0; i<numverts; i++) {
429 scan_edge(polyvert[start],polyvert[i]);
430 start = i;
431 }
432
433 // make sure all scans go left-right.
434 fixup_scan_table((int)floor(miny),(int)floor(maxy));
435
436 // draw the scanlines
437 for (i=(int)floor(miny); i<=(int)floor(maxy); i++) {
438
439// if (_scantab[i].P[LEFT].Pos.x != EMPTY_SPAN) {
440
441 Draw_Line(
442 _scantab[i].P[LEFT].Pos.x,
443 _scantab[i].P[LEFT].Pos.y,
444 _scantab[i].P[RIGHT].Pos.x,
445 _scantab[i].P[RIGHT].Pos.y);
446// }
447 }
448}
449
450
451/***********************************************************************************************
452 * clip_tri_to_slab -- Clips a triangle against a voxel slab *
453 * *
454 * INPUT: *
455 * *
456 * OUTPUT: *
457 * *
458 * WARNINGS: *
459 * *
460 * HISTORY: *
461 * 06/10/1997 GH : Created. *
462 *=============================================================================================*/
463static void clip_tri_to_slab
464(
465 Point3 p0,
466 Point3 p1,
467 Point3 p2,
468 float z0,
469 float z1,
470 vertexstruct * outverts,
471 int * setnum
472)
473{
474 static vertexstruct tmpverts[8];
475
476 memset(outverts,0,sizeof(outverts));
477 memset(tmpverts,0,sizeof(tmpverts));
478
479 // copy the three intial points:
480 outverts[0].Pos = p0;
481 outverts[1].Pos = p1;
482 outverts[2].Pos = p2;
483 outverts[0].Bary = Point3(1.0f,0.0f,0.0f);
484 outverts[1].Bary = Point3(0.0f,1.0f,0.0f);
485 outverts[2].Bary = Point3(0.0f,0.0f,1.0f);
486
487 // clip from the out buffer to the tmp buffer against bottom of slab:
488 clip_poly(outverts,3,tmpverts,setnum,PlaneClass(Vector3(0.0f,0.0f,1.0f),-z0));
489
490 // clip from the tmp buffer to the out buffer against top of slab:
491 clip_poly(tmpverts,*setnum,outverts,setnum,PlaneClass(Vector3(0.0f,0.0f,-1.0f),z1));
492}
493
494
495/***********************************************************************************************
496 * clip_poly -- clip a polygon against a single 3D plane *
497 * *
498 * INPUT: *
499 * *
500 * OUTPUT: *
501 * *
502 * WARNINGS: *
503 * *
504 * HISTORY: *
505 * 06/10/1997 GH : Created. *
506 *=============================================================================================*/
507static void clip_poly
508(
509 vertexstruct * inverts,
510 int innum,
511 vertexstruct * outverts,
512 int * outnum,
513 const PlaneClass & clipplane
514)
515{
516 vertexstruct p0,p1; // start and end of current edge
517 vertexstruct pi; // intersection point
518 int i;
519
520 // start with zero vertices
521 *outnum = 0;
522
523 p0 = inverts[innum-1];
524 for (i=0; i<innum; i++) {
525 p1 = inverts[i];
526
527 if (inside(p1,clipplane)) {
528 if (inside(p0,clipplane)) {
529 output(p1,outverts,outnum); //both inside: output p1
530 } else {
531 pi = intersect(p0,p1,clipplane);
532 output(pi,outverts,outnum); //p0 out, p1 in: output intersect and p1
533 output(p1,outverts,outnum);
534 }
535 } else {
536 if (inside(p0,clipplane)) {
537 pi = intersect(p0,p1,clipplane); //p0 in, p1 out: output intersect
538 output(pi,outverts,outnum);
539 }
540 }
541 p0 = p1;
542 }
543}
544
545
546/***********************************************************************************************
547 * output -- Emit a vertex into a polygons vertex list *
548 * *
549 * INPUT: *
550 * *
551 * OUTPUT: *
552 * *
553 * WARNINGS: *
554 * *
555 * HISTORY: *
556 * 06/10/1997 GH : Created. *
557 *=============================================================================================*/
558static void output
559(
560 const vertexstruct & outvert,
561 vertexstruct * poly,
562 int * numverts
563)
564{
565 poly[*numverts] = outvert;
566 (*numverts)++;
567}
568
569
570/***********************************************************************************************
571 * inside -- Test whether a point is in the front half-space of a plane *
572 * *
573 * INPUT: *
574 * *
575 * OUTPUT: *
576 * *
577 * WARNINGS: *
578 * *
579 * HISTORY: *
580 * 06/10/1997 GH : Created. *
581 *=============================================================================================*/
582static int inside
583(
584 const vertexstruct & p,
585 const PlaneClass & plane
586)
587{
588 float dist = p.Pos.x * plane.N[0] + p.Pos.y * plane.N[1] + p.Pos.z * plane.N[2] + plane.D;
589 if (dist >= 0.0f) {
590 return 1;
591 } else {
592 return 0;
593 }
594}
595
596
597/***********************************************************************************************
598 * intersect -- compute intersection between a line and a plane *
599 * *
600 * INPUT: *
601 * *
602 * OUTPUT: *
603 * *
604 * WARNINGS: *
605 * *
606 * HISTORY: *
607 * 06/10/1997 GH : Created. *
608 *=============================================================================================*/
609static vertexstruct intersect
610(
611 const vertexstruct & p0,
612 const vertexstruct & p1,
613 const PlaneClass & plane
614)
615{
616 float t;
617
618 Point3 delta = p1.Pos - p0.Pos;
619
620 float num = -( plane.N[0] * p0.Pos.x +
621 plane.N[1] * p0.Pos.y +
622 plane.N[2] * p0.Pos.z + plane.D );
623
624 float den = plane.N[0] * delta.x +
625 plane.N[1] * delta.y +
626 plane.N[2] * delta.z;
627
628 if (den != 0.0f) {
629 t = num / den;
630 } else {
631 t = 0.0f;
632 }
633
634 vertexstruct i;
635 i.Pos = (1.0f - t) * p0.Pos + t*p1.Pos;
636 i.Bary = (1.0f - t) * p0.Bary + t*p1.Bary;
637 return i;
638}
639
640
641/***********************************************************************************************
642 * clear_scan_table -- clears the static scanline table *
643 * *
644 * INPUT: *
645 * *
646 * OUTPUT: *
647 * *
648 * WARNINGS: *
649 * *
650 * HISTORY: *
651 * 06/10/1997 GH : Created. *
652 *=============================================================================================*/
653static void clear_scan_table(void)
654{
655 memset(_scantab,0,sizeof(_scantab));
656 for (int i=0; i<256; i++) {
657 _scantab[i].P[0].Pos.x = EMPTY_SPAN;
658 _scantab[i].P[1].Pos.x = EMPTY_SPAN;
659 }
660}
661
662/***********************************************************************************************
663 * fixup_scan_table -- ensure all spans are left->right in order *
664 * *
665 * INPUT: *
666 * *
667 * OUTPUT: *
668 * *
669 * WARNINGS: *
670 * *
671 * HISTORY: *
672 * 06/10/1997 GH : Created. *
673 *=============================================================================================*/
674static void fixup_scan_table(int y0,int y1)
675{
676 int i;
677 assert(y1 >= y0);
678
679 // Ensure the left -> right convention is followed.
680 for (i=y0; i<=y1; i++) {
681 if (_scantab[i].P[LEFT].Pos.x > _scantab[i].P[RIGHT].Pos.x) {
682 vertexstruct tmp = _scantab[i].P[LEFT];
683 _scantab[i].P[LEFT] = _scantab[i].P[RIGHT];
684 _scantab[i].P[RIGHT] = tmp;
685 }
686 }
687
688 // Ensure that we leave no gaps.
689 for (i=y0; i<y1; i++) {
690 if (_scantab[i+1].P[RIGHT].Pos.x < _scantab[i].P[LEFT].Pos.x) {
691
692 _scantab[i+1].P[RIGHT].Pos.x = _scantab[i].P[LEFT].Pos.x;
693
694 } else if (_scantab[i+1].P[LEFT].Pos.x > _scantab[i].P[RIGHT].Pos.x) {
695
696 _scantab[i+1].P[LEFT].Pos.x = _scantab[i].P[RIGHT].Pos.x;
697
698 }
699 }
700}
701
702
703/***********************************************************************************************
704 * scan_edge -- Scan convert an edge *
705 * *
706 * INPUT: *
707 * *
708 * OUTPUT: *
709 * *
710 * WARNINGS: *
711 * *
712 * HISTORY: *
713 * 06/10/1997 GH : Created. *
714 *=============================================================================================*/
715static void scan_edge
716(
717 const vertexstruct & p0,
718 const vertexstruct & p1
719)
720{
721 // is this a perfectly horizontal edge:
722 if (floor(p0.Pos.y) == floor(p1.Pos.y)) {
723
724 int si = (int)floor(p0.Pos.y);
725 const vertexstruct *left, *right;
726
727 if (p0.Pos.x < p1.Pos.x) {
728 left = &p0;
729 right = &p1;
730 } else {
731 left = &p1;
732 right = &p0;
733 }
734
735 // does this scanline already have a span in it?
736 if (_scantab[si].P[0].Pos.x != EMPTY_SPAN) {
737
738 // yes, expand this scanline's span to include this span
739 if (left->Pos.x < _scantab[si].P[LEFT].Pos.x) {
740 _scantab[si].P[LEFT] = *left;
741 }
742
743 if (right->Pos.x > _scantab[si].P[RIGHT].Pos.x) {
744 _scantab[si].P[RIGHT] = *right;
745 }
746
747 } else {
748
749 // no, set this scanline with the span for this edge
750 _scantab[si].P[LEFT] = *left;
751 _scantab[si].P[RIGHT] = *right;
752
753 }
754 return;
755 }
756
757 // is this a left or right edge:
758 int side;
759 const vertexstruct *top, *bot;
760
761 if (p0.Pos.y < p1.Pos.y) {
762 side = RIGHT;
763 top = &p0;
764 bot = &p1;
765 } else {
766 side = LEFT;
767 top = &p1;
768 bot = &p0;
769 }
770
771 // scan the edge into _scantab
772 for (double y = floor(top->Pos.y); y <= floor(bot->Pos.y); y += 1.0f) {
773
774 // parametric position on the scanline:
775 double t = (y - floor(top->Pos.y)) / (floor(bot->Pos.y) - floor(top->Pos.y));
776
777 // position:
778 _scantab[(int)y].P[side].Pos = (1.0f - (float)t)*top->Pos + (float)t*bot->Pos;
779
780 // barycentric coords:
781 _scantab[(int)y].P[side].Bary = (1.0f - (float)t)*top->Bary + (float)t*bot->Bary;
782 }
783}
Color scale(const Color &a, const Color &b)
Definition GameMtl.cpp:722
unsigned Num_Nodes(void) const
Definition nodelist.h:75
Vector3 N
Definition plane.h:67
float D
Definition plane.h:68
void Intersect_Triangle(Point3 a, Point3 b, Point3 c, float z)
Definition vxllayer.cpp:229
void Add_Solid(int x, int y)
Definition vxllayer.h:120
void Scan_Triangle(Point3 a, Point3 b, Point3 c)
Definition vxllayer.cpp:396
sint8 Solid[max_bitmap_width][max_bitmap_height]
Definition vxllayer.h:137
void Draw_Line(double x0, double y0, double x1, double y1)
Definition vxllayer.cpp:308
WWINLINE Quaternion Inverse(const Quaternion &a)
Definition quat.h:117
vertexstruct P[2]
Definition vxllayer.cpp:66
Point3 Bary
Definition vxllayer.cpp:61
const int RIGHT
Definition vxllayer.cpp:76
const int LEFT
Definition vxllayer.cpp:75
const float EMPTY_SPAN
Definition vxllayer.cpp:77