/* ** 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 : Linegroup.cpp * * * * $Archive:: /VSS_Sync/ww3d2/linegrp.cpp $* * * * Original Author:: Hector Yee * * * * $Author:: Vss_sync $* * * * $Modtime:: 10/26/01 3:12p $* * * * $Revision:: 1 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "sharebuf.h" #include "linegrp.h" #include "texture.h" #include "vertmaterial.h" #include "dx8wrapper.h" #include "wwmath.h" #include "rinfo.h" #include "camera.h" #include "dx8indexbuffer.h" #include "dx8vertexbuffer.h" #include "sortingrenderer.h" // Line groups are a rendering primitive similar to point groups // They are tetrahedra which are aligned with the view plane with their centers // at StartLineLoc. The apex of the tetrahedron is at EndLineLoc. // They can be individually colored LineDiffuse // and the LineUCoord determines the U coordinate of the texture to use // the V coordinate is always 0 at the flat end of the tetrahedron // and 1 at the apex LineGroupClass::LineGroupClass(void) : StartLineLoc(NULL), EndLineLoc(NULL), LineDiffuse(NULL), TailDiffuse(NULL), ALT(NULL), LineSize(NULL), LineUCoord(NULL), LineCount(0), Texture(NULL), Flags(0), Shader(ShaderClass::_PresetAdditiveSpriteShader), DefaultLineSize(0.0f), DefaultLineColor(1.0f, 1.0f, 1.0f), DefaultLineAlpha(1.0f), DefaultLineUCoord(0.0f), DefaultTailDiffuse(0.0f, 0.0f, 0.0f, 0.0f), LineMode(TETRAHEDRON) { } LineGroupClass::~LineGroupClass(void) { REF_PTR_RELEASE(StartLineLoc); REF_PTR_RELEASE(EndLineLoc); REF_PTR_RELEASE(LineDiffuse); REF_PTR_RELEASE(TailDiffuse); REF_PTR_RELEASE(ALT); REF_PTR_RELEASE(LineSize); REF_PTR_RELEASE(LineUCoord); REF_PTR_RELEASE(Texture); } void LineGroupClass::Set_Arrays( ShareBufferClass *startlocs, ShareBufferClass *endlocs, ShareBufferClass *diffuse, ShareBufferClass *taildiffuse, ShareBufferClass *alt, ShareBufferClass *sizes, ShareBufferClass *ucoords, int active_line_count ) { // The Line locations arrays are NOT optional! WWASSERT(startlocs); WWASSERT(endlocs); // Ensure lengths of all arrays are the same: WWASSERT(startlocs->Get_Count() == endlocs->Get_Count()); WWASSERT(!diffuse || startlocs->Get_Count() == diffuse->Get_Count()); WWASSERT(!alt || startlocs->Get_Count() == alt->Get_Count()); WWASSERT(!sizes || startlocs->Get_Count() == sizes->Get_Count()); WWASSERT(!ucoords || startlocs->Get_Count() == ucoords->Get_Count()); WWASSERT(!taildiffuse || startlocs->Get_Count() == taildiffuse->Get_Count()); REF_PTR_SET(StartLineLoc,startlocs); REF_PTR_SET(EndLineLoc,endlocs); REF_PTR_SET(LineDiffuse,diffuse); REF_PTR_SET(TailDiffuse,taildiffuse); REF_PTR_SET(ALT,alt); REF_PTR_SET(LineSize,sizes); REF_PTR_SET(LineUCoord,ucoords); if (ALT) { LineCount = active_line_count; } else { LineCount = (active_line_count >= 0) ? active_line_count : StartLineLoc->Get_Count(); } } void LineGroupClass::Set_Line_Size(float size) { DefaultLineSize = size; } float LineGroupClass::Get_Line_Size(void) { return DefaultLineSize; } void LineGroupClass::Set_Line_Color(const Vector3 &color) { DefaultLineColor = color; } Vector3 LineGroupClass::Get_Line_Color(void) { return DefaultLineColor; } void LineGroupClass::Set_Tail_Diffuse(const Vector4 &tdiffuse) { DefaultTailDiffuse = tdiffuse; } Vector4 LineGroupClass::Get_Tail_Diffuse(void) { return DefaultTailDiffuse; } void LineGroupClass::Set_Line_Alpha(float alpha) { DefaultLineAlpha = alpha; } float LineGroupClass::Get_Line_Alpha(void) { return DefaultLineAlpha; } void LineGroupClass::Set_Line_UCoord(float ucoord) { DefaultLineUCoord = ucoord; } float LineGroupClass::Get_Line_UCoord(void) { return DefaultLineUCoord; } void LineGroupClass::Set_Flag(FlagsType flag, bool on) { if (on) Flags |= 1 << flag; else Flags &= ~(1 << flag); } int LineGroupClass::Get_Flag(FlagsType flag) { return (Flags >> flag) & 0x1; } void LineGroupClass::Set_Texture(TextureClass* texture) { REF_PTR_SET(Texture,texture); } TextureClass * LineGroupClass::Get_Texture(void) { if (Texture) Texture->Add_Ref(); return Texture; } TextureClass * LineGroupClass::Peek_Texture(void) { return Texture; } void LineGroupClass::Set_Shader(const ShaderClass &shader) { Shader = shader; } ShaderClass LineGroupClass::Get_Shader(void) { return Shader; } void LineGroupClass::Set_Line_Mode(LineModeType linemode) { LineMode = linemode; } LineGroupClass::LineModeType LineGroupClass::Get_Line_Mode(void) { return LineMode; } void LineGroupClass::Render(RenderInfoClass &rinfo) { int i; // If no lines, do nothing: if (LineCount == 0) return; // Shader handling Shader.Set_Cull_Mode(ShaderClass::CULL_MODE_ENABLE); // If there is a color or alpha array enable gradient in shader - otherwise disable. float value_255 = 0.9961f; //254 / 255 bool default_white_opaque = ( DefaultLineColor.X > value_255 && DefaultLineColor.Y > value_255 && DefaultLineColor.Z > value_255 && DefaultLineAlpha > value_255); if (LineDiffuse || !default_white_opaque || !Texture) { Shader.Set_Primary_Gradient(ShaderClass::GRADIENT_MODULATE); } else { Shader.Set_Primary_Gradient(ShaderClass::GRADIENT_DISABLE); } // If Texture is non-NULL enable texturing in shader - otherwise disable. if (Texture) { Shader.Set_Texturing(ShaderClass::TEXTURING_ENABLE); } else { Shader.Set_Texturing(ShaderClass::TEXTURING_DISABLE); } VertexMaterialClass * linemat = VertexMaterialClass::Get_Preset(VertexMaterialClass::PRELIT_DIFFUSE); DX8Wrapper::Set_Material(linemat); DX8Wrapper::Set_Shader(Shader); DX8Wrapper::Set_Texture(0, Texture); REF_PTR_RELEASE(linemat); WWASSERT(StartLineLoc && StartLineLoc->Get_Array()); WWASSERT(EndLineLoc && EndLineLoc->Get_Array()); // Enable sorting if the primitives are translucent and alpha testing is not enabled. const bool sort = (Shader.Get_Dst_Blend_Func() != ShaderClass::DSTBLEND_ZERO) && (Shader.Get_Alpha_Test() == ShaderClass::ALPHATEST_DISABLE) && (WW3D::Is_Sorting_Enabled()); // the 3 offsets in view space const static Vector3 offset_a = Vector3(WWMath::Cos(WWMATH_PI / 2), WWMath::Sin(WWMATH_PI /2 ), 0); const static Vector3 offset_b = Vector3(WWMath::Cos(7 * WWMATH_PI / 6), WWMath::Sin(7 * WWMATH_PI / 6), 0); const static Vector3 offset_c = Vector3(WWMath::Cos(11 * WWMATH_PI / 6), WWMath::Sin(11 * WWMATH_PI / 6), 0); static Vector3 offset[3]; offset[0].Set(offset_a); offset[1].Set(offset_b); offset[2].Set(offset_c); // Save off the view matrix Matrix4 view; DX8Wrapper::Get_Transform(D3DTS_VIEW, view); Matrix4 identity(true); DX8Wrapper::Set_Transform(D3DTS_WORLD, identity); // if the points are in world space, transform the offsets if (Get_Flag(TRANSFORM)) { Matrix3D xform_mat; xform_mat = rinfo.Camera.Get_Transform(); xform_mat.Set_Translation(Vector3(0, 0, 0)); xform_mat.Get_Orthogonal_Inverse(xform_mat); for (i = 0; i < 3; i++) { Matrix3D::Transform_Vector(xform_mat, offset[i], &offset[i]); } } else { DX8Wrapper::Set_Transform(D3DTS_VIEW, identity); } int num_tris=0; int num_indices=0; int num_vertices=0; switch (LineMode) { case TETRAHEDRON: num_tris =4 * LineCount; num_indices =3 * num_tris; num_vertices =4 * LineCount; break; case PRISM: num_tris =8 * LineCount; num_indices =3 * num_tris; num_vertices =6 * LineCount; break; } // construct the tetrahedra in the index buffers // assume first vertex is the apex, followed by offset[0-3] DynamicIBAccessClass iba(sort?BUFFER_TYPE_DYNAMIC_SORTING:BUFFER_TYPE_DYNAMIC_DX8,num_indices); { DynamicIBAccessClass::WriteLockClass lock(&iba); unsigned short *ibptr = lock.Get_Index_Array(); unsigned short j, idx; switch (LineMode) { case TETRAHEDRON: for (j=0; jGet_Element(i) : i; if (LineSize) size = LineSize->Get_Element(point); if (LineDiffuse) diffuse = LineDiffuse->Get_Element(point); if (LineUCoord) ucoord = LineUCoord->Get_Element(point); if (TailDiffuse) taildiffuse = TailDiffuse->Get_Element(point); end.Set(EndLineLoc->Get_Element(point)); start.Set(StartLineLoc->Get_Element(point)); switch (LineMode) { case TETRAHEDRON: // apex vb->x = end.X; vb->y = end.Y; vb->z = end.Z; vb->diffuse = DX8Wrapper::Convert_Color(taildiffuse); vb->u1 = ucoord; vb->v1 = 1.0f; vb++; for (j=0; j<3; j++) { loc.Set(start + size * offset[j]); vb->x = loc.X; vb->y = loc.Y; vb->z = loc.Z; vb->diffuse = DX8Wrapper::Convert_Color(diffuse); vb->u1 = ucoord; vb->v1 = 0.0f; vb++; } break; case PRISM: // start cap for (j = 0; j < 3; j++) { loc.Set(start + size * offset[j]); vb->x = loc.X; vb->y = loc.Y; vb->z = loc.Z; vb->diffuse = DX8Wrapper::Convert_Color(diffuse); vb->u1 = ucoord; vb->v1 = 0.0f; vb++; } // Do not merge loops. The vb has to be written in a specific order // (This is to optimize AGP memory write) // end cap for (j=0; j<3; j++) { loc.Set(end + size * offset[j]); vb->x = loc.X; vb->y = loc.Y; vb->z = loc.Z; vb->diffuse = DX8Wrapper::Convert_Color(taildiffuse); vb->u1 = ucoord; vb->v1 = 1.0f; vb++; } break; } } } // writing to vb DX8Wrapper::Set_Index_Buffer(iba, 0); DX8Wrapper::Set_Vertex_Buffer(vba); if (sort) { SortingRendererClass::Insert_Triangles(0, num_tris, 0, num_vertices); } else { DX8Wrapper::Draw_Triangles(0, num_tris, 0, num_vertices); } // restore the matrices DX8Wrapper::Set_Transform(D3DTS_VIEW, view); } int LineGroupClass::Get_Polygon_Count(void) { switch (LineMode) { case TETRAHEDRON: return LineCount * 4; break; case PRISM: return LineCount * 8; break; } WWASSERT(0); return 0; }