/* ** Command & Conquer Renegade(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ /*********************************************************************************************** *** 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 *** *********************************************************************************************** * * * Project Name : G * * * * $Archive:: /VSS_Sync/ww3d2/segline.cpp $* * * * $Author:: Vss_sync $* * * * $Modtime:: 8/29/01 7:29p $* * * * $Revision:: 23 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "segline.h" #include "ww3d.h" #include "rinfo.h" #include "predlod.h" #include "v3_rnd.h" #include "texture.h" #include "coltest.h" #include "w3d_file.h" #include "texture.h" #include "dx8wrapper.h" #include "vp.h" #include "vector3i.h" #include "sortingrenderer.h" static SegLineRendererClass _LineRenderer; /* ** SegmentedLineClass implementation: */ SegmentedLineClass::SegmentedLineClass(void) : MaxSubdivisionLevels(0), NormalizedScreenArea(0.0f) { } SegmentedLineClass::SegmentedLineClass(const SegmentedLineClass & src) : MaxSubdivisionLevels(src.MaxSubdivisionLevels), NormalizedScreenArea(src.NormalizedScreenArea), PointLocations(src.PointLocations), LineRenderer(src.LineRenderer) { } SegmentedLineClass & SegmentedLineClass::operator = (const SegmentedLineClass &that) { RenderObjClass::operator = (that); if (this != &that) { MaxSubdivisionLevels = that.MaxSubdivisionLevels; NormalizedScreenArea = that.NormalizedScreenArea; PointLocations = that.PointLocations; LineRenderer = that.LineRenderer; } return * this; } SegmentedLineClass::~SegmentedLineClass(void) { } void SegmentedLineClass::Reset_Line(void) { LineRenderer.Reset_Line(); } // These are segment points, and include the start and end point of the // entire line. Therefore there must be at least two. void SegmentedLineClass::Set_Points(unsigned int num_points, Vector3 *locs) { if (num_points < 2 || !locs) { WWASSERT(0); return; } PointLocations.Delete_All(); for (unsigned int i=0; iProcess_Reduction(); unsigned int sort_level = SORT_LEVEL_NONE; if (!WW3D::Is_Sorting_Enabled()) sort_level=Get_Shader().Guess_Sort_Level(); if (WW3D::Are_Static_Sort_Lists_Enabled() && sort_level!=SORT_LEVEL_NONE) { WW3D::Add_To_Static_Sort_List(this, sort_level); } else Render_Seg_Line(rinfo); } void SegmentedLineClass::Get_Obj_Space_Bounding_Sphere(SphereClass & sphere) const { // Get object-space bounding box and create bounding sphere from it AABoxClass box; Get_Obj_Space_Bounding_Box(box); // Create object-space bounding sphere from the bounding box: sphere.Center = box.Center; sphere.Radius = box.Extent.Length(); } void SegmentedLineClass::Get_Obj_Space_Bounding_Box(AABoxClass & box) const { unsigned int num_points = PointLocations.Count(); // Line must have at least two points to be valid if (num_points >= 2) { // Find object-space axis-aligned bounding box Vector3 max_coords; Vector3 min_coords; unsigned int i; // We create two bounding boxes; one from the points, and if we have random noise // subdivision we create another one from the midpoints and factor the noise amplitude // into the second box, and then combine the two. // First bounding box: max_coords = PointLocations[0]; min_coords = PointLocations[0]; for (i = 1; i < num_points; i++) { max_coords.Update_Max(PointLocations[i]); min_coords.Update_Min(PointLocations[i]); } // Enlarge bounding box by half the width float enlarge_factor = LineRenderer.Get_Width() * 0.5f; Vector3 enlarge_offset; enlarge_offset.Set(enlarge_factor, enlarge_factor, enlarge_factor); max_coords += enlarge_offset; min_coords -= enlarge_offset; if (MaxSubdivisionLevels > 0) { // Second bounding box: Vector3 max_coords2; Vector3 min_coords2; Vector3 midpoint = (PointLocations[0] + PointLocations[1]) * 0.5f; max_coords2 = midpoint; min_coords2 = midpoint; for (i = 1; i < num_points - 1; i++) { midpoint = (PointLocations[i] + PointLocations[i + 1]) * 0.5f; max_coords2.Update_Max(midpoint); min_coords2.Update_Min(midpoint); } // We ignore the actual number of subdivision levels: we multiply the random noise // amplitude by 2, which is the limit as the number of subdivision levels goes to // infinity. enlarge_factor += (2 * LineRenderer.Get_Noise_Amplitude()); enlarge_offset.Set(enlarge_factor, enlarge_factor, enlarge_factor); max_coords2 += enlarge_offset; min_coords2 -= enlarge_offset; // Combine the two: max_coords.Update_Max(max_coords2); min_coords.Update_Min(min_coords2); } box.Init_Min_Max(min_coords, max_coords); } else { // Invalid line - return something box.Init(Vector3(0,0,0),Vector3(1,1,1)); } } void SegmentedLineClass::Prepare_LOD(CameraClass &camera) { // Find the maximum screen dimension of the object in pixels NormalizedScreenArea = Get_Screen_Size(camera); // // Find and set texture reduction factor // Set_Texture_Reduction_Factor(Calculate_Texture_Reduction_Factor(NormalizedScreenArea)); // Ensure subdivision level is legal unsigned int lvl = LineRenderer.Get_Current_Subdivision_Level(); lvl = MIN(lvl, MaxSubdivisionLevels); LineRenderer.Set_Current_Subdivision_Level(lvl); // Prepare LOD processing if the line has subdivision enabled: if (MaxSubdivisionLevels > 0) { // Add myself to the LOD optimizer: PredictiveLODOptimizerClass::Add_Object(this); } else { // Not added to optimizer, need to add cost PredictiveLODOptimizerClass::Add_Cost(Get_Cost()); } } void SegmentedLineClass::Increment_LOD(void) { unsigned int lvl = LineRenderer.Get_Current_Subdivision_Level(); lvl = MIN(lvl+1,MaxSubdivisionLevels); LineRenderer.Set_Current_Subdivision_Level(lvl); } void SegmentedLineClass::Decrement_LOD(void) { int lvl = LineRenderer.Get_Current_Subdivision_Level(); if (lvl == 0) return; LineRenderer.Set_Current_Subdivision_Level(lvl-1); } float SegmentedLineClass::Get_Cost(void) const { return Get_Num_Polys(); } float SegmentedLineClass::Get_Value(void) const { // If we are at the minimum LOD, we must return AT_MIN_LOD. if (LineRenderer.Get_Current_Subdivision_Level() == 0) { return AT_MIN_LOD; } else { float polycount = (float)Get_Num_Polys(); float benefit_factor = 1.0f - (0.5f / (polycount * polycount)); return (benefit_factor * NormalizedScreenArea) / Get_Cost(); } } float SegmentedLineClass::Get_Post_Increment_Value(void) const { // If we are at the maximum LOD, we must return AT_MIN_LOD. if (LineRenderer.Get_Current_Subdivision_Level() == MaxSubdivisionLevels) { return AT_MAX_LOD; } else { // Assumption: each subdivision level doubles polycount float polycount = 2.0f * (float)Get_Num_Polys(); float benefit_factor = 1.0f - (0.5f / (polycount * polycount)); // Assumption: Cost() == polycount return (benefit_factor * NormalizedScreenArea) / polycount; } } void SegmentedLineClass::Set_LOD_Level(int lod) { lod = MAX(0, lod); lod = MIN(lod, (int)MaxSubdivisionLevels); LineRenderer.Set_Current_Subdivision_Level((unsigned int)lod); } int SegmentedLineClass::Get_LOD_Level(void) const { return (int) LineRenderer.Get_Current_Subdivision_Level(); } int SegmentedLineClass::Get_LOD_Count(void) const { return (int)MaxSubdivisionLevels; } /* void SegmentedLineClass::Set_Texture_Reduction_Factor(float trf) { if (LineRenderer.Peek_Texture()) LineRenderer.Peek_Texture()->Set_Reduction_Factor(trf); }*/ void SegmentedLineClass::Render_Seg_Line(RenderInfoClass & rinfo) { // Line must have at least two points to be valid if (PointLocations.Count() < 2) return; SphereClass bounding_sphere; Get_Obj_Space_Bounding_Sphere(bounding_sphere); LineRenderer.Render( rinfo, Transform, PointLocations.Count(), &(PointLocations[0]), bounding_sphere ); } bool SegmentedLineClass::Cast_Ray(RayCollisionTestClass & raytest) { if ((Get_Collision_Type() & raytest.CollisionType) == 0) return false; bool retval = false; // // Check each line segment against the ray // float fraction = 1.0F; for (uint32 index = 1; index < (unsigned int)PointLocations.Count(); index ++) { Vector3 curr_start = Transform * PointLocations[index-1]; Vector3 curr_end = Transform * PointLocations[index]; LineSegClass line_seg (curr_start, curr_end); Vector3 p0; Vector3 p1; if (raytest.Ray.Find_Intersection (line_seg, &p0, &fraction, &p1, NULL)) { // // Determine if the ray was close enough to this line to be // considered intersecting // float dist = (p0 - p1).Length (); if (dist <= LineRenderer.Get_Width() && fraction >= 0 && fraction < raytest.Result->Fraction) { //if (dist <= Width && fraction < raytest.Result->Fraction) { retval = true; break; } } } // // Fill in the raytest structure if we were successfull // if (retval) { raytest.Result->Fraction = fraction; raytest.Result->SurfaceType = SURFACE_TYPE_DEFAULT; raytest.CollidedRenderObj = this; } return retval; }