Richard Boegli's CnC_Generals_Zero_Hour Fork WIP
This is documentation of Richard Boegil's Zero Hour Fork
 
Loading...
Searching...
No Matches
visrasterizer.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/***********************************************************************************************
20 *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
21 ***********************************************************************************************
22 * *
23 * Project Name : ww3d *
24 * *
25 * $Archive:: /Commando/Code/ww3d2/visrasterizer.cpp $*
26 * *
27 * Original Author:: Greg Hjelstrom *
28 * *
29 * $Author:: Jani_p $*
30 * *
31 * $Modtime:: 11/24/01 5:42p $*
32 * *
33 * $Revision:: 11 $*
34 * *
35 *---------------------------------------------------------------------------------------------*
36 * Functions: *
37 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
38
39#include "visrasterizer.h"
40#include "camera.h"
41#include "plane.h"
42#include "vp.h"
43
44
50{
51public:
52 void Reset(void);
53 void Add_Vertex(const Vector3 & point);
54 void Clip(const PlaneClass & plane,VisPolyClass & dest) const;
55
57};
58
59
61{
62 Verts.Delete_All(false);
63}
64
66{
67 Verts.Add(point);
68}
69
70void VisPolyClass::Clip(const PlaneClass & plane,VisPolyClass & dest) const
71{
72 dest.Reset();
73
74 // temporary variables used in clipping
75 int i = 0;
76 int vcount = Verts.Count();
77 int iprev = vcount - 1;
78 bool cur_point_in_front;
79 bool prev_point_in_front;
80 float alpha;
81 Vector3 int_point;
82
83 if (vcount <= 2) return;
84
85 // perform clipping
86 prev_point_in_front = !plane.In_Front(Verts[iprev]); // Note, plane normal is outward so we invert this test
87 for (int j=0; j<vcount; j++) {
88
89 cur_point_in_front = !plane.In_Front(Verts[i]); // Note, plane nomral is out so we invert this test
90 if (prev_point_in_front) {
91
92 if (cur_point_in_front) {
93
94 // Previous vertex was in front of plane and this vertex is in
95 // front of the plane so we emit this vertex.
96 dest.Add_Vertex(Verts[i]);
97
98 } else {
99
100 // Previous vert was in front, this vert is behind, compute
101 // the intersection and emit the point.
102 plane.Compute_Intersection(Verts[iprev],Verts[i],&alpha);
103 Vector3::Lerp(Verts[iprev],Verts[i],alpha,&int_point);
104 dest.Add_Vertex(int_point);
105
106 }
107
108 } else {
109
110 if (cur_point_in_front) {
111
112 // segment is going from the back halfspace to the front halfspace
113 // compute the intersection and emit it, then continue
114 // the edge into the front halfspace and emit the end point.
115 plane.Compute_Intersection(Verts[iprev],Verts[i],&alpha);
116 Vector3::Lerp(Verts[iprev],Verts[i],alpha,&int_point);
117 dest.Add_Vertex(int_point);
118 dest.Add_Vertex(Verts[i]);
119
120 }
121 }
122
123 prev_point_in_front = cur_point_in_front;
124 iprev = i;
125
126 //i = (i+1)%(Verts.Count());
127 i++;
128 if (i>=vcount) {
129 i = 0;
130 }
131 }
132}
133
134static VisPolyClass _VisPoly0;
135static VisPolyClass _VisPoly1;
136
137
138
139/*********************************************************************************************
140
141 VisRasterizerClass Implementation
142
143*********************************************************************************************/
144
145
152
157
158void VisRasterizerClass::Set_Resolution(int width,int height)
159{
160 IDBuffer.Set_Resolution(width,height);
161}
162
163void VisRasterizerClass::Get_Resolution(int * set_width,int * set_height)
164{
165 IDBuffer.Get_Resolution(set_width,set_height);
166}
167
173
179
181{
182 if (Camera != NULL) {
183 Camera->Add_Ref();
184 }
185 return Camera;
186}
187
192
194{
195 Matrix3D view_tm(1);
196
197 if (Camera) {
198 Camera->Get_View_Matrix(&view_tm);
199 }
200
201#ifdef ALLOW_TEMPORARIES
202 MVTransform = view_tm * ModelTransform;
203#else
204 MVTransform.mul(view_tm, ModelTransform);
205#endif
206}
207
209{
210 // TODO: optimize this
211 Update_MV_Transform(); // the user can and does mess with the camera directly!
212 return MVTransform;
213}
214
216{
217 TempVertexBuffer.Uninitialised_Grow(count);
218 return &(TempVertexBuffer[0]);
219}
220
221
223(
224 const Vector3 * verts,
225 int vcount,
226 const TriIndex * tris,
227 int tcount,const
228 AABoxClass & bounds
229)
230{
231 WWASSERT(verts != NULL);
232 WWASSERT(tris != NULL);
233 WWASSERT(vcount > 0);
234 WWASSERT(tcount > 0);
235
236 /*
237 ** if the user supplied bounds, check if we need to clip
238 */
239 if (CollisionMath::Overlap_Test(Camera->Get_Frustum(),bounds) == CollisionMath::INSIDE) {
240 return Render_Triangles_No_Clip(verts,vcount,tris,tcount);
241 } else {
242 return Render_Triangles_Clip(verts,vcount,tris,tcount);
243 }
244}
245
246
248(
249 const Vector3 * verts,
250 int vcount,
251 const TriIndex * tris,
252 int tcount
253)
254{
255 bool pixel_passed = false;
256
257 /*
258 ** 1. transform verts into homogeneous view space and project
259 */
260 const Matrix3D & tm = Get_MV_Transform();
261 Vector3 * tverts = Get_Temp_Vertex_Buffer(vcount);
262
263 VectorProcessorClass::Transform(tverts,verts,tm,vcount);
264
265 for (int i=0; i<vcount; i++) {
266 Camera->Project_Camera_Space_Point(tverts[i],tverts[i]);
267 }
268
269 /*
270 ** 2. Pass triangles on to the ID buffer for scan conversion
271 */
272 for (int tri_index=0; tri_index<tcount; tri_index++) {
273
274 const TriIndex & tri = tris[tri_index];
275 pixel_passed |= IDBuffer.Render_Triangle(tverts[tri.I],tverts[tri.J],tverts[tri.K]);
276 if (pixel_passed && (IDBuffer.Get_Render_Mode() == IDBufferClass::NON_OCCLUDER_MODE)) {
277 return true;
278 }
279 }
280 return pixel_passed;
281}
282
283
285(
286 const Vector3 * verts,
287 int vcount,
288 const TriIndex * tris,
289 int tcount
290)
291{
292 bool pixel_passed = false;
293
294 /*
295 ** 1. transform triangles into homogeneous view space
296 */
297 const Matrix3D & tm = Get_MV_Transform();
298 Vector3 * tverts = Get_Temp_Vertex_Buffer(vcount);
299
300 VectorProcessorClass::Transform(tverts,verts,tm,vcount);
301
302 /*
303 ** 2. get the frustum clipping planes
304 */
305 const PlaneClass *planes = Camera->Get_View_Space_Frustum_Planes();
306
307 /*
308 ** 3. Clip triangles to the view volume and pass on to the ID buffer for scan conversion
309 */
310 for (int tri_index=0; tri_index<tcount; tri_index++) {
311
312 /*
313 ** Copy triangle data into the vis clipping structure
314 */
315 _VisPoly0.Reset();
316 _VisPoly0.Add_Vertex(tverts[tris[tri_index].I]);
317 _VisPoly0.Add_Vertex(tverts[tris[tri_index].J]);
318 _VisPoly0.Add_Vertex(tverts[tris[tri_index].K]);
319
320 /*
321 ** Clip against the view frustum
322 */
323 _VisPoly0.Clip(planes[0],_VisPoly1);
324 _VisPoly1.Clip(planes[1],_VisPoly0);
325 _VisPoly0.Clip(planes[2],_VisPoly1);
326 _VisPoly1.Clip(planes[3],_VisPoly0);
327 _VisPoly0.Clip(planes[4],_VisPoly1);
328 _VisPoly1.Clip(planes[5],_VisPoly0);
329
330 /*
331 ** Project the vertices
332 */
333 int final_vcount = _VisPoly0.Verts.Count();
334
335 if (final_vcount >= 3) {
336
337 Vector3 * final_verts = &(_VisPoly0.Verts[0]);
338
339 int i;
340 for (i=0; i<final_vcount; i++) {
341 Camera->Project_Camera_Space_Point(final_verts[i],final_verts[i]);
342 }
343
344 /*
345 ** Pass the resulting triangle fan to the IDBuffer
346 */
347 for (i=1; i<final_vcount - 1; i++) {
348
349 pixel_passed |= IDBuffer.Render_Triangle(final_verts[0],final_verts[i],final_verts[i+1]);
350 if (pixel_passed && (IDBuffer.Get_Render_Mode() == IDBufferClass::NON_OCCLUDER_MODE)) {
351 return true;
352 }
353 }
354 }
355 }
356 return pixel_passed;
357}
358
359
360
361
362
363/*********************************************************************************************
364
365 IDBufferClass Implementation
366
367*********************************************************************************************/
368
382
383
388
390{
391 if ((w == ResWidth) && (h == ResHeight)) {
392 return;
393 } else {
394 ResWidth = w;
395 ResHeight = h;
397 }
398}
399
400void IDBufferClass::Get_Resolution(int * get_w,int * get_h)
401{
402 if (get_w != NULL) { *get_w = ResWidth; }
403 if (get_h != NULL) { *get_h = ResHeight; }
404}
405
407{
408 if (IDBuffer!=NULL) {
409 delete[] IDBuffer;
410 IDBuffer = NULL;
411 }
412 if (ZBuffer != NULL) {
413 delete[] ZBuffer;
414 ZBuffer = NULL;
415 }
416 PixelCounter = 0;
417}
418
420{
421 Reset();
422
423 int bufsize = ResWidth * ResHeight;
424
425 if (bufsize > 0) {
426 IDBuffer = W3DNEWARRAY uint32 [bufsize];
427 ZBuffer = W3DNEWARRAY float [bufsize];
428 }
429}
430
432{
433 if ((ResWidth > 0) && (ResHeight > 0)) {
434 int byte_count = ResWidth * ResWidth * sizeof(uint32);
435
438 memset(IDBuffer,0,byte_count);
439 memset(ZBuffer,0,byte_count);
440 }
441}
442
443
449{
451 {
452 float oodx = 1 / ( ((verts[1].X - verts[2].X) * (verts[0].Y - verts[2].Y)) -
453 ((verts[0].X - verts[2].X) * (verts[1].Y - verts[2].Y)));
454 float oody = -oodx;
455
456 for(int i=0; i<3; i++) {
457 OOZ[i] = 1/verts[i].Z;
458 }
459
460 DOOZ_DX = oodx * ( ((OOZ[1] - OOZ[2]) * (verts[0].Y - verts[2].Y)) -
461 ((OOZ[0] - OOZ[2]) * (verts[1].Y - verts[2].Y)));
462
463 DOOZ_DY = oody * ( ((OOZ[1] - OOZ[2]) * (verts[0].X - verts[2].X)) -
464 ((OOZ[0] - OOZ[2]) * (verts[1].X - verts[2].X)));
465 }
466
467 float OOZ[3]; // 1/z for each vertex
468 float DOOZ_DX; // change in 1/z per change in x
469 float DOOZ_DY; // change in 1/z per change in y
470};
471
472
478{
479 EdgeStruct(const GradientsStruct & grad,const Vector3 * verts,int top,int bottom)
480 {
481 Y = WWMath::Ceil(verts[top].Y);
482 Height = WWMath::Ceil(verts[bottom].Y) - Y;
483
484 float y_prestep = Y - verts[top].Y;
485 float real_height = verts[bottom].Y - verts[top].Y;
486 float real_width = verts[bottom].X - verts[top].X;
487
488 X = ((real_width * y_prestep)/real_height) + verts[top].X;
489 XStep = real_width / real_height;
490 float x_prestep = X - verts[top].X;
491
492 OOZ = grad.OOZ[top] + y_prestep * grad.DOOZ_DY + x_prestep * grad.DOOZ_DX;
493 OOZStep = XStep * grad.DOOZ_DX + grad.DOOZ_DY;
494 }
495
496 inline int Step(void)
497 {
498 X+=XStep;
499 Y++;
500 Height--;
501 OOZ+=OOZStep;
502 return Height;
503 }
504
505 float X; // fractional x coord
506 float XStep; // change in x per scanline
507 int Y; // current y coord
508 int Height; // number of scanlines left
509 float OOZ; // current 1/z
510 float OOZStep; // change in 1/z per scanline
511};
512
513
514bool IDBufferClass::Render_Triangle(const Vector3 & p0,const Vector3 & p1,const Vector3 & p2)
515{
516 if ((ZBuffer == NULL) || (IDBuffer == NULL)) {
517 return false;
518 }
519
520 int pixels_passed = 0;
521 bool is_backfacing = Is_Backfacing(p0,p1,p2);
522
523 if ((is_backfacing) && (TwoSidedRenderingEnabled == false)) {
525 return false;
526 }
528 } else {
530 }
531
532
533
534 /*
535 ** Transform the coordinates to device coords
536 ** All coordinates come in with the range -1 -> +1
537 */
538 Vector3 points[3];
539 points[0].X = 0.5f * (p0.X + 1.0f) * ResWidth;
540 points[1].X = 0.5f * (p1.X + 1.0f) * ResWidth;
541 points[2].X = 0.5f * (p2.X + 1.0f) * ResWidth;
542
543 points[0].Y = 0.5f * (1.0f - p0.Y) * ResHeight;
544 points[1].Y = 0.5f * (1.0f - p1.Y) * ResHeight;
545 points[2].Y = 0.5f * (1.0f - p2.Y) * ResHeight;
546
547 points[0].Z = 0.5f * (p0.Z + 1.001f) * 1000.0f;
548 points[1].Z = 0.5f * (p1.Z + 1.001f) * 1000.0f;
549 points[2].Z = 0.5f * (p2.Z + 1.001f) * 1000.0f;
550
551 /*
552 ** Sort points based on Y
553 */
554 float y0 = points[0].Y;
555 float y1 = points[1].Y;
556 float y2 = points[2].Y;
557
558 int top,middle,bottom,middle_for_compare,bottom_for_compare;
559
560 if(y0 < y1) {
561 if(y2 < y0) {
562 top = 2; middle = 0; bottom = 1;
563 middle_for_compare = 0; bottom_for_compare = 1;
564 } else {
565 top = 0;
566 if(y1 < y2) {
567 middle = 1; bottom = 2;
568 middle_for_compare = 1; bottom_for_compare = 2;
569 } else {
570 middle = 2; bottom = 1;
571 middle_for_compare = 2; bottom_for_compare = 1;
572 }
573 }
574 } else {
575 if(y2 < y1) {
576 top = 2; middle = 1; bottom = 0;
577 middle_for_compare = 1; bottom_for_compare = 0;
578 } else {
579 top = 1;
580 if(y0 < y2) {
581 middle = 0; bottom = 2;
582 middle_for_compare = 3; bottom_for_compare = 2;
583 } else {
584 middle = 2; bottom = 0;
585 middle_for_compare = 2; bottom_for_compare = 3;
586 }
587 }
588 }
589
590 /*
591 ** Compute the gradients and set up the edge structures
592 */
593 GradientsStruct grads(points);
594 EdgeStruct top_to_bottom_edge(grads,points,top,bottom);
595 EdgeStruct top_to_middle_edge(grads,points,top,middle);
596 EdgeStruct middle_to_bottom_edge(grads,points,middle,bottom);
597
598 EdgeStruct * left_edge = NULL;
599 EdgeStruct * right_edge = NULL;
600
601 bool middle_is_left = false;
602 if (bottom_for_compare > middle_for_compare) {
603 middle_is_left = 1 ^ is_backfacing;
604 } else {
605 middle_is_left = 0 ^ is_backfacing;
606 }
607 if (middle_is_left) {
608 left_edge = &top_to_middle_edge;
609 right_edge = &top_to_bottom_edge;
610 } else {
611 left_edge = &top_to_bottom_edge;
612 right_edge = &top_to_middle_edge;
613 }
614
615 /*
616 ** Fill scanlines
617 */
618 int height = top_to_middle_edge.Height;
619
620 while (height--) {
621 if (RenderMode == OCCLUDER_MODE) {
622 pixels_passed += Render_Occluder_Scanline(grads,left_edge,right_edge);
623 } else {
624 pixels_passed += Render_Non_Occluder_Scanline(grads,left_edge,right_edge);
625 }
626 left_edge->Step();
627 right_edge->Step();
628 }
629
630 if (middle_is_left) {
631 left_edge = &middle_to_bottom_edge;
632 right_edge = &top_to_bottom_edge;
633 } else {
634 left_edge = &top_to_bottom_edge;
635 right_edge = &middle_to_bottom_edge;
636 }
637
638 height = middle_to_bottom_edge.Height;
639
640 while (height--) {
641 if (RenderMode == OCCLUDER_MODE) {
642 pixels_passed += Render_Occluder_Scanline(grads,left_edge,right_edge);
643 } else {
644 pixels_passed += Render_Non_Occluder_Scanline(grads,left_edge,right_edge);
645 }
646 left_edge->Step();
647 right_edge->Step();
648 }
649 return (pixels_passed > 0);
650}
651
652
654{
655 if ((left->Y < 1) || (left->Y >= ResHeight)) {
656 return 0;
657 }
658
659 int xstart = WWMath::Float_To_Long(WWMath::Max(WWMath::Ceil(left->X),1.0f));
660 int width = WWMath::Float_To_Long(WWMath::Ceil(right->X)) - xstart;
661 if (xstart + width > ResWidth) {
662 width = ResWidth - xstart;
663 }
664
665 float xprestep = (float)xstart - left->X;
666 int address = Pixel_Coords_To_Address(xstart,left->Y);
667 float ooz = left->OOZ + xprestep * grads.DOOZ_DX;
668 int pixel_counter = 0;
669
670 /*
671 ** Two separate loops, backfaces only render when LESS THAN
672 */
673 if (CurID == BackfaceID) {
674 while (width-- > 0) {
675 if (ooz > ZBuffer[address]) {
676 IDBuffer[address] = CurID;
677 ZBuffer[address] = ooz;
678 pixel_counter++;
679 }
680 ooz += grads.DOOZ_DX;
681 address++;
682 }
683 /*
684 ** Front faces render when LESS THAN OR EQUAL TO
685 */
686 } else {
687 while (width-- > 0) {
688 if (ooz >= ZBuffer[address]) {
689 IDBuffer[address] = CurID;
690 ZBuffer[address] = ooz;
691 pixel_counter++;
692 }
693 ooz += grads.DOOZ_DX;
694 address++;
695 }
696 }
697
698 PixelCounter += pixel_counter;
699 return pixel_counter;
700}
701
702
704{
705 if ((left->Y < 1) || (left->Y >= ResHeight)) {
706 return 0;
707 }
708
709 int xstart = WWMath::Float_To_Long(WWMath::Max(WWMath::Ceil(left->X),1));
710 int width = WWMath::Float_To_Long(WWMath::Ceil(right->X)) - xstart;
711 if (xstart + width > ResWidth) {
712 width = ResWidth - xstart;
713 }
714
715 float xprestep = (float)xstart - left->X;
716 int address = Pixel_Coords_To_Address(xstart,left->Y);
717 float ooz = left->OOZ + xprestep * grads.DOOZ_DX;
718
719 while (width-- > 0) {
720 if (ooz >= ZBuffer[address]) {
721 PixelCounter++;
722 return 1;
723 }
724 ooz += grads.DOOZ_DX;
725 address++;
726 }
727
728 return 0;
729}
730
#define NULL
Definition BaseType.h:92
#define WWASSERT
#define W3DNEWARRAY
Definition always.h:110
unsigned long uint32
Definition bittype.h:46
@ false
Definition bool.h:59
static OverlapType Overlap_Test(const AAPlaneClass &plane, const Vector3 &point)
void Get_Resolution(int *get_w, int *get_h)
int Render_Non_Occluder_Scanline(GradientsStruct &grads, EdgeStruct *left, EdgeStruct *right)
bool Is_Backfacing(const Vector3 &p0, const Vector3 &p1, const Vector3 &p2)
uint32 * IDBuffer
ModeType RenderMode
int Render_Occluder_Scanline(GradientsStruct &grads, EdgeStruct *left, EdgeStruct *right)
void Allocate_Buffers(void)
bool TwoSidedRenderingEnabled
int Pixel_Coords_To_Address(int x, int y)
void Set_Resolution(int w, int h)
bool Render_Triangle(const Vector3 &p0, const Vector3 &p1, const Vector3 &p2)
bool Compute_Intersection(const Vector3 &p0, const Vector3 &p1, float *set_t) const
Definition plane.h:156
bool In_Front(const Vector3 &point) const
Definition plane.h:183
float X
Definition vector3.h:90
float Z
Definition vector3.h:92
float Y
Definition vector3.h:91
static void Lerp(const Vector3 &a, const Vector3 &b, float alpha, Vector3 *set_result)
Definition vector3.h:535
unsigned short K
Definition Vector3i.h:102
unsigned short J
Definition Vector3i.h:101
unsigned short I
Definition Vector3i.h:100
static void Transform(Vector3 *dst, const Vector3 *src, const Matrix3D &matrix, const int count)
Definition vp.cpp:80
SimpleDynVecClass< Vector3 > Verts
void Reset(void)
void Add_Vertex(const Vector3 &point)
void Clip(const PlaneClass &plane, VisPolyClass &dest) const
CameraClass * Camera
void Set_Camera(CameraClass *camera)
bool Render_Triangles_No_Clip(const Vector3 *verts, int vcount, const TriIndex *tris, int tcount)
CameraClass * Peek_Camera(void)
IDBufferClass IDBuffer
bool Render_Triangles_Clip(const Vector3 *verts, int vcount, const TriIndex *tris, int tcount)
void Get_Resolution(int *set_width, int *set_height)
void Set_Model_Transform(const Matrix3D &model)
SimpleVecClass< Vector3 > TempVertexBuffer
const Matrix3D & Get_MV_Transform(void)
void Update_MV_Transform(void)
bool Render_Triangles(const Vector3 *verts, int vcount, const TriIndex *tris, int tcount, const AABoxClass &bounds)
Vector3 * Get_Temp_Vertex_Buffer(int count)
CameraClass * Get_Camera(void)
void Set_Resolution(int width, int height)
static float Max(float a, float b)
Definition wwmath.h:264
static float Ceil(float val)
Definition wwmath.h:152
static long Float_To_Long(float f)
Definition wwmath.h:322
#define I(x, y, z)
Definition md5.cpp:89
Vector3i16 TriIndex
#define REF_PTR_RELEASE(x)
Definition refcount.h:80
#define REF_PTR_SET(dst, src)
Definition refcount.h:79
EdgeStruct(const GradientsStruct &grad, const Vector3 *verts, int top, int bottom)
int Step(void)
GradientsStruct(const Vector3 *verts)