Richard Boegli's CnC_Generals_Zero_Hour Fork WIP
This is documentation of Richard Boegil's Zero Hour Fork
 
Loading...
Searching...
No Matches
seglinerenderer.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/seglinerenderer.cpp $*
26 * *
27 * Original Author:: Greg Hjelstrom *
28 * *
29 * $Author:: Kenny Mitchell *
30 * *
31 * $Modtime:: 06/26/02 4:04p $*
32 * *
33 * $Revision:: 5 $*
34 * *
35 * 06/26/02 KM Matrix name change to avoid MAX conflicts *
36 *---------------------------------------------------------------------------------------------*
37 * Functions: *
38 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
39
40#include "seglinerenderer.h"
41#include "ww3d.h"
42#include "rinfo.h"
43#include "dx8wrapper.h"
44#include "sortingrenderer.h"
45#include "vp.h"
46#include "vector3i.h"
47#include "random.h"
48#include "v3_rnd.h"
49#include "meshgeometry.h"
50
51
52/* We have chunking logic which handles N segments at a time. To simplify the subdivision logic,
53** we will ensure that N is a power of two and that N >= 2^MAX_SEGLINE_SUBDIV_LEVELS, so that the
54** subdivision logic can be inside the chunking loop.
55*/
56
57#if MAX_SEGLINE_SUBDIV_LEVELS > 7
58#define SEGLINE_CHUNK_SIZE (1 << MAX_SEGLINE_SUBDIV_LEVELS)
59#else
60#define SEGLINE_CHUNK_SIZE (128)
61#endif
62
63#ifdef _INTERNAL
64// for occasional debugging...
65//#pragma optimize("", off)
66//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
67#endif
68
69#define MAX_SEGLINE_POINT_BUFFER_SIZE (1 + SEGLINE_CHUNK_SIZE)
70// This macro depends on the assumption that each line segment is two polys.
71#define MAX_SEGLINE_POLY_BUFFER_SIZE (SEGLINE_CHUNK_SIZE * 2)
72
73
74
75
77 Texture(NULL),
78 Shader(ShaderClass::_PresetAdditiveSpriteShader),
79 Width(0.0f),
80 Color(Vector3(1,1,1)),
81 Opacity(1.0f),
82 SubdivisionLevel(0),
83 NoiseAmplitude(0.0f),
84 MergeAbortFactor(1.5f),
85 TextureTileFactor(1.0f),
86 LastUsedSyncTime(WW3D::Get_Sync_Time()),
87 CurrentUVOffset(0.0f,0.0f),
88 UVOffsetDeltaPerMS(0.0f, 0.0f),
89 Bits(DEFAULT_BITS),
90 m_vertexBufferSize(0),
91 m_vertexBuffer(NULL)
92{
93 // EMPTY
94}
95
97 Texture(NULL),
98 Shader(ShaderClass::_PresetAdditiveSpriteShader),
99 Width(0.0f),
100 Color(Vector3(1,1,1)),
101 Opacity(1.0f),
102 SubdivisionLevel(0),
103 NoiseAmplitude(0.0f),
104 MergeAbortFactor(1.5f),
105 TextureTileFactor(1.0f),
106 LastUsedSyncTime(that.LastUsedSyncTime),
107 CurrentUVOffset(0.0f,0.0f),
108 UVOffsetDeltaPerMS(0.0f, 0.0f),
109 Bits(DEFAULT_BITS),
110 m_vertexBufferSize(0),
111 m_vertexBuffer(NULL)
112{
113 *this = that;
114}
115
117{
118 if (this != &that) {
119 REF_PTR_SET(Texture,that.Texture);
120 Shader = that.Shader;
121 Width = that.Width;
122 Color = that.Color;
123 Opacity = that.Opacity;
124 SubdivisionLevel = that.SubdivisionLevel;
125 NoiseAmplitude = that.NoiseAmplitude;
126 MergeAbortFactor = that.MergeAbortFactor;
127 TextureTileFactor = that.TextureTileFactor;
128 LastUsedSyncTime = that.LastUsedSyncTime;
129 CurrentUVOffset = that.CurrentUVOffset;
130 UVOffsetDeltaPerMS = that.UVOffsetDeltaPerMS;
131 Bits = that.Bits;
132 // Don't modify m_vertexBufferSize and m_vertexBuffer
133 }
134 return *this;
135}
136
138{
139 REF_PTR_RELEASE(Texture);
140 delete [] m_vertexBuffer;
141}
142
172
173
175{
176 REF_PTR_SET(Texture,texture);
177}
178
180{
181 if (Texture != NULL) {
182 Texture->Add_Ref();
183 }
184 return Texture;
185}
186
188{
189 CurrentUVOffset = offset;
190}
191
193{
194 // Care should be taken to avoid tiling a texture too many times over a single polygon;
195 // otherwise performance may be adversely affected.
197 const static float MAX_LINE_TILING_FACTOR = 50.0f;
198 if (factor > MAX_LINE_TILING_FACTOR) {
199 WWDEBUG_SAY(("Texture (%s) Tile Factor (%.2f) too large in SegLineRendererClass!\r\n", Get_Texture()->Get_Texture_Name(), TextureTileFactor));
200 factor = MAX_LINE_TILING_FACTOR;
201 } else {
202 factor = MAX(factor, 0.0f);
203 }
204 TextureTileFactor = factor;
205}
206
208{
209 LastUsedSyncTime = WW3D::Get_Sync_Time();
210 CurrentUVOffset.Set(0.0f,0.0f);
211}
212
213
214
216(
217 RenderInfoClass & rinfo,
218 const Matrix3D & transform,
219 unsigned int num_points,
220 Vector3 * points,
221 const SphereClass & obj_sphere,
222 Vector4 * rgbas
223)
224{
225 Matrix4x4 view;
226 DX8Wrapper::Get_Transform(D3DTS_VIEW,view);
227
228 Matrix4x4 identity(true);
229 DX8Wrapper::Set_Transform(D3DTS_WORLD,identity);
230 DX8Wrapper::Set_Transform(D3DTS_VIEW,identity);
231
232 /*
233 ** Handle texture UV offset animation (done once for entire line).
234 */
235 unsigned int delta = WW3D::Get_Sync_Time() - LastUsedSyncTime;
236 float del = (float)delta;
237 Vector2 uv_offset = CurrentUVOffset + UVOffsetDeltaPerMS * del;
238
239 // ensure offsets are in [0, 1] range:
240 uv_offset.X = uv_offset.X - floorf(uv_offset.X);
241 uv_offset.Y = uv_offset.Y - floorf(uv_offset.Y);
242
243 // Update state
244 CurrentUVOffset = uv_offset;
245 LastUsedSyncTime = WW3D::Get_Sync_Time();
246
247 // Used later
249
250 /*
251 ** Process line geometry:
252 */
253
254 // This has been tweaked to produce empirically good results.
255 const float parallel_factor = 0.9f;
256
257 // We reduce the chunk size to take account of subdivision levels (so that the # of points
258 // after subdivision will be no higher than the allowed maximum). We know this will not reduce
259 // the chunk size below 2, since the chunk size must be at least two to the power of the
260 // maximum allowable number of subdivisions. The plus 1 is because #points = #segments + 1.
261 unsigned int chunk_size = (SEGLINE_CHUNK_SIZE >> SubdivisionLevel) + 1;
262 if (chunk_size > num_points) chunk_size = num_points;
263
264 // Chunk through the points (we increment by chunk_size - 1 because the last point of this
265 // chunk must be reused as the first point of the next chunk. This is also the reason we stop
266 // when chidx = NumPoints - 1: the last point has already been processed in the previous
267 // iteration so we don't need another one).
268 for (unsigned int chidx = 0; chidx < num_points - 1; chidx += (chunk_size - 1)) {
269 unsigned int point_cnt = num_points - chidx;
270 point_cnt = MIN(point_cnt, chunk_size);
271
272 // We use these different loop indices (which loop INSIDE a chunk) to improve readability:
273 unsigned int pidx; // Point index
274 unsigned int sidx; // Segment index
275 unsigned int iidx; // Intersection index
276
277
278 /*
279 ** Transform points in chunk from objectspace to eyespace:
280 */
281
283
284 Matrix3D view2( view[0].X,view[0].Y,view[0].Z,view[0].W,
285 view[1].X,view[1].Y,view[1].Z,view[1].W,
286 view[2].X,view[2].Y,view[2].Z,view[2].W);
287
288#ifdef ALLOW_TEMPORARIES
289 Matrix3D modelview=view2*transform;
290#else
291 Matrix3D modelview;
292 modelview.mul(view2, transform);
293#endif
294
295 VectorProcessorClass::Transform(&xformed_pts[0],
296 &points[chidx], modelview, point_cnt);
297
298
299 /*
300 ** Prepare v parameter per point - used for texture mapping (esp. tiled mapping mode)
301 */
302
303 float base_tex_v[MAX_SEGLINE_POINT_BUFFER_SIZE];
304 float u_values[2];
305 switch (map_mode) {
307 for (pidx = 0; pidx < point_cnt; pidx++) {
308 // All 0
309 base_tex_v[pidx] = 0.0f;
310 }
311 u_values[0] = 0.0f;
312 u_values[1] = 1.0f;
313 break;
315 for (pidx = 0; pidx < point_cnt; pidx++) {
316 // Increasing V
317 base_tex_v[pidx] = (float)(pidx + chidx) * TextureTileFactor;
318 }
319 u_values[0] = 0.0f;
320 u_values[1] = 0.0f;
321 break;
323 for (pidx = 0; pidx < point_cnt; pidx++) {
324 // Increasing V
325 base_tex_v[pidx] = (float)(pidx + chidx) * TextureTileFactor;
326 }
327 u_values[0] = 0.0f;
328 u_values[1] = 1.0f;
329 break;
330 }
331
332
333 /*
334 ** Fractal noise recursive subdivision:
335 ** We find the midpoint for each section, apply a random offset, and recurse. We also find
336 ** the average V coordinate of the endpoints which is the midpoint V (for tiled texture
337 ** mapping).
338 */
339
340 Vector3 xformed_subdiv_pts[MAX_SEGLINE_POINT_BUFFER_SIZE];
341 float subdiv_tex_v[MAX_SEGLINE_POINT_BUFFER_SIZE];
343 unsigned int sub_point_cnt;
344
345 Vector4 *rgbasPointer = rgbas ? &rgbas[ chidx ] : NULL;
346
347 subdivision_util(point_cnt, xformed_pts, base_tex_v, &sub_point_cnt, xformed_subdiv_pts, subdiv_tex_v, rgbasPointer, subdiv_rgbas);
348
349 // Start using subdivided points from now on
350 Vector3 *points = xformed_subdiv_pts;
351 float *tex_v = subdiv_tex_v;
352 Vector4 *diffuse = subdiv_rgbas;
353 point_cnt = sub_point_cnt;
354
355
356 /*
357 ** Calculate line segment edge planes:
358 */
359
360 // For each line segment find the two silhouette planes from eyepoint to the line segment
361 // cylinder. To simplify we do not find the tangent planes but intersect the cylinder with a
362 // plane passing through its axis and perpendicular to the eye vector, find the edges of the
363 // resulting rectangle, and create planes through these edges and the eyepoint.
364 // Note that these planes are represented as a single normal rather than a normal and a
365 // distance; this is because they pass through the origin (eyepoint) so their distance is
366 // always zero.
367
368 // Since the line has thickness, each segment has two edges. We name these 'top' and
369 // 'bottom' - note however that the top/bottom distinction does not relate to screen
370 // up/down and remains consistent throughout the segmented line.
371 enum SegmentEdge {
372 FIRST_EDGE = 0, // For loop conditions
373 TOP_EDGE = 0, // Top Edge
374 BOTTOM_EDGE = 1, // Bottom Edge
375 MAX_EDGE = 1, // For loop conditions
376 NUM_EDGES = 2 // For array allocations
377 };
378
379 bool switch_edges = false;
380
381 // We have dummy segments for "before the first point" and "after the last point" - in these
382 // segments the top and bottom edge are the same - they are a perpendicular plane defined by
383 // the endpoint vertices. This is so we can merge intersections properly for the first and
384 // last points.
385
386 struct LineSegment {
387 Vector3 StartPlane;
388 Vector3 EdgePlane[NUM_EDGES];
389 };
390
391 // # segments = numpoints + 1 (numpoints - 1, plus two dummy segments)
392 LineSegment segment[MAX_SEGLINE_POINT_BUFFER_SIZE + 1];
393
394 // Intersections. This has data for two edges (top or bottom) intersecting.
395 struct LineSegmentIntersection {
396 unsigned int PointCount; // How many points does this intersection represent
397 unsigned int NextSegmentID; // ID of segment after this intersection
398 Vector3 Direction; // Calculated intersection direction line
399 Vector3 Point; // Averaged 3D point on the line which this represents
400 float TexV; // Averaged texture V coordinate of points
401 Vector4 RGBA; // Averaged RGBA of the points
402 bool Fold; // Does the line fold over at this intersection?
403 bool Parallel; // Edges at this intersection are parallel (or almost-)
404 };
405
406 // Used to calculate the edge planes
407 float radius = Width * 0.5f;
408
409 // The number of intersections is the number of points minus 2. However, we store
410 // intersection records for the first and last point, even though they are not really
411 // intersections. The reason we do this is for the intersection merging - the vertices for
412 // the first and last points can get merged just like any other intersection. Also, we have
413 // a dummy intersection record before the first point - this is because we want "previous
414 // segments" for the first point and each intersection only has an index for the next
415 // segment.
416 LineSegmentIntersection intersection[MAX_SEGLINE_POINT_BUFFER_SIZE + 1][NUM_EDGES];
417
418 for (sidx = 1; sidx < point_cnt; sidx++) { // #segments = #points - 1 (+ 2 dummy segments)
419
420 Vector3 &curr_point = points[sidx - 1];
421 Vector3 &next_point = points[sidx];
422 if (Equal_Within_Epsilon(curr_point, next_point, 0.0001f))
423 {
424 next_point.X += 0.001f;
425 }
426
427 // We temporarily store the segment direction in the segment's StartPlane (since it is
428 // used to calculate the StartPlane later).
429 Vector3 &segdir = segment[sidx].StartPlane;
430 segdir = next_point - curr_point;
431 segdir.Normalize();
432
433 // Find nearest point on infinite line to eye (origin)
434 Vector3 nearest = curr_point + segdir * -Vector3::Dot_Product(segdir, curr_point);
435
436 // Find top and bottom points on cylinder
437 Vector3 offset;
438 Vector3::Cross_Product(segdir, nearest, &offset);
439 offset.Normalize();
440 Vector3 top = curr_point + offset * radius;
441 Vector3 bottom = curr_point + offset * -radius;
442
443 // Find planes through top/bottom points and eyepoint. In addition to the two points, we
444 // know that the planes are parallel to the line segment.
445 Vector3 top_normal;
446 Vector3::Cross_Product(top, segdir, &top_normal);
447 top_normal.Normalize();
448 segment[sidx].EdgePlane[TOP_EDGE] = top_normal;
449
450 Vector3 bottom_normal;
451 Vector3::Cross_Product(segdir, bottom, &bottom_normal);
452 bottom_normal.Normalize();
453 segment[sidx].EdgePlane[BOTTOM_EDGE] = bottom_normal;
454
455 // If the visual angle between the previous and current line segments (we use the angle
456 // between the planes defined by each line segment and the eyepoint) is less than 90
457 // degrees, switch the top and bottom edges for the current and subsequent segments and
458 // mark the intersection as having a fold
459 if (sidx > 1) {
460
461 Vector3 prev_plane;
462 Vector3::Cross_Product(points[sidx - 2], curr_point, &prev_plane);
463 prev_plane.Normalize();
464
465 Vector3 curr_plane;
466 Vector3::Cross_Product(curr_point, next_point, &curr_plane);
467 curr_plane.Normalize();
468
469 if (Vector3::Dot_Product(prev_plane, curr_plane) < 0.0f) {
470 switch_edges = !switch_edges;
471 intersection[sidx][TOP_EDGE].Fold = true;
472 intersection[sidx][BOTTOM_EDGE].Fold = true;
473 } else {
474 intersection[sidx][TOP_EDGE].Fold = false;
475 intersection[sidx][BOTTOM_EDGE].Fold = false;
476 }
477 }
478
479 if (switch_edges) {
480 // We switch signs so the normals will always point inwards
481 segment[sidx].EdgePlane[TOP_EDGE] = -bottom_normal;
482 segment[sidx].EdgePlane[BOTTOM_EDGE] = -top_normal;
483 }
484
485 }
486
487 // The two dummy segments for the clipping edges of the first and last real segments will be
488 // defined later, with the first and last intersections.
489
490
491 /*
492 ** Calculate segment edge intersections:
493 */
494
495 unsigned int numsegs = point_cnt - 1; // Doesn't include the two dummy segments
496 unsigned int num_intersections[NUM_EDGES];
497
498 // These include the 1st, last point "intersections", not the pre-first dummy intersection
499 num_intersections[TOP_EDGE] = point_cnt;
500 num_intersections[BOTTOM_EDGE] = point_cnt;
501
502 // Initialize pre-first point dummy intersection record (only NextSegmentID will be used).
503 intersection[0][TOP_EDGE].PointCount = 0; // Should never be used
504 intersection[0][TOP_EDGE].NextSegmentID = 0; // Points to first dummy segment
505 intersection[0][TOP_EDGE].Direction.Set(1,0,0); // Should never be used
506 intersection[0][TOP_EDGE].Point.Set(0,0,0); // Should never be used
507 intersection[0][TOP_EDGE].TexV = 0.0f; // Should never be used
508 intersection[0][TOP_EDGE].RGBA.Set(0, 0, 0, 0); // Should never be used
509 intersection[0][TOP_EDGE].Fold = true; // Should never be used
510 intersection[0][TOP_EDGE].Parallel = false; // Should never be used
511 intersection[0][BOTTOM_EDGE].PointCount = 0; // Should never be used
512 intersection[0][BOTTOM_EDGE].NextSegmentID = 0; // Points to first dummy segment
513 intersection[0][BOTTOM_EDGE].Point.Set(0,0,0); // Should never be used
514 intersection[0][BOTTOM_EDGE].TexV = 0.0f; // Should never be used
515 intersection[0][BOTTOM_EDGE].RGBA.Set(0, 0, 0, 0); // Should never be used
516 intersection[0][BOTTOM_EDGE].Direction.Set(1,0,0); // Should never be used
517 intersection[0][BOTTOM_EDGE].Fold = true; // Should never be used
518 intersection[0][BOTTOM_EDGE].Parallel = false; // Should never be used
519
520 // Initialize first point "intersection" record.
521 intersection[1][TOP_EDGE].PointCount = 1;
522 intersection[1][TOP_EDGE].NextSegmentID = 1;
523 intersection[1][TOP_EDGE].Point = points[0];
524 intersection[1][TOP_EDGE].TexV = tex_v[0];
525 intersection[1][TOP_EDGE].RGBA = diffuse[0];
526 intersection[1][TOP_EDGE].Fold = true;
527 intersection[1][TOP_EDGE].Parallel = false;
528 intersection[1][BOTTOM_EDGE].PointCount = 1;
529 intersection[1][BOTTOM_EDGE].NextSegmentID = 1;
530 intersection[1][BOTTOM_EDGE].Point = points[0];
531 intersection[1][BOTTOM_EDGE].TexV = tex_v[0];
532 intersection[1][BOTTOM_EDGE].RGBA = diffuse[0];
533 intersection[1][BOTTOM_EDGE].Fold = true;
534 intersection[1][BOTTOM_EDGE].Parallel = false;
535
536 // Find closest point to 1st top/bottom segment edge plane, and convert to direction vector
537 // and dummy segment edge plane.
538
539 Vector3 top;
540 Vector3 bottom;
541
542 Vector3 &first_point = points[0];
543 Vector3 *first_plane = &(segment[1].EdgePlane[0]);
544 top = first_point - first_plane[TOP_EDGE] * Vector3::Dot_Product(first_plane[TOP_EDGE], first_point);
545 top.Normalize();
546 intersection[1][TOP_EDGE].Direction = top;
547 bottom = first_point - first_plane[BOTTOM_EDGE] * Vector3::Dot_Product(first_plane[BOTTOM_EDGE], first_point);
548 bottom.Normalize();
549 intersection[1][BOTTOM_EDGE].Direction = bottom;
550
551 Vector3 segdir = points[1] - points[0];
552 segdir.Normalize(); // Is this needed? Probably not - remove later when all works
553 Vector3 start_pl;
554 Vector3::Cross_Product(top, bottom, &start_pl);
555 start_pl.Normalize();
556 float dp = Vector3::Dot_Product(segdir, start_pl);
557 if (dp > 0.0f) {
558 segment[0].StartPlane = segment[0].EdgePlane[TOP_EDGE] = segment[0].EdgePlane[BOTTOM_EDGE] = start_pl;
559 } else {
560 segment[0].StartPlane = segment[0].EdgePlane[TOP_EDGE] = segment[0].EdgePlane[BOTTOM_EDGE] = -start_pl;
561 }
562
563 // Initialize StartPlane for the first "real" segment
564 segment[1].StartPlane = segment[0].StartPlane;
565
566 // Initialize last point "intersection" record.
567 unsigned int last_isec = num_intersections[TOP_EDGE]; // Same # top, bottom intersections
568
569 intersection[last_isec][TOP_EDGE].PointCount = 1;
570 intersection[last_isec][TOP_EDGE].NextSegmentID = numsegs + 1; // Last dummy segment
571 intersection[last_isec][TOP_EDGE].Point = points[point_cnt - 1];
572 intersection[last_isec][TOP_EDGE].TexV = tex_v[point_cnt - 1];
573 intersection[last_isec][TOP_EDGE].RGBA = diffuse[point_cnt - 1];
574 intersection[last_isec][TOP_EDGE].Fold = true;
575 intersection[last_isec][TOP_EDGE].Parallel = false;
576 intersection[last_isec][BOTTOM_EDGE].PointCount = 1;
577 intersection[last_isec][BOTTOM_EDGE].NextSegmentID = numsegs + 1;// Last dummy segment
578 intersection[last_isec][BOTTOM_EDGE].Point = points[point_cnt - 1];
579 intersection[last_isec][BOTTOM_EDGE].TexV = tex_v[point_cnt - 1];
580 intersection[last_isec][BOTTOM_EDGE].RGBA = diffuse[point_cnt - 1];
581 intersection[last_isec][BOTTOM_EDGE].Fold = true;
582 intersection[last_isec][BOTTOM_EDGE].Parallel = false;
583
584 // Find closest point to last top/bottom segment edge plane, and convert to direction vector
585 // and dummy segment edge vector
586
587 Vector3 &last_point = points[point_cnt - 1];
588 Vector3 *last_plane = &(segment[numsegs].EdgePlane[0]);
589 top = last_point - last_plane[TOP_EDGE] * Vector3::Dot_Product(last_plane[TOP_EDGE], last_point);
590 top.Normalize();
591 intersection[last_isec][TOP_EDGE].Direction = top;
592 bottom = last_point - last_plane[BOTTOM_EDGE] * Vector3::Dot_Product(last_plane[BOTTOM_EDGE], last_point);
593 bottom.Normalize();
594 intersection[last_isec][BOTTOM_EDGE].Direction = bottom;
595
596 segdir = points[point_cnt - 1] - points[point_cnt - 2];
597 segdir.Normalize(); // Is this needed? Probably not - remove later when all works
598 Vector3::Cross_Product(top, bottom, &start_pl);
599 start_pl.Normalize();
600 dp = Vector3::Dot_Product(segdir, start_pl);
601 if (dp > 0.0f) {
602 segment[numsegs + 1].StartPlane = segment[numsegs + 1].EdgePlane[TOP_EDGE] =
603 segment[numsegs + 1].EdgePlane[BOTTOM_EDGE] = start_pl;
604 } else {
605 segment[numsegs + 1].StartPlane = segment[numsegs + 1].EdgePlane[TOP_EDGE] =
606 segment[numsegs + 1].EdgePlane[BOTTOM_EDGE] = -start_pl;
607 }
608
609 // Calculate midpoint segment intersections. There are 2 segment intersections for each
610 // point: top and bottom (due to the fact that the segments have width, so they have a top
611 // edge and a bottom edge). Note that the top/bottom distinction does not relate to screen
612 // up/down. Since each segment edge is represented by a plane passing through the origin
613 // (eyepoint), the intersection of two such is a line passing through the origin, which is
614 // represented as a normalized direction vector.
615 // We use both segment intersections to define the startplane for the segment which begins
616 // at that intersection.
617
618 float vdp;
619
620 for (iidx = 2; iidx < num_intersections[TOP_EDGE]; iidx++) {
621
622 // Relevant midpoint:
623 Vector3 &midpoint = points[iidx - 1];
624 float mid_tex_v = tex_v[iidx - 1];
625 Vector4 mid_diffuse = diffuse[iidx - 1];
626
627 // Initialize misc. fields
628 intersection[iidx][TOP_EDGE].PointCount = 1;
629 intersection[iidx][TOP_EDGE].NextSegmentID = iidx;
630 intersection[iidx][TOP_EDGE].Point = midpoint;
631 intersection[iidx][TOP_EDGE].TexV = mid_tex_v;
632 intersection[iidx][TOP_EDGE].RGBA = mid_diffuse;
633 intersection[iidx][BOTTOM_EDGE].PointCount = 1;
634 intersection[iidx][BOTTOM_EDGE].NextSegmentID = iidx;
635 intersection[iidx][BOTTOM_EDGE].Point = midpoint;
636 intersection[iidx][BOTTOM_EDGE].TexV = mid_tex_v;
637 intersection[iidx][BOTTOM_EDGE].RGBA = mid_diffuse;
638
639 // Intersection calculation: if the top/bottom planes of both adjoining segments are not
640 // very close to being parallel, intersect them to get top/bottom intersection lines. If
641 // the planes are almost parallel, pick one, find the point on the plane closest to the
642 // midpoint, and convert that point to a line direction vector.
643
644 // Top:
645 vdp = Vector3::Dot_Product(segment[iidx - 1].EdgePlane[TOP_EDGE], segment[iidx].EdgePlane[TOP_EDGE]);
646 if (fabs(vdp) < parallel_factor) {
647
648 // Not parallel - intersect planes to get line (get vector, normalize it, ensure it is
649 // pointing towards the midpoint)
650 Vector3::Cross_Product(segment[iidx - 1].EdgePlane[TOP_EDGE], segment[iidx].EdgePlane[TOP_EDGE],
651 &(intersection[iidx][TOP_EDGE].Direction));
652 intersection[iidx][TOP_EDGE].Direction.Normalize();
653 if (Vector3::Dot_Product(intersection[iidx][TOP_EDGE].Direction, midpoint) < 0.0f) {
654 intersection[iidx][TOP_EDGE].Direction = -intersection[iidx][TOP_EDGE].Direction;
655 }
656
657 intersection[iidx][TOP_EDGE].Parallel = false;
658
659 } else {
660
661 // Parallel (or almost): find point on av. plane closest to midpoint, convert to line
662
663 // Ensure average calculation is numerically stable:
664 Vector3 pl;
665 if (vdp > 0.0f) {
666 pl = segment[iidx - 1].EdgePlane[TOP_EDGE] + segment[iidx].EdgePlane[TOP_EDGE];
667 } else {
668 pl = segment[iidx - 1].EdgePlane[TOP_EDGE] - segment[iidx].EdgePlane[TOP_EDGE];
669 }
670 pl.Normalize();
671
672 intersection[iidx][TOP_EDGE].Direction = midpoint - pl * Vector3::Dot_Product(pl, midpoint);
673 intersection[iidx][TOP_EDGE].Direction.Normalize();
674
675 intersection[iidx][TOP_EDGE].Parallel = true;
676 }
677
678 // Bottom:
679 vdp = Vector3::Dot_Product(segment[iidx - 1].EdgePlane[BOTTOM_EDGE], segment[iidx].EdgePlane[BOTTOM_EDGE]);
680 if (fabs(vdp) < parallel_factor) {
681
682 // Not parallel - intersect planes to get line (get vector, normalize it, ensure it is
683 // pointing towards the midpoint)
684 Vector3::Cross_Product(segment[iidx - 1].EdgePlane[BOTTOM_EDGE], segment[iidx].EdgePlane[BOTTOM_EDGE],
685 &(intersection[iidx][BOTTOM_EDGE].Direction));
686 intersection[iidx][BOTTOM_EDGE].Direction.Normalize();
687 if (Vector3::Dot_Product(intersection[iidx][BOTTOM_EDGE].Direction, midpoint) < 0.0f) {
688 intersection[iidx][BOTTOM_EDGE].Direction = -intersection[iidx][BOTTOM_EDGE].Direction;
689 }
690
691 intersection[iidx][BOTTOM_EDGE].Parallel = false;
692
693 } else {
694
695 // Parallel (or almost): find point on av. plane closest to midpoint, convert to line
696
697 // Ensure average calculation is numerically stable:
698 Vector3 pl;
699 if (vdp > 0.0f) {
700 pl = segment[iidx - 1].EdgePlane[BOTTOM_EDGE] + segment[iidx].EdgePlane[BOTTOM_EDGE];
701 } else {
702 pl = segment[iidx - 1].EdgePlane[BOTTOM_EDGE] - segment[iidx].EdgePlane[BOTTOM_EDGE];
703 }
704 pl.Normalize();
705
706 intersection[iidx][BOTTOM_EDGE].Direction = midpoint - pl * Vector3::Dot_Product(pl, midpoint);
707 intersection[iidx][BOTTOM_EDGE].Direction.Normalize();
708
709 intersection[iidx][BOTTOM_EDGE].Parallel = true;
710 }
711
712 // Find StartPlane:
713 Vector3::Cross_Product(intersection[iidx][TOP_EDGE].Direction, intersection[iidx][BOTTOM_EDGE].Direction, &start_pl);
714 start_pl.Normalize();
715 dp = Vector3::Dot_Product(segment[iidx].StartPlane, start_pl);
716 if (dp > 0.0f) {
717 segment[iidx].StartPlane = start_pl;
718 } else {
719 segment[iidx].StartPlane = -start_pl;
720 }
721
722 } // for iidx
723
724
725 /*
726 ** Intersection merging: when an intersection is inside an adjacent segment and certain
727 ** other conditions hold true, we need to merge intersections to avoid visual glitches
728 ** caused by the polys folding over on themselves.
729 */
730
732
733 // Since we are merging the intersections in-place, we have two index variables, a "read
734 // index" and a "write index".
735 unsigned int iidx_r;
736 unsigned int iidx_w;
737
738 // The merges will be repeated in multiple passes until none are performed. The reason
739 // for this is that one merge may cause the need for another merge elsewhere.
740 bool merged = true;
741
742 while (merged) {
743
744 merged = false;
745
746 SegmentEdge edge;
747 for (edge = FIRST_EDGE; edge <= MAX_EDGE; edge = (SegmentEdge)((int)edge + 1)) {
748 // Merge top and bottom edge intersections: loop through the intersections from the
749 // first intersection to the penultimate intersection, for each intersection check
750 // if it needs to be merged with the next one (which is why the loop doesn't go all
751 // the way to the last intersection). We start at 1 because 0 is the dummy
752 // "pre-first-point" intersection.
753 unsigned int num_isects = num_intersections[edge]; // Capture here because will change inside loop
754 for (iidx_r = 1, iidx_w = 1; iidx_r < num_isects; iidx_r++, iidx_w++) {
755
756 // Check for either of two possible reasons to merge this intersection with the
757 // next: either the segment on the far side of the next intersection overlaps
758 // this intersection, or the previous segment overlaps the next intersection.
759 // Note that some other conditions need to be true as well.
760
761 // Note: iidx_r is used for anything at or after the current position, iidx_w is
762 // used for anything before the current position (previous positions have
763 // potentially already been merged).
764 // Note: iidx_r is used for anything at or after the current position, iidx_w is
765 // used for anything before the current position (previous positions have
766 // potentially already been merged).
767 LineSegmentIntersection *curr_int = &(intersection[iidx_r][edge]);
768 LineSegmentIntersection *next_int = &(intersection[iidx_r + 1][edge]);
769 LineSegmentIntersection *write_int = &(intersection[iidx_w][edge]);
770 LineSegmentIntersection *prev_int = &(intersection[iidx_w - 1][edge]);
771 LineSegment *next_seg = &(segment[next_int->NextSegmentID]);
772 LineSegment *curr_seg = &(segment[curr_int->NextSegmentID]);
773 LineSegment *prev_seg = &(segment[prev_int->NextSegmentID]);
774
775 // If this intersection is inside both the start plane and the segment edge
776 // plane of the segment after the next intersection, merge this edge
777 // intersection and the next. We repeat merging until no longer needed.
778 // NOTE - we do not merge across a fold.
779 while ( (!next_int->Fold &&
780 (Vector3::Dot_Product(curr_int->Direction, next_seg->StartPlane) > 0.0f) &&
781 (Vector3::Dot_Product(curr_int->Direction, next_seg->EdgePlane[edge]) > 0.0f )) ||
782 (!curr_int->Fold &&
783 (Vector3::Dot_Product(next_int->Direction, -curr_seg->StartPlane) > 0.0f) &&
784 (Vector3::Dot_Product(next_int->Direction, prev_seg->EdgePlane[edge]) > 0.0f )) ) {
785
786 // First calculate location of merged intersection - this is so we can abort
787 // the merge if it yields funky results.
788
789 // Find mean point (weighted so all points have same weighting)
790 unsigned int new_count = curr_int->PointCount + next_int->PointCount;
791 float oo_new_count = 1.0f / (float)new_count;
792 float curr_factor = oo_new_count * (float)curr_int->PointCount;
793 float next_factor = oo_new_count * (float)curr_int->PointCount;
794 Vector3 new_point = curr_int->Point * curr_factor + next_int->Point * next_factor;
795 float new_tex_v = curr_int->TexV * curr_factor + next_int->TexV * next_factor;
796 Vector4 new_diffuse = curr_int->RGBA * curr_factor + next_int->RGBA * next_factor;
797
798 // Calculate new intersection direction by intersecting prev_seg with next_seg
799 bool new_parallel;
800 Vector3 new_direction;
801 vdp = Vector3::Dot_Product(prev_seg->EdgePlane[edge], next_seg->EdgePlane[edge]);
802 if (fabs(vdp) < parallel_factor) {
803
804 // Not parallel - intersect planes to get line (get vector, normalize it,
805 // ensure it is pointing towards the current point)
806 Vector3::Cross_Product(prev_seg->EdgePlane[edge], next_seg->EdgePlane[edge], &new_direction);
807 new_direction.Normalize();
808 if (Vector3::Dot_Product(new_direction, new_point) < 0.0f) {
809 new_direction = -new_direction;
810 }
811
812 new_parallel = false;
813
814 } else {
815
816 // Parallel (or almost). If the current intersection is not parallel, take
817 // the average plane and intersect it with the skipped plane. If the
818 // current intersection is parallel, find the average plane, and find the
819 // direction vector on it closest to the current intersections direction
820 // vector.
821
822 // Ensure average calculation is numerically stable:
823 Vector3 pl;
824 if (vdp > 0.0f) {
825 pl = prev_seg->EdgePlane[edge] + next_seg->EdgePlane[edge];
826 } else {
827 pl = prev_seg->EdgePlane[edge] - next_seg->EdgePlane[edge];
828 }
829 pl.Normalize();
830
831 if (curr_int->Parallel) {
832 new_direction = new_direction - pl * Vector3::Dot_Product(pl, new_direction);
833 new_direction.Normalize();
834 } else {
835 Vector3::Cross_Product(curr_seg->EdgePlane[edge], pl, &new_direction);
836 new_direction.Normalize();
837 }
838
839 new_parallel = true;
840 }
841
842 // Now check to see if the merge caused any funky results - if so abort it.
843 // Currently we check to see if the distance of the direction from the two
844 // points is larger than the radius times the merge_abort factor.
845 if (MergeAbortFactor > 0.0f) {
846 float abort_dist = radius * MergeAbortFactor;
847 float abort_dist2 = abort_dist * abort_dist;
848 Vector3 diff_curr = curr_int->Point -
849 new_direction * Vector3::Dot_Product(curr_int->Point, new_direction);
850 if (diff_curr.Length2() > abort_dist2) break;
851 Vector3 next_curr = next_int->Point -
852 new_direction * Vector3::Dot_Product(next_int->Point, new_direction);
853 if (next_curr.Length2() > abort_dist2) break;
854 }
855
856 // Merge edge intersections (curr_int and next_int) into curr_int
857 merged = true;
858
859 curr_int->Direction = new_direction;
860 curr_int->Parallel = new_parallel;
861 curr_int->Point = new_point;
862 curr_int->TexV = new_tex_v;
863 curr_int->RGBA = new_diffuse;
864 curr_int->PointCount = new_count;
865 curr_int->NextSegmentID = next_int->NextSegmentID;
866 curr_int->Fold = curr_int->Fold || next_int->Fold;
867
868 // Decrement number of edge intersections
869 num_intersections[edge]--;
870
871 // Advance iidx_r to shift subsequent entries backwards in result.
872 iidx_r++;
873
874 // If we are at the end then break:
875 if (iidx_r == num_isects) {
876 break;
877 }
878
879 // Advance next_int and next_seg.
880 next_int = &(intersection[iidx_r + 1][edge]);
881 next_seg = &(segment[next_int->NextSegmentID]);
882
883 } // while <merging needed>
884
885 // Copy from "read index" to "write index"
886 write_int->PointCount = curr_int->PointCount;
887 write_int->NextSegmentID = curr_int->NextSegmentID;
888 write_int->Point = curr_int->Point;
889 write_int->TexV = curr_int->TexV;
890 write_int->RGBA = curr_int->RGBA;
891 write_int->Direction = curr_int->Direction;
892 write_int->Fold = curr_int->Fold;
893
894 } // for iidx
895
896 // If iidx_r is exactly equal to num_isects (rather than being larger by one) at this
897 // point, this means that the last intersection was not merged with the previous one. In
898 // this case, we need to do one last copy:
899 if (iidx_r == num_isects) {
900 LineSegmentIntersection *write_int = &(intersection[iidx_w][edge]);
901 LineSegmentIntersection *curr_int = &(intersection[iidx_r][edge]);
902 write_int->PointCount = curr_int->PointCount;
903 write_int->NextSegmentID = curr_int->NextSegmentID;
904 write_int->Point = curr_int->Point;
905 write_int->TexV = curr_int->TexV;
906 write_int->RGBA = curr_int->RGBA;
907 write_int->Direction = curr_int->Direction;
908 write_int->Fold = curr_int->Fold;
909 }
910
911#ifdef ENABLE_WWDEBUGGING
912 // Testing code - ensure total PointCount fits the number of points
913 unsigned int total_cnt = 0;
914 for (unsigned int nidx = 0; nidx <= num_intersections[edge]; nidx++) {
915 total_cnt += intersection[nidx][edge].PointCount;
916 }
917 assert(total_cnt == point_cnt);
918#endif
919
920 } // for edge
921 } // while (merged)
922 } // if (Is_Merge_Intersections())
923
924 /*
925 ** Find vertex positions, generate vertices and triangles:
926 ** Since we can have top/bottom intersections merged, we need to skip points if both the top
927 ** and bottom intersections are merged, generate triangle fans if one of the sides is merged
928 ** and the other isnt, and generate triangle strips otherwise.
929 */
930
931 // Configure vertex array and setup renderer.
932 unsigned int vnum = num_intersections[TOP_EDGE] + num_intersections[BOTTOM_EDGE];
933 VertexFormatXYZDUV1 *vArray = getVertexBuffer(vnum);
935
936 // Vertex and triangle indices
937 unsigned int vidx = 0;
938 unsigned int tidx = 0;
939
940// GENERALIZE FOR WHEN NO TEXTURE (DO NOT SET UV IN THESE CASES? NEED TO GENERALIZE FOR DIFFERENT TEXTURING MODES ANYWAY).
941
942 // "Prime the pump" with two vertices (pick nearest point on each direction line):
943 Vector3 &top_dir = intersection[1][TOP_EDGE].Direction;
944 top = top_dir * Vector3::Dot_Product(points[0], top_dir);
945 Vector3 &bottom_dir = intersection[1][BOTTOM_EDGE].Direction;
946 bottom = bottom_dir * Vector3::Dot_Product(points[0], bottom_dir);
947 vArray[vidx].x = top.X;
948 vArray[vidx].y = top.Y;
949 vArray[vidx].z = top.Z;
950 vArray[vidx].diffuse = DX8Wrapper::Convert_Color(intersection[1][TOP_EDGE].RGBA);
951 vArray[vidx].u1 = u_values[0] + uv_offset.X;
952 vArray[vidx].v1 = intersection[1][TOP_EDGE].TexV + uv_offset.Y;
953 vidx++;
954 vArray[vidx].x = bottom.X;
955 vArray[vidx].y = bottom.Y;
956 vArray[vidx].z = bottom.Z;
957 vArray[vidx].diffuse = DX8Wrapper::Convert_Color(intersection[1][BOTTOM_EDGE].RGBA);
958 vArray[vidx].u1 = u_values[1] + uv_offset.X;
959 vArray[vidx].v1 = intersection[1][BOTTOM_EDGE].TexV + uv_offset.Y;
960 vidx++;
961
962 unsigned int last_top_vidx = 0;
963 unsigned int last_bottom_vidx = 1;
964
965 // Loop over intersections, create new vertices and triangles.
966 unsigned int top_int_idx = 1; // Skip "pre-first-point" dummy intersection
967 unsigned int bottom_int_idx = 1; // Skip "pre-first-point" dummy intersection
968 pidx = 0;
969 unsigned int residual_top_points = intersection[1][TOP_EDGE].PointCount;
970 unsigned int residual_bottom_points = intersection[1][BOTTOM_EDGE].PointCount;
971
972 // Reduce both pointcounts by the same amount so the smaller one is 1 (skip points)
973 unsigned int delta = MIN(residual_top_points, residual_bottom_points) - 1;
974 residual_top_points -= delta;
975 residual_bottom_points -= delta;
976 pidx += delta;
977
978 for (; ; ) {
979
980 if (residual_top_points == 1 && residual_bottom_points == 1) {
981 // Advance both intersections, creating a tristrip segment
982 v_index_array[tidx].I = last_top_vidx;
983 v_index_array[tidx].J = last_bottom_vidx;
984 v_index_array[tidx].K = vidx;
985 tidx++;
986 v_index_array[tidx].I = last_bottom_vidx;
987 v_index_array[tidx].J = vidx + 1;
988 v_index_array[tidx].K = vidx;
989 tidx++;
990 last_top_vidx = vidx;
991 last_bottom_vidx = vidx + 1;
992
993 // Advance both intersections.
994 top_int_idx++;
995 bottom_int_idx++;
996 residual_top_points = intersection[top_int_idx][TOP_EDGE].PointCount;
997 residual_bottom_points = intersection[bottom_int_idx][BOTTOM_EDGE].PointCount;
998
999 // Advance point index (must do here because the new point index is used below):
1000 pidx++;
1001
1002 // Generate two vertices for next point by picking nearest point on each direction line
1003 Vector3 &top_dir = intersection[top_int_idx][TOP_EDGE].Direction;
1004 top = top_dir * Vector3::Dot_Product(points[pidx], top_dir);
1005 Vector3 &bottom_dir = intersection[bottom_int_idx][BOTTOM_EDGE].Direction;
1006 bottom = bottom_dir * Vector3::Dot_Product(points[pidx], bottom_dir);
1007
1008 vArray[vidx].x = top.X;
1009 vArray[vidx].y = top.Y;
1010 vArray[vidx].z = top.Z;
1011 vArray[vidx].diffuse = DX8Wrapper::Convert_Color(intersection[top_int_idx][TOP_EDGE].RGBA);
1012 vArray[vidx].u1 = u_values[0] + uv_offset.X;
1013 vArray[vidx].v1 = intersection[top_int_idx][TOP_EDGE].TexV + uv_offset.Y;
1014 vidx++;
1015 vArray[vidx].x = bottom.X;
1016 vArray[vidx].y = bottom.Y;
1017 vArray[vidx].z = bottom.Z;
1018 vArray[vidx].diffuse = DX8Wrapper::Convert_Color(intersection[bottom_int_idx][BOTTOM_EDGE].RGBA);
1019 vArray[vidx].u1 = u_values[1] + uv_offset.X;
1020 vArray[vidx].v1 = intersection[bottom_int_idx][BOTTOM_EDGE].TexV + uv_offset.Y;
1021 vidx++;
1022 } else {
1023 // Exactly one of the pointcounts is greater than one - advance it and draw one triangle
1024 if (residual_top_points > 1) {
1025 // Draw one triangle (fan segment)
1026 v_index_array[tidx].I = last_top_vidx;
1027 v_index_array[tidx].J = last_bottom_vidx;
1028 v_index_array[tidx].K = vidx;
1029 tidx++;
1030 last_bottom_vidx = vidx;
1031
1032 // Advance bottom intersection only
1033 residual_top_points--;
1034 bottom_int_idx++;
1035 residual_bottom_points = intersection[bottom_int_idx][BOTTOM_EDGE].PointCount;
1036
1037 // Advance point index (must do here because the new point index is used below):
1038 pidx++;
1039
1040 // Generate bottom vertex by picking nearest point on bottom direction line
1041 Vector3 &bottom_dir = intersection[bottom_int_idx][BOTTOM_EDGE].Direction;
1042 bottom = bottom_dir * Vector3::Dot_Product(points[pidx], bottom_dir);
1043
1044 vArray[vidx].x = bottom.X;
1045 vArray[vidx].y = bottom.Y;
1046 vArray[vidx].z = bottom.Z;
1047 vArray[vidx].diffuse = DX8Wrapper::Convert_Color(intersection[bottom_int_idx][BOTTOM_EDGE].RGBA);
1048 vArray[vidx].u1 = u_values[1] + uv_offset.X;
1049 vArray[vidx].v1 = intersection[bottom_int_idx][BOTTOM_EDGE].TexV + uv_offset.Y;
1050 vidx++;
1051 } else {
1052
1053 // residual_bottom_points > 1
1054
1055 // Draw one triangle (fan segment)
1056 v_index_array[tidx].I = last_top_vidx;
1057 v_index_array[tidx].J = last_bottom_vidx;
1058 v_index_array[tidx].K = vidx;
1059 tidx++;
1060 last_top_vidx = vidx;
1061
1062 // Advance top intersection only
1063 residual_bottom_points--;
1064 top_int_idx++;
1065 residual_top_points = intersection[top_int_idx][TOP_EDGE].PointCount;
1066
1067 // Advance point index (must do here because the new point index is used below):
1068 pidx++;
1069
1070 // Generate top vertex by picking nearest point on top direction line
1071 Vector3 &top_dir = intersection[top_int_idx][TOP_EDGE].Direction;
1072 top = top_dir * Vector3::Dot_Product(points[pidx], top_dir);
1073 vArray[vidx].x = top.X;
1074 vArray[vidx].y = top.Y;
1075 vArray[vidx].z = top.Z;
1076 vArray[vidx].diffuse = DX8Wrapper::Convert_Color(intersection[top_int_idx][TOP_EDGE].RGBA);
1077 vArray[vidx].u1 = u_values[0] + uv_offset.X;
1078 vArray[vidx].v1 = intersection[top_int_idx][TOP_EDGE].TexV + uv_offset.Y;
1079 vidx++;
1080 }
1081 }
1082
1083 // Reduce both pointcounts by the same amount so the smaller one is 1 (skip points)
1084 delta = MIN(residual_top_points, residual_bottom_points) - 1;
1085 residual_top_points -= delta;
1086 residual_bottom_points -= delta;
1087 pidx += delta;
1088
1089 // Exit conditions
1090 if ( (top_int_idx >= num_intersections[TOP_EDGE] && residual_top_points == 1) ||
1091 (bottom_int_idx >= num_intersections[BOTTOM_EDGE] && residual_bottom_points == 1)) {
1092 // Debugging check - if either intersection index is before end, both of them should be
1093 // and the points should be before the end.
1094 assert(top_int_idx == num_intersections[TOP_EDGE]);
1095 assert(bottom_int_idx == num_intersections[BOTTOM_EDGE]);
1096 assert(pidx == point_cnt - 1);
1097 break;
1098 }
1099 }
1100
1101 /*
1102 ** Set color, opacity, vertex flags:
1103 */
1104
1105 // If color is not white or opacity not 100%, enable gradient in shader and in renderer - otherwise disable.
1106 unsigned int rgba;
1107 rgba=DX8Wrapper::Convert_Color(Color,Opacity);
1108 bool rgba_all=(rgba==0xFFFFFFFF);
1109
1110 // Enable sorting if sorting has not been disabled and line is translucent and alpha testing is not enabled.
1111 bool sorting = (!Is_Sorting_Disabled()) && (Shader.Get_Dst_Blend_Func() != ShaderClass::DSTBLEND_ZERO && Shader.Get_Alpha_Test() == ShaderClass::ALPHATEST_DISABLE);
1112
1113 ShaderClass shader = Shader;
1115
1116 VertexMaterialClass *mat;
1117
1118 // if there's a default color or an rgba array modulate
1119 if (!rgba_all || (rgba != 0) ) {
1122 } else {
1123 // othewise it's texture only
1126 }
1127
1128 // If Texture is non-NULL enable texturing in shader - otherwise disable.
1129 if (Texture) {
1131 } else {
1133 }
1134
1135
1136 /*
1137 ** Render
1138 */
1139
1141 // Copy in the data to the VB
1142 {
1144 unsigned int i;
1145 unsigned char *vb=(unsigned char*)Lock.Get_Formatted_Vertex_Array();
1146 const FVFInfoClass& fvfinfo=Verts.FVF_Info();
1147
1148 const unsigned int verticesOffset = fvfinfo.Get_Location_Offset();
1149 const unsigned diffuseOffset = fvfinfo.Get_Diffuse_Offset();
1150 const unsigned textureOffset = fvfinfo.Get_Tex_Offset(0);
1151 const unsigned vbSize = fvfinfo.Get_FVF_Size();
1152
1153 for (i=0; i<vnum; i++)
1154 {
1155 // Copy Locations
1156 Vector3 *vertex = reinterpret_cast<Vector3 *>(vb + verticesOffset);
1157 vertex->X = vArray[i].x;
1158 vertex->Y = vArray[i].y;
1159 vertex->Z = vArray[i].z;
1160 *reinterpret_cast<unsigned int *>(vb + diffuseOffset) = vArray[i].diffuse;
1161 Vector2 *texture = reinterpret_cast<Vector2 *>(vb + textureOffset);
1162 texture->U = vArray[i].u1;
1163 texture->V = vArray[i].v1;
1164 vb += vbSize;
1165 }
1166 } // copy
1167
1169 {
1170 unsigned int i;
1171 DynamicIBAccessClass::WriteLockClass lock(&ib_access);
1172 unsigned short* inds=lock.Get_Index_Array();
1173
1174 try {
1175 for (i=0; i<tidx; i++)
1176 {
1177 *inds++=v_index_array[i].I;
1178 *inds++=v_index_array[i].J;
1179 *inds++=v_index_array[i].K;
1180 }
1182 } catch(...) {
1184 }
1185 }
1186
1187 DX8Wrapper::Set_Index_Buffer(ib_access,0);
1190 DX8Wrapper::Set_Texture(0,Texture);
1191 DX8Wrapper::Set_Shader(shader);
1192
1193 if (sorting) {
1194 SortingRendererClass::Insert_Triangles(obj_sphere,0,tidx,0,vnum);
1195 } else {
1196 DX8Wrapper::Draw_Triangles(0,tidx,0,vnum);
1197 }
1198
1199 REF_PTR_RELEASE(mat);
1200
1201 } // Chunking loop
1202
1203 DX8Wrapper::Set_Transform(D3DTS_VIEW,view);
1204
1205}
1206
1207
1208void SegLineRendererClass::subdivision_util(unsigned int point_cnt, const Vector3 *xformed_pts,
1209 const float *base_tex_v, unsigned int *p_sub_point_cnt, Vector3 *xformed_subdiv_pts,
1210 float *subdiv_tex_v, Vector4 *base_diffuse, Vector4 *subdiv_diffuse)
1211{
1212 // CAUTION: freezing the random offsets will make it more readily apparent that the offsets
1213 // are in camera space rather than worldspace.
1214 int freeze_random = Is_Freeze_Random();
1215 Random3Class randomize;
1216 const float oo_int_max = 1.0f / (float)INT_MAX;
1217 Vector3SolidBoxRandomizer randomizer(Vector3(1,1,1));
1218 Vector3 randvec(0,0,0);
1219 unsigned int sub_pidx = 0;
1220
1221 struct SegLineSubdivision {
1222 Vector3 StartPos;
1223 Vector3 EndPos;
1224 float StartTexV; // V texture coordinate of start point
1225 float EndTexV; // V texture coordinate of end point
1226 Vector4 StartDiffuse;
1227 Vector4 EndDiffuse;
1228 float Rand;
1229 unsigned int Level; // Subdivision level
1230 };
1231
1232 SegLineSubdivision stack[2 * MAX_SEGLINE_SUBDIV_LEVELS]; // Maximum number needed
1233 int tos = 0;
1234
1235 for (unsigned int pidx = 0; pidx < point_cnt - 1; pidx++) {
1236
1237 // Subdivide the (pidx, pidx + 1) segment. Produce pidx and all subdivided points up to
1238 // (not including) pidx + 1.
1239 tos = 0;
1240 stack[0].StartPos = xformed_pts[pidx];
1241 stack[0].EndPos = xformed_pts[pidx + 1];
1242 stack[0].StartTexV = base_tex_v[pidx];
1243 stack[0].EndTexV = base_tex_v[pidx + 1];
1244
1245 if (base_diffuse) {
1246 stack[0].StartDiffuse = base_diffuse[pidx];
1247 stack[0].EndDiffuse = base_diffuse[pidx+1];
1248 } else {
1249 stack[0].StartDiffuse.Set(Color.X, Color.Y, Color.Z, Opacity);
1250 stack[0].EndDiffuse = stack[0].StartDiffuse;
1251 }
1252
1253 stack[0].Rand = NoiseAmplitude;
1254 stack[0].Level = 0;
1255
1256 for (; tos >= 0;) {
1257 if (stack[tos].Level == SubdivisionLevel) {
1258 // Generate point location and texture V coordinate
1259 xformed_subdiv_pts[sub_pidx] = stack[tos].StartPos;
1260 subdiv_tex_v[sub_pidx] = stack[tos].StartTexV;
1261 subdiv_diffuse[sub_pidx] = stack[tos].StartDiffuse;
1262
1263 sub_pidx = sub_pidx + 1;
1264
1265 // Pop
1266 tos--;
1267 } else {
1268 // Recurse down: pop existing entry and push two subdivided ones.
1269 if (freeze_random) {
1270 randvec.Set(randomize * oo_int_max, randomize * oo_int_max, randomize * oo_int_max);
1271 } else {
1272 randomizer.Get_Vector(randvec);
1273 }
1274 stack[tos + 1].StartPos = stack[tos].StartPos;
1275 stack[tos + 1].EndPos = (stack[tos].StartPos + stack[tos].EndPos) * 0.5f + randvec * stack[tos].Rand;
1276 stack[tos + 1].StartTexV = stack[tos].StartTexV;
1277 stack[tos + 1].EndTexV = (stack[tos].StartTexV + stack[tos].EndTexV) * 0.5f;
1278 stack[tos + 1].StartDiffuse = stack[tos].StartDiffuse;
1279 stack[tos + 1].EndDiffuse = (stack[tos].StartDiffuse + stack[tos].EndDiffuse) * 0.5f;
1280 stack[tos + 1].Rand = stack[tos].Rand * 0.5f;
1281 stack[tos + 1].Level = stack[tos].Level + 1;
1282 stack[tos].StartPos = stack[tos + 1].EndPos;
1283 // stack[tos].EndPos already has the right value
1284 stack[tos].StartTexV = stack[tos + 1].EndTexV;
1285 // stack[tos].EndTexV already has the right value
1286 stack[tos].Rand = stack[tos + 1].Rand;
1287 stack[tos].Level = stack[tos + 1].Level;
1288 tos++;
1289 }
1290 }
1291 }
1292 // Last point
1293 xformed_subdiv_pts[sub_pidx] = xformed_pts[point_cnt - 1];
1294 subdiv_tex_v[sub_pidx] = base_tex_v[point_cnt - 1];
1295 if (base_diffuse) {
1296 subdiv_diffuse[sub_pidx] = base_diffuse[point_cnt - 1];
1297 } else {
1298 subdiv_diffuse[sub_pidx].Set(Color.X, Color.Y, Color.Z, Opacity);
1299 }
1300
1301 sub_pidx = sub_pidx + 1;
1302
1303 // Output:
1304 *p_sub_point_cnt = sub_pidx;
1305}
1306
1308{
1309 Width *= scale;
1310 NoiseAmplitude *= scale;
1311}
1312
1313VertexFormatXYZDUV1 *SegLineRendererClass::getVertexBuffer(unsigned int number)
1314{
1315 // TODO: use a stl vector instead of our own array.
1316 if (number > m_vertexBufferSize)
1317 {
1318 unsigned int numberToAlloc = number + (number >> 1);
1319 delete [] m_vertexBuffer;
1320 m_vertexBuffer = W3DNEWARRAY VertexFormatXYZDUV1[numberToAlloc];
1321 m_vertexBufferSize = numberToAlloc;
1322 }
1323
1324 return m_vertexBuffer;
1325}
#define NULL
Definition BaseType.h:92
Color scale(const Color &a, const Color &b)
Definition GameMtl.cpp:722
#define W3D_ELINE_FREEZE_RANDOM
Definition w3d_file.h:1926
#define W3D_ELINE_DISABLE_SORTING
Definition w3d_file.h:1927
#define W3D_ELINE_MERGE_INTERSECTIONS
Definition w3d_file.h:1925
#define W3D_ELINE_TEXTURE_MAP_MODE_MASK
Definition w3d_file.h:1929
#define W3D_ELINE_TEXTURE_MAP_MODE_OFFSET
Definition w3d_file.h:1931
#define W3D_ELINE_UNIFORM_LENGTH_TEXTURE_MAP
Definition w3d_file.h:1933
#define W3D_ELINE_UNIFORM_WIDTH_TEXTURE_MAP
Definition w3d_file.h:1932
#define W3D_ELINE_END_CAPS
Definition w3d_file.h:1928
#define W3D_ELINE_TILED_TEXTURE_MAP
Definition w3d_file.h:1934
#define W3DNEWARRAY
Definition always.h:110
#define MIN(a, b)
Definition always.h:189
#define MAX(a, b)
Definition always.h:185
static void Set_Vertex_Buffer(const VertexBufferClass *vb, unsigned stream=0)
static void Set_Texture(unsigned stage, TextureBaseClass *texture)
static void Set_Index_Buffer(const IndexBufferClass *ib, unsigned short index_base_offset)
static Vector4 Convert_Color(unsigned color)
Definition dx8wrapper.h:958
static void Draw_Triangles(unsigned buffer_type, unsigned short start_index, unsigned short polygon_count, unsigned short min_vertex_index, unsigned short vertex_count)
static void Set_Material(const VertexMaterialClass *material)
static void Set_Shader(const ShaderClass &shader)
static void Get_Transform(D3DTRANSFORMSTATETYPE transform, Matrix4x4 &m)
static void Set_Transform(D3DTRANSFORMSTATETYPE transform, const Matrix4x4 &m)
VertexFormatXYZNDUV2 * Get_Formatted_Vertex_Array()
const FVFInfoClass & FVF_Info() const
unsigned Get_Tex_Offset(unsigned int n) const
Definition dx8fvf.h:274
unsigned Get_FVF_Size() const
Definition dx8fvf.h:280
unsigned Get_Diffuse_Offset() const
Definition dx8fvf.h:277
unsigned Get_Location_Offset() const
Definition dx8fvf.h:269
void mul(const Matrix3D &a, const Matrix3D &b)
Definition matrix3d.h:1572
void Render(RenderInfoClass &rinfo, const Matrix3D &transform, unsigned int point_count, Vector3 *points, const SphereClass &obj_sphere, Vector4 *rgbas=0)
TextureMapMode Get_Texture_Mapping_Mode(void) const
void Set_Texture_Tile_Factor(float factor)
void Set_Texture(TextureClass *texture)
void Set_Texture_Mapping_Mode(TextureMapMode mode)
void Set_Current_Subdivision_Level(unsigned int lv)
int Is_Freeze_Random(void) const
SegLineRendererClass & operator=(const SegLineRendererClass &that)
void Set_End_Caps(int onoff)
void Set_Noise_Amplitude(float amplitude)
void Set_Merge_Abort_Factor(float factor)
void Set_Disable_Sorting(int onoff)
void Set_Merge_Intersections(int onoff)
void Scale(float scale)
int Is_Merge_Intersections(void) const
void Init(const W3dEmitterLinePropertiesStruct &props)
TextureClass * Get_Texture(void) const
void Set_Freeze_Random(int onoff)
void Set_UV_Offset_Rate(const Vector2 &rate)
void Set_Current_UV_Offset(const Vector2 &offset)
int Is_Sorting_Disabled(void) const
void Set_Texturing(TexturingType x)
Definition shader.h:335
@ GRADIENT_DISABLE
Definition shader.h:192
@ GRADIENT_MODULATE
Definition shader.h:193
void Set_Cull_Mode(CullModeType x)
Definition shader.h:329
void Set_Primary_Gradient(PriGradientType x)
Definition shader.h:332
@ TEXTURING_DISABLE
Definition shader.h:219
@ TEXTURING_ENABLE
Definition shader.h:220
@ CULL_MODE_DISABLE
Definition shader.h:158
@ ALPHATEST_DISABLE
Definition shader.h:96
@ DSTBLEND_ZERO
Definition shader.h:172
static void Insert_Triangles(const SphereClass &bounding_sphere, unsigned short start_index, unsigned short polygon_count, unsigned short min_vertex_index, unsigned short vertex_count)
float V
Definition vector2.h:80
float Y
Definition vector2.h:79
float X
Definition vector2.h:74
float U
Definition vector2.h:75
static WWINLINE float Dot_Product(const Vector3 &a, const Vector3 &b)
Definition vector3.h:293
float X
Definition vector3.h:90
WWINLINE float Length2(void) const
Definition vector3.h:469
float Z
Definition vector3.h:92
float Y
Definition vector3.h:91
void Normalize(void)
Definition vector3.h:417
static WWINLINE void Cross_Product(const Vector3 &a, const Vector3 &b, Vector3 *result)
Definition vector3.h:374
unsigned short K
Definition Vector3i.h:102
unsigned short J
Definition Vector3i.h:101
unsigned short I
Definition Vector3i.h:100
WWINLINE void Set(float x, float y, float z, float w)
Definition vector4.h:80
static void Transform(Vector3 *dst, const Vector3 *src, const Matrix3D &matrix, const int count)
Definition vp.cpp:80
static VertexMaterialClass * Get_Preset(PresetType type)
Definition ww3d.h:78
static unsigned int Get_Sync_Time(void)
Definition ww3d.h:172
int IndexBufferExceptionFunc(void)
const unsigned dynamic_fvf_type
@ BUFFER_TYPE_DYNAMIC_DX8
Definition dx8wrapper.h:90
@ BUFFER_TYPE_DYNAMIC_SORTING
Definition dx8wrapper.h:91
#define W(x)
Definition generals.cpp:204
Vector3i16 TriIndex
WWINLINE bool Equal_Within_Epsilon(const Quaternion &a, const Quaternion &b, float epsilon)
Definition quat.h:283
#define REF_PTR_RELEASE(x)
Definition refcount.h:80
#define REF_PTR_SET(dst, src)
Definition refcount.h:79
#define MAX_SEGLINE_POINT_BUFFER_SIZE
#define SEGLINE_CHUNK_SIZE
#define MAX_SEGLINE_POLY_BUFFER_SIZE
#define MAX_SEGLINE_SUBDIV_LEVELS
unsigned diffuse
Definition dx8fvf.h:157
#define WWDEBUG_SAY(x)
Definition wwdebug.h:114