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