493 lines
No EOL
14 KiB
C++
493 lines
No EOL
14 KiB
C++
/*
|
|
** 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/***********************************************************************************************
|
|
*** 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<Vector3> *startlocs,
|
|
ShareBufferClass<Vector3> *endlocs,
|
|
ShareBufferClass<Vector4> *diffuse,
|
|
ShareBufferClass<Vector4> *taildiffuse,
|
|
ShareBufferClass<unsigned int> *alt,
|
|
ShareBufferClass<float> *sizes,
|
|
ShareBufferClass<float> *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; j<LineCount; j++) {
|
|
idx = 4 * j;
|
|
// apex, offset[1], offset[0]
|
|
*ibptr++ = idx + 0;
|
|
*ibptr++ = idx + 2;
|
|
*ibptr++ = idx + 1;
|
|
// apex, offset[2], offset[1]
|
|
*ibptr++ = idx + 0;
|
|
*ibptr++ = idx + 3;
|
|
*ibptr++ = idx + 2;
|
|
// apex, offset[0], offset[2]
|
|
*ibptr++ = idx + 0;
|
|
*ibptr++ = idx + 1;
|
|
*ibptr++ = idx + 3;
|
|
// offset[0-3]
|
|
*ibptr++ = idx + 1;
|
|
*ibptr++ = idx + 2;
|
|
*ibptr++ = idx + 3;
|
|
}
|
|
break;
|
|
case PRISM:
|
|
for (j=0; j<LineCount; j++) {
|
|
idx = 6 * j;
|
|
// starting cap 0,1,2
|
|
*ibptr++ = idx + 0;
|
|
*ibptr++ = idx + 1;
|
|
*ibptr++ = idx + 2;
|
|
// left side
|
|
*ibptr++ = idx + 0;
|
|
*ibptr++ = idx + 3;
|
|
*ibptr++ = idx + 1;
|
|
*ibptr++ = idx + 1;
|
|
*ibptr++ = idx + 3;
|
|
*ibptr++ = idx + 4;
|
|
// bottom side
|
|
*ibptr++ = idx + 1;
|
|
*ibptr++ = idx + 4;
|
|
*ibptr++ = idx + 5;
|
|
*ibptr++ = idx + 1;
|
|
*ibptr++ = idx + 5;
|
|
*ibptr++ = idx + 2;
|
|
// right side
|
|
*ibptr++ = idx + 0;
|
|
*ibptr++ = idx + 2;
|
|
*ibptr++ = idx + 5;
|
|
*ibptr++ = idx + 0;
|
|
*ibptr++ = idx + 5;
|
|
*ibptr++ = idx + 3;
|
|
// end cap
|
|
*ibptr++ = idx + 3;
|
|
*ibptr++ = idx + 5;
|
|
*ibptr++ = idx + 4;
|
|
}
|
|
break;
|
|
}
|
|
} // writing to ib
|
|
|
|
// make the vertex buffers
|
|
|
|
DynamicVBAccessClass vba(sort ? BUFFER_TYPE_DYNAMIC_SORTING : BUFFER_TYPE_DYNAMIC_DX8,dynamic_fvf_type,num_vertices);
|
|
|
|
{
|
|
DynamicVBAccessClass::WriteLockClass lock(&vba);
|
|
|
|
VertexFormatXYZNDUV2 *vb = lock.Get_Formatted_Vertex_Array();
|
|
|
|
Vector3 loc, start, end;
|
|
int point, j;
|
|
float size = DefaultLineSize;
|
|
Vector4 diffuse(DefaultLineColor.X, DefaultLineColor.Y, DefaultLineColor.Z, DefaultLineAlpha);
|
|
float ucoord = DefaultLineUCoord;
|
|
Vector4 taildiffuse = DefaultTailDiffuse;
|
|
|
|
for (i = 0; i < LineCount; i++)
|
|
{
|
|
point = (ALT) ? ALT->Get_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;
|
|
} |