Initial commit of Command & Conquer Generals and Command & Conquer Generals Zero Hour source code.

This commit is contained in:
LFeenanEA
2025-02-27 17:34:39 +00:00
parent 2e338c00cb
commit 3d0ee53a05
6072 changed files with 2283311 additions and 0 deletions

View File

@@ -0,0 +1,143 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/vector3i.h $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 11/24/01 5:24p $*
* *
* $Revision:: 5 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef VECTOR3I_H
#define VECTOR3I_H
#include "always.h"
class Vector3i
{
public:
int I;
int J;
int K;
WWINLINE Vector3i(void);
WWINLINE Vector3i(int i,int j,int k);
WWINLINE bool operator== (const Vector3i & v) const;
WWINLINE bool operator!= (const Vector3i& v) const;
WWINLINE const int& operator[] (int n) const;
WWINLINE int& operator[] (int n);
};
WWINLINE Vector3i::Vector3i(void)
{
}
WWINLINE Vector3i::Vector3i(int i,int j,int k)
{
I = i; J = j; K = k;
}
WWINLINE bool Vector3i::operator == (const Vector3i & v) const
{
return (I == v.I && J == v.J && K == v.K);
}
WWINLINE bool Vector3i::operator != (const Vector3i& v) const
{
return !(I == v.I && J == v.J && K == v.K);
}
WWINLINE const int& Vector3i::operator[] (int n) const
{
return ((int*)this)[n];
}
WWINLINE int& Vector3i::operator[] (int n)
{
return ((int*)this)[n];
}
// ----------------------------------------------------------------------------
class Vector3i16
{
public:
unsigned short I;
unsigned short J;
unsigned short K;
WWINLINE Vector3i16(void);
WWINLINE Vector3i16(unsigned short i,unsigned short j,unsigned short k);
WWINLINE bool operator== (const Vector3i & v) const;
WWINLINE bool operator!= (const Vector3i& v) const;
WWINLINE const unsigned short & operator[] (int n) const;
WWINLINE unsigned short & operator[] (int n);
};
WWINLINE Vector3i16::Vector3i16(void)
{
}
WWINLINE Vector3i16::Vector3i16(unsigned short i,unsigned short j,unsigned short k)
{
I = i; J = j; K = k;
}
WWINLINE bool Vector3i16::operator == (const Vector3i & v) const
{
return (I == v.I && J == v.J && K == v.K);
}
WWINLINE bool Vector3i16::operator != (const Vector3i& v) const
{
return !(I == v.I && J == v.J && K == v.K);
}
WWINLINE const unsigned short & Vector3i16::operator[] (int n) const
{
return ((unsigned short *)this)[n];
}
WWINLINE unsigned short & Vector3i16::operator[] (int n)
{
return ((unsigned short *)this)[n];
}
#endif

View File

@@ -0,0 +1,79 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/aabox.cpp $*
* *
* Author:: Greg_h *
* *
* $Modtime:: 5/08/01 6:33p $*
* *
* $Revision:: 18 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* AABoxClass::Init_Random -- initializes this box to a random state *
* AABoxClass::Contains -- test whether this box contains the given point *
* AABoxClass::Contains -- Test whether this box contains the given box *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "aabox.h"
#include "colmath.h"
#include "colmathinlines.h"
#include <float.h>
/***********************************************************************************************
* AABoxClass::Init_Random -- initializes this box to a random state *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/17/2000 gth : Created. *
*=============================================================================================*/
void AABoxClass::Init_Random(float min_center,float max_center,float min_extent,float max_extent)
{
Center.X = min_center + WWMath::Random_Float() * (max_center - min_center);
Center.Y = min_center + WWMath::Random_Float() * (max_center - min_center);
Center.Z = min_center + WWMath::Random_Float() * (max_center - min_center);
Extent.X = min_extent + WWMath::Random_Float() * (max_extent - min_extent);
Extent.Y = min_extent + WWMath::Random_Float() * (max_extent - min_extent);
Extent.Z = min_extent + WWMath::Random_Float() * (max_extent - min_extent);
}
void AABoxClass::Transform(const Matrix3D & tm,const AABoxClass & in,AABoxClass * out)
{
tm.Transform_Center_Extent_AABox(in.Center,in.Extent,&(out->Center),&(out->Extent));
}
void MinMaxAABoxClass::Init_Empty(void)
{
MinCorner.Set(FLT_MAX,FLT_MAX,FLT_MAX);
MaxCorner.Set(-FLT_MAX,-FLT_MAX,-FLT_MAX);
}

View File

@@ -0,0 +1,679 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/aabox.h $*
* *
* Author:: Greg_h *
* *
* $Modtime:: 5/08/01 6:35p $*
* *
* $Revision:: 30 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* AABoxClass::Transform -- transform an aabox *
* AABoxClass::Translate -- transform an aabox *
* AABoxClass::Init -- create a box which bounds the given points *
* AABoxClass::Init -- initialize from a min-max form of a box *
* AABoxClass::Init_Min_Max -- init the box from a min and max vector *
* AABoxClass::Add_Point -- expand the box to contain the given point *
* AABoxClass::Project_To_Axis -- compute projection onto the given axis *
* AABoxClass::Intersects -- test for intersection with another static aabox *
* AABoxClass::Add_Box -- expand this box to enclose the passed box *
* AABoxClass::Add_Box -- Expand this box to enclose the passed box *
* MinMaxAABoxClass::Init -- init the box from an array of points *
* MinMaxAABoxClass::Init -- initializes this box from a center-extent box *
* MinMaxAABoxClass::Add_Point -- updates this box so it encloses the given point *
* MinMaxAABoxClass::Add_Box -- update this box to enclose the given box *
* MinMaxAABoxClass::Add_Box -- Updates this box to enclose the specified box *
* MinMaxAABoxClass::Transform -- Updates this box to enclose its transformed version *
* MinMaxAABoxClass::Translate -- translates the box *
* AABoxClass::Init -- Init from a line segment *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef AABOX_H
#define AABOX_H
#include "always.h"
#include "matrix3d.h"
#include "lineseg.h"
#include "colmath.h"
class AABoxClass;
class MinMaxAABoxClass;
class OBBoxClass;
class TriClass;
class PlaneClass;
struct CastResultStruct;
/*
** AABoxClass
**
** Axis-Aligned Boxes. I've coded these similar to the OrientedBoxClass only
** without a rotation matrix. A similar algorithm is used to test the box
** for intersection.
*/
class AABoxClass
{
public:
WWINLINE AABoxClass(void) { }
WWINLINE AABoxClass(const Vector3 & center,const Vector3 & extent) :
Center(center),
Extent(extent)
{ }
AABoxClass(const MinMaxAABoxClass & minmaxbox) { Init(minmaxbox); }
AABoxClass(Vector3 * points,int num) { Init(points,num); }
bool operator== (const AABoxClass &src);
bool operator!= (const AABoxClass &src);
WWINLINE void Init(const Vector3& center,const Vector3 & extent) { Center = center; Extent = extent; }
WWINLINE void Init(Vector3 * points,int num);
WWINLINE void Init(const MinMaxAABoxClass & minmaxbox);
void Init(const LineSegClass & line);
void Init_Min_Max(const Vector3 & min,const Vector3 & max);
void Init_Random(float min_center = -1.0f,float max_center = 1.0f,float min_extent = 0.5f,float max_extent = 1.0f);
void Add_Point(const Vector3 & point);
void Add_Box(const AABoxClass & b);
void Add_Box(const MinMaxAABoxClass & b);
float Project_To_Axis(const Vector3 & axis) const;
void Transform(const Matrix3D & tm);
void Translate(const Vector3 & pos);
WWINLINE float Volume(void) const { return 2.0*Extent.X * 2.0*Extent.Y * 2.0*Extent.Z; }
WWINLINE bool Contains(const Vector3 & point) const;
WWINLINE bool Contains(const AABoxClass & other_box) const;
WWINLINE bool Contains(const MinMaxAABoxClass & other_box) const;
static void Transform(const Matrix3D & tm,const AABoxClass & in,AABoxClass * out);
Vector3 Center; // world space center
Vector3 Extent; // size of the box in the three directions
};
/*
** MinMaxAABoxClass
** This is another form of an AABox. It can be faster to build one of these
** and then convert it into a center-extent AABox in some cases. Its purpose
** is basically that.
*/
class MinMaxAABoxClass
{
public:
WWINLINE MinMaxAABoxClass(void) { }
WWINLINE MinMaxAABoxClass(const Vector3 & min_corner,const Vector3 & max_corner) :
MinCorner(min_corner),
MaxCorner(max_corner)
{
}
WWINLINE MinMaxAABoxClass(Vector3 * points,int num) { Init(points,num); }
WWINLINE MinMaxAABoxClass(const AABoxClass & that) { Init(that); }
WWINLINE void Init(Vector3 * points,int num);
WWINLINE void Init(const AABoxClass & box);
void Init_Empty(void);
void Add_Point(const Vector3 & point);
void Add_Box(const MinMaxAABoxClass & box);
void Add_Box(const AABoxClass & box);
void Add_Box(const Vector3 & min_corner,const Vector3 & max_corner);
void Transform(const Matrix3D & tm);
void Translate(const Vector3 & pos);
WWINLINE float Volume(void) const { Vector3 size = MaxCorner - MinCorner; return size.X*size.Y*size.Z; }
Vector3 MinCorner;
Vector3 MaxCorner;
};
/***********************************************************************************************
* AABoxClass::Transform -- transform an aabox *
* *
* Note that this function expands the box to enclose its transformed form. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 2/24/98 GTH : Created. *
*=============================================================================================*/
WWINLINE void AABoxClass::Transform(const Matrix3D & tm)
{
Vector3 oldcenter = Center;
Vector3 oldextent = Extent;
tm.Transform_Center_Extent_AABox(oldcenter,oldextent,&Center,&Extent);
}
/***********************************************************************************************
* AABoxClass::Translate -- translate an aabox *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 2/24/98 GTH : Created. *
*=============================================================================================*/
WWINLINE void AABoxClass::Translate(const Vector3 & trans)
{
Center += trans;
}
/***********************************************************************************************
* AABoxClass::operator== -- Comparison operator *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 6/21/00 PDS : Created. *
*=============================================================================================*/
WWINLINE bool AABoxClass::operator== (const AABoxClass &src)
{
return (Center == src.Center) && (Extent == src.Extent);
}
/***********************************************************************************************
* AABoxClass::operator!= -- Comparison operator *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 6/21/00 PDS : Created. *
*=============================================================================================*/
WWINLINE bool AABoxClass::operator!= (const AABoxClass &src)
{
return (Center != src.Center) || (Extent != src.Extent);
}
/***********************************************************************************************
* AABoxClass::Init -- create a box which bounds the given points *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 2/24/98 GTH : Created. *
*=============================================================================================*/
WWINLINE void AABoxClass::Init(Vector3 * points,int num)
{
Vector3 Min = points[0];
Vector3 Max = points[0];
for (int i=1; i<num; i++) {
if (Min.X > points[i].X) Min.X = points[i].X;
if (Min.Y > points[i].Y) Min.Y = points[i].Y;
if (Min.Z > points[i].Z) Min.Z = points[i].Z;
if (Max.X < points[i].X) Max.X = points[i].X;
if (Max.Y < points[i].Y) Max.Y = points[i].Y;
if (Max.Z < points[i].Z) Max.Z = points[i].Z;
}
Center = (Max + Min) * 0.5f;
Extent = (Max - Min) * 0.5f;
}
/***********************************************************************************************
* AABoxClass::Init -- initialize from a min-max form of a box *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/31/98 GTH : Created. *
*=============================================================================================*/
WWINLINE void AABoxClass::Init(const MinMaxAABoxClass & mmbox)
{
Center = (mmbox.MaxCorner + mmbox.MinCorner) * 0.5f;
Extent = (mmbox.MaxCorner - mmbox.MinCorner) * 0.5f;
}
/***********************************************************************************************
* AABoxClass::Init -- Init from a line segment *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/27/2000 gth : Created. *
*=============================================================================================*/
WWINLINE void AABoxClass::Init(const LineSegClass & line)
{
Vector3 min_corner = line.Get_P0();
Vector3 max_corner = line.Get_P0();
if (min_corner.X > line.Get_P1().X) min_corner.X = line.Get_P1().X;
if (min_corner.Y > line.Get_P1().Y) min_corner.Y = line.Get_P1().Y;
if (min_corner.Z > line.Get_P1().Z) min_corner.Z = line.Get_P1().Z;
if (max_corner.X < line.Get_P1().X) max_corner.X = line.Get_P1().X;
if (max_corner.Y < line.Get_P1().Y) max_corner.Y = line.Get_P1().Y;
if (max_corner.Z < line.Get_P1().Z) max_corner.Z = line.Get_P1().Z;
Center = (max_corner + min_corner) * 0.5f;
Extent = (max_corner - min_corner) * 0.5f;
}
/***********************************************************************************************
* AABoxClass::Init_Min_Max -- init the box from a min and max vector *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/9/99 GTH : Created. *
*=============================================================================================*/
WWINLINE void AABoxClass::Init_Min_Max(const Vector3 & min,const Vector3 & max)
{
Center = (max + min) * 0.5f;
Extent = (max - min) * 0.5f;
}
/***********************************************************************************************
* AABoxClass::Add_Point -- expand the box to contain the given point *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 2/24/98 GTH : Created. *
*=============================================================================================*/
WWINLINE void AABoxClass::Add_Point(const Vector3 & point)
{
Vector3 Min = Center - Extent;
Vector3 Max = Center + Extent;
if (Min.X > point.X) Min.X = point.X;
if (Min.Y > point.Y) Min.Y = point.Y;
if (Min.Z > point.Z) Min.Z = point.Z;
if (Max.X < point.X) Max.X = point.X;
if (Max.Y < point.Y) Max.Y = point.Y;
if (Max.Z < point.Z) Max.Z = point.Z;
Center = (Max + Min) / 2.0f;
Extent = (Max - Min) / 2.0f;
}
/***********************************************************************************************
* AABoxClass::Add_Box -- expand this box to enclose the passed box *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/31/98 GTH : Created. *
* 9/29/2000 gth : Ok to add boxes with zero extent *
*=============================================================================================*/
WWINLINE void AABoxClass::Add_Box(const AABoxClass & b)
{
Vector3 newmin = Center - Extent;
Vector3 newmax = Center + Extent;
newmin.Update_Min(b.Center - b.Extent);
newmax.Update_Max(b.Center + b.Extent);
Center = (newmax + newmin) * 0.5f;
Extent = (newmax - newmin) * 0.5f;
}
/***********************************************************************************************
* AABoxClass::Add_Box -- Expand this box to enclose the passed box *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/31/98 GTH : Created. *
* 9/29/2000 gth : Ok to add boxes with zero extent *
*=============================================================================================*/
WWINLINE void AABoxClass::Add_Box(const MinMaxAABoxClass & b)
{
Vector3 newmin = Center - Extent;
Vector3 newmax = Center + Extent;
newmin.Update_Min(b.MinCorner);
newmax.Update_Max(b.MaxCorner);
Center = (newmax + newmin) * 0.5f;
Extent = (newmax - newmin) * 0.5f;
}
/***********************************************************************************************
* AABoxClass::Project_To_Axis -- compute projection onto the given axis *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 2/24/98 GTH : Created. *
*=============================================================================================*/
WWINLINE float AABoxClass::Project_To_Axis(const Vector3 & axis) const
{
float x = Extent[0] * axis[0];
float y = Extent[1] * axis[1];
float z = Extent[2] * axis[2];
// projection is the sum of the absolute values of the projections of the three extents
return (WWMath::Fabs(x) + WWMath::Fabs(y) + WWMath::Fabs(z));
}
/***********************************************************************************************
* AABoxClass::Contains -- Test whether this box contains the given box *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 9/2/98 GTH : Created. *
*=============================================================================================*/
WWINLINE bool AABoxClass::Contains(const AABoxClass & other_box) const
{
return CollisionMath::Overlap_Test(*this,other_box) == CollisionMath::INSIDE;
}
/***********************************************************************************************
* AABoxClass::Contains -- Test whether this box contains the given box *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 9/2/98 GTH : Created. *
*=============================================================================================*/
WWINLINE bool AABoxClass::Contains(const MinMaxAABoxClass & other_box) const
{
Vector3 bmin = Center - Extent;
Vector3 bmax = Center + Extent;
if (other_box.MinCorner.X < bmin.X) return false;
if (other_box.MinCorner.Y < bmin.Y) return false;
if (other_box.MinCorner.Z < bmin.Z) return false;
if (other_box.MaxCorner.X > bmax.X) return false;
if (other_box.MaxCorner.Y > bmax.Y) return false;
if (other_box.MaxCorner.Z > bmax.Z) return false;
return true;
}
/***********************************************************************************************
* AABoxClass::Contains -- test whether this box contains the given point *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 9/2/98 GTH : Created. *
*=============================================================================================*/
WWINLINE bool AABoxClass::Contains(const Vector3 & point) const
{
return CollisionMath::Overlap_Test(*this,point) == CollisionMath::INSIDE;
}
/***********************************************************************************************
* MinMaxAABoxClass::Init -- init the box from an array of points *
* *
* Makes a box which encloses the given array of points *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/31/98 GTH : Created. *
*=============================================================================================*/
WWINLINE void MinMaxAABoxClass::Init(Vector3 * points,int num)
{
assert(num > 0);
assert(points != NULL);
MinCorner = points[0];
MaxCorner = points[0];
for (int i=0; i<num; i++) {
MinCorner.Update_Min(points[i]);
MaxCorner.Update_Max(points[i]);
}
}
/***********************************************************************************************
* MinMaxAABoxClass::Init -- initializes this box from a center-extent box *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/31/98 GTH : Created. *
*=============================================================================================*/
WWINLINE void MinMaxAABoxClass::Init(const AABoxClass & box)
{
MinCorner = box.Center - box.Extent;
MaxCorner = box.Center + box.Extent;
}
/***********************************************************************************************
* MinMaxAABoxClass::Add_Point -- updates this box so it encloses the given point *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/31/98 GTH : Created. *
*=============================================================================================*/
WWINLINE void MinMaxAABoxClass::Add_Point(const Vector3 & point)
{
MinCorner.Update_Min(point);
MaxCorner.Update_Max(point);
}
/***********************************************************************************************
* MinMaxAABoxClass::Add_Box -- update this box to enclose the given box *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/31/98 GTH : Created. *
*=============================================================================================*/
WWINLINE void MinMaxAABoxClass::Add_Box(const MinMaxAABoxClass & box)
{
if (box.MinCorner == box.MaxCorner) return;
MinCorner.Update_Min(box.MinCorner);
MaxCorner.Update_Max(box.MaxCorner);
}
/***********************************************************************************************
* MinMaxAABoxClass::Add_Box -- update this box to enclose the given box *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/31/98 GTH : Created. *
*=============================================================================================*/
WWINLINE void MinMaxAABoxClass::Add_Box(const AABoxClass & box)
{
if (box.Extent == Vector3(0.0f, 0.0f, 0.0f)) return;
MinCorner.Update_Min(box.Center - box.Extent);
MaxCorner.Update_Max(box.Center + box.Extent);
}
/***********************************************************************************************
* MinMaxAABoxClass::Add_Box -- Updates this box to enclose the specified box *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/31/98 GTH : Created. *
*=============================================================================================*/
WWINLINE void MinMaxAABoxClass::Add_Box(const Vector3 & min_corner,const Vector3 & max_corner)
{
assert(max_corner.X >= min_corner.X);
assert(max_corner.Y >= min_corner.Y);
assert(max_corner.Z >= min_corner.Z);
if (min_corner == max_corner) return;
MinCorner.Update_Min(min_corner);
MaxCorner.Update_Max(max_corner);
}
/***********************************************************************************************
* MinMaxAABoxClass::Transform -- Updates this box to enclose its transformed version *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/31/98 GTH : Created. *
*=============================================================================================*/
WWINLINE void MinMaxAABoxClass::Transform(const Matrix3D & tm)
{
Vector3 oldmin = MinCorner;
Vector3 oldmax = MaxCorner;
tm.Transform_Min_Max_AABox(oldmin,oldmax,&MinCorner,&MaxCorner);
}
/***********************************************************************************************
* MinMaxAABoxClass::Translate -- translates the box *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/31/98 GTH : Created. *
*=============================================================================================*/
WWINLINE void MinMaxAABoxClass::Translate(const Vector3 & pos)
{
MinCorner+=pos;
MaxCorner+=pos;
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,344 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/aabtreecull.h $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 7/24/01 10:00a $*
* *
* $Revision:: 15 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef AABTREECULL_H
#define AABTREECULL_H
#include "cullsys.h"
#include "aaplane.h"
#include "wwmath.h"
#include "mempool.h"
#include "simplevec.h"
#include <math.h>
#include <float.h>
class AABTreeNodeClass;
class ChunkLoadClass;
class ChunkSaveClass;
class SphereClass;
/**
** AABTreeCullSystemClass
** Derived culling system that uses an Axis-Aligned Bounding Box Tree
*/
class AABTreeCullSystemClass : public CullSystemClass
{
public:
AABTreeCullSystemClass(void);
virtual ~AABTreeCullSystemClass(void);
/*
** Re-partition the tree. Two methods can be used to accomplish this. The
** first re-partitions the tree based on the objects contained within, the second
** re-partitions the tree based solely on a set of input "seed" boxes. Each seed
** box will become a leaf; then the objects will be re-inserted in the new tree.
*/
void Re_Partition(void);
void Re_Partition(const AABoxClass & bounds,SimpleDynVecClass<AABoxClass> & boxes);
/*
** Update_Bounding_Boxes. This function causes all bounding boxes in the tree to update themselves.
** If any box is found to not bound the objects it is supposed to contain, the box is updated
** Note that this is normally not necessary, the reason this function existsis due to the fact
** that the renegade level editor tries to do everything possible to not discard the precalculated
** visibilty data for a level. In some cases, we want to load geometry that has been edited back
** into the same AABTree without re-partitioning.
*/
void Update_Bounding_Boxes(void);
/*
** Re-insert an object into the tree
*/
virtual void Update_Culling(CullableClass * obj);
/*
** Statistics about the AAB-Tree
*/
int Partition_Node_Count(void) const;
int Partition_Tree_Depth(void) const;
int Object_Count(void) const;
/*
** Collect objects which overlap the given primitive
*/
virtual void Collect_Objects(const Vector3 & point);
virtual void Collect_Objects(const AABoxClass & box);
virtual void Collect_Objects(const OBBoxClass & box);
virtual void Collect_Objects(const FrustumClass & frustum);
virtual void Collect_Objects(const SphereClass & sphere);
/*
** Load and Save a description of this AAB-Tree and its contents
*/
virtual void Load(ChunkLoadClass & cload);
virtual void Save(ChunkSaveClass & csave);
/*
** Save an objects linkage, load the linkage and re-link the object
*/
void Load_Object_Linkage(ChunkLoadClass & cload,CullableClass * obj);
void Save_Object_Linkage(ChunkSaveClass & csave,CullableClass * obj);
/*
** Bounding box of the entire tree
*/
const AABoxClass & Get_Bounding_Box(void);
void Get_Node_Bounds(int node_id,AABoxClass * set_bounds);
/*
** Statistics
*/
struct StatsStruct
{
int NodeCount;
int NodesAccepted;
int NodesTriviallyAccepted;
int NodesRejected;
};
void Reset_Statistics(void);
const StatsStruct & Get_Statistics(void);
protected:
/*
** Internal stat tracking
*/
#ifdef WWDEBUG
void NODE_ACCEPTED(void) { Stats.NodesAccepted ++; }
void NODE_TRIVIALLY_ACCEPTED(void) { Stats.NodesTriviallyAccepted ++; }
void NODE_REJECTED(void) { Stats.NodesRejected ++; }
#else
void NODE_ACCEPTED(void) { }
void NODE_TRIVIALLY_ACCEPTED(void) { }
void NODE_REJECTED(void) { }
#endif
/*
** Internal functions
*/
void Add_Object_Internal(CullableClass * obj,int node_index = -1);
void Remove_Object_Internal(CullableClass * obj);
void Re_Index_Nodes(void);
void Re_Index_Nodes_Recursive(AABTreeNodeClass * node,int & counter);
int Partition_Node_Count_Recursive(AABTreeNodeClass * node) const;
void Partition_Tree_Depth_Recursive(AABTreeNodeClass * node,int cur_depth,int & max_depth) const;
void Add_Object_Recursive(AABTreeNodeClass * node,CullableClass * obj);
void Add_Loaded_Object(AABTreeNodeClass * node,CullableClass * obj);
void Collect_Objects_Recursive(AABTreeNodeClass * node);
void Collect_Objects_Recursive(AABTreeNodeClass * node,const Vector3 & point);
void Collect_Objects_Recursive(AABTreeNodeClass * node,const AABoxClass & box);
void Collect_Objects_Recursive(AABTreeNodeClass * node,const OBBoxClass & box);
void Collect_Objects_Recursive(AABTreeNodeClass * node,const FrustumClass & frustum);
void Collect_Objects_Recursive(AABTreeNodeClass * node,const FrustumClass & frustum,int planes_passed);
void Collect_Objects_Recursive(AABTreeNodeClass * node,const SphereClass & sphere);
void Update_Bounding_Boxes_Recursive(AABTreeNodeClass * node);
void Load_Nodes(AABTreeNodeClass * node,ChunkLoadClass & cload);
void Save_Nodes(AABTreeNodeClass * node,ChunkSaveClass & csave);
virtual void Load_Node_Contents(AABTreeNodeClass * /*node*/,ChunkLoadClass & /*cload*/) { }
virtual void Save_Node_Contents(AABTreeNodeClass * /*node*/,ChunkSaveClass & /*csave*/) { }
AABTreeNodeClass * RootNode; // root of the AAB-Tree
int ObjectCount; // number of objects in the system
int NodeCount; // number of nodes
AABTreeNodeClass ** IndexedNodes; // index access to the nodes
StatsStruct Stats;
friend class AABTreeIterator;
};
/**
** AABTreeIterator
** This iterator allows the user to walk a tree. It can return the index of the current
** node and the bounds of the current node.
*/
class AABTreeIterator
{
public:
AABTreeIterator(AABTreeCullSystemClass * tree);
void Reset(void);
bool Enter_Parent(void);
bool Enter_Sibling(void);
bool Has_Front_Child(void);
bool Enter_Front_Child(void);
bool Has_Back_Child(void);
bool Enter_Back_Child(void);
int Get_Current_Node_Index(void);
void Get_Current_Box(AABoxClass * set_box);
private:
void validate(void);
AABTreeCullSystemClass * Tree;
int CurNodeIndex;
};
/**
** TypedAABTreeCullSystemClass
** This template adds type-safety to an AABTree. It allows you to create trees
** containing a particular type of object (the class must be derived from CullableClass though)
*/
template <class T> class TypedAABTreeCullSystemClass : public AABTreeCullSystemClass
{
public:
virtual void Add_Object(T * obj,int node_index=-1) { Add_Object_Internal(obj,node_index); }
virtual void Remove_Object(T * obj) { Remove_Object_Internal(obj); }
T * Get_First_Collected_Object(void) { return (T*)Get_First_Collected_Object_Internal(); }
T * Get_Next_Collected_Object(T * obj) { return (T*)Get_Next_Collected_Object_Internal(obj); }
T * Peek_First_Collected_Object(void) { return (T*)Peek_First_Collected_Object_Internal(); }
T * Peek_Next_Collected_Object(T * obj) { return (T*)Peek_Next_Collected_Object_Internal(obj); }
};
/**
** AABTreeNodeClass - the aab-tree is built out of these objects
** CullableClass's can be linked into any of these nodes. Whenever the
** tree is re-built, all objects will end up in the leaf nodes. Between
** re-builds, as objects are added, they will be placed as deep into the
** tree as possible.
*/
class AABTreeNodeClass : public AutoPoolClass<AABTreeNodeClass,256>
{
public:
AABTreeNodeClass(void);
~AABTreeNodeClass(void);
void Add_Object(CullableClass * obj,bool update_bounds = true);
void Remove_Object(CullableClass * obj);
int Object_Count(void);
CullableClass * Peek_Object(int index);
uint32 Index; // Index of this node
AABoxClass Box; // Bounding box of the node
AABTreeNodeClass * Parent; // parent of this node
AABTreeNodeClass * Front; // front node
AABTreeNodeClass * Back; // back node
CullableClass * Object; // objects in this node
uint32 UserData; // 32bit field for the user, initialized to 0
/*
** Construction support:
*/
struct SplitChoiceStruct
{
SplitChoiceStruct(void) : Cost(FLT_MAX),FrontCount(0),BackCount(0),Plane(AAPlaneClass::XNORMAL,0.0f)
{
FrontBox.Init_Empty();
BackBox.Init_Empty();
}
float Cost;
int FrontCount;
int BackCount;
MinMaxAABoxClass FrontBox;
MinMaxAABoxClass BackBox;
AAPlaneClass Plane;
};
void Compute_Bounding_Box(void);
void Compute_Local_Bounding_Box(void);
float Compute_Volume(void);
void Transfer_Objects(AABTreeNodeClass * dummy_node);
/*
** Partition the tree based on the objects contained.
*/
void Partition(void);
void Split_Objects( const SplitChoiceStruct & sc,
AABTreeNodeClass * front,
AABTreeNodeClass * back);
/*
** Partition the tree based on a set of input "seed" boxes.
*/
void Partition(const AABoxClass & bounds,SimpleDynVecClass<AABoxClass> & boxes);
void Split_Boxes( const SplitChoiceStruct & sc,
SimpleDynVecClass<AABoxClass> & boxes,
SimpleDynVecClass<AABoxClass> & frontboxes,
SimpleDynVecClass<AABoxClass> & backboxes);
/*
** Functions used by both partitioning algorithms
*/
void Select_Splitting_Plane(SplitChoiceStruct * sc,SimpleDynVecClass<AABoxClass> & boxes);
void Select_Splitting_Plane_Brute_Force(SplitChoiceStruct * sc,SimpleDynVecClass<AABoxClass> & boxes);
void Compute_Score(SplitChoiceStruct * sc,SimpleDynVecClass<AABoxClass> & boxes);
};
/*
** AABTreeLinkClass
** This structure is used to link objects into an AAB-Tree culling system.
*/
class AABTreeLinkClass : public CullLinkClass, public AutoPoolClass<AABTreeLinkClass,256>
{
public:
AABTreeLinkClass(AABTreeCullSystemClass * system) : CullLinkClass(system),Node(NULL), NextObject(NULL) { }
AABTreeNodeClass * Node; // partition node containing this object
CullableClass * NextObject; // next object in the node
};
#endif // AABTREECULL_H

View File

@@ -0,0 +1,83 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/aaplane.h $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 5/19/00 3:12p $*
* *
* $Revision:: 8 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef AAPLANE_H
#define AAPLANE_H
#include "always.h"
#include "vector3.h"
/*
** This class is used to describe an "axis-aligned" plane. I.e, the normal
** of the plane is one of the three coordinate axes.
*/
class AAPlaneClass
{
public:
enum AxisEnum { XNORMAL = 0, YNORMAL = 1, ZNORMAL = 2 };
AAPlaneClass(void) { }
AAPlaneClass(AxisEnum normal,float dist) : Normal(normal),Dist(dist) { }
void Set(AxisEnum normal,float dist);
void Get_Normal(Vector3 * normal) const;
public:
AxisEnum Normal;
float Dist;
};
inline void AAPlaneClass::Set(AxisEnum normal,float dist)
{
Normal = normal;
Dist = dist;
}
inline void AAPlaneClass::Get_Normal(Vector3 * normal) const
{
normal->Set(0,0,0);
(*normal)[Normal] = 1.0f;
}
#endif

View File

@@ -0,0 +1,356 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/cardinalspline.cpp $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 9/16/01 4:07p $*
* *
* $Revision:: 6 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "cardinalspline.h"
#include "wwdebug.h"
#include "persistfactory.h"
#include "wwmathids.h"
#include "wwhack.h"
/*
** Force-Link this module because the linker can't detect that we actually need it...
*/
DECLARE_FORCE_LINK(cardinalspline);
/*
** Save-Load stuff
*/
SimplePersistFactoryClass<CardinalSpline3DClass,WWMATH_CHUNKID_CARDINALSPLINE3D> _CardinalSpline3DFactory;
SimplePersistFactoryClass<CardinalSpline1DClass,WWMATH_CHUNKID_CARDINALSPLINE1D> _CardinalSpline1DFactory;
enum
{
// ID's used by CardinalSpline3D
CARDINAL3D_CHUNK_HERMITE3D = 0x02070957,
CARDINAL3D_CHUNK_TIGHTNESSKEYS,
// ID's used by CardinalSpline1D
CARDINAL1D_CHUNK_HERMITE1D = 0x02070959,
CARDINAL1D_CHUNK_TIGHTNESSKEYS
};
/*
** CardinalSpline3DClass Implementation
*/
int CardinalSpline3DClass::Add_Key(const Vector3 & point,float t)
{
int index = HermiteSpline3DClass::Add_Key(point,t);
float tightness = 0.5f;
Tightness.Insert(index,tightness);
return index;
}
void CardinalSpline3DClass::Remove_Key(int i)
{
Tightness.Delete(i);
HermiteSpline3DClass::Remove_Key(i);
}
void CardinalSpline3DClass::Clear_Keys(void)
{
Tightness.Clear();
HermiteSpline3DClass::Clear_Keys();
}
void CardinalSpline3DClass::Set_Tightness(int i,float tightness)
{
WWASSERT(i >= 0);
WWASSERT(i < Tightness.Count());
Tightness[i] = tightness;
TangentsDirty = true;
}
float CardinalSpline3DClass::Get_Tightness(int i)
{
return Tightness[i];
}
void CardinalSpline3DClass::Update_Tangents(void)
{
if (Keys.Count() < 2) {
for (int i=0; i<Keys.Count(); i++) {
Tangents[0].InTangent.Set(0,0,0);
Tangents[0].OutTangent.Set(0,0,0);
}
return;
}
// First and Last Key:
// Only need to compute the OutTangent for key[0] and the InTangent for key[end]
int end = Keys.Count() - 1;
Tangents[0].InTangent.Set(0,0,0);
Tangents[end].OutTangent.Set(0,0,0);
if (IsLooping) {
// This really only works if the start and end points have the same position...
Tangents[0].OutTangent.X = (1.0f - Tightness[0])*(Keys[1].Point.X - Keys[end-1].Point.X);
Tangents[0].OutTangent.Y = (1.0f - Tightness[0])*(Keys[1].Point.Y - Keys[end-1].Point.Y);
Tangents[0].OutTangent.Z = (1.0f - Tightness[0])*(Keys[1].Point.Z - Keys[end-1].Point.Z);
Tangents[end].InTangent = Tangents[0].OutTangent;
} else {
Tangents[0].OutTangent.X = (1.0f - Tightness[0])*(Keys[1].Point.X - Keys[0].Point.X);
Tangents[0].OutTangent.Y = (1.0f - Tightness[0])*(Keys[1].Point.Y - Keys[0].Point.Y);
Tangents[0].OutTangent.Z = (1.0f - Tightness[0])*(Keys[1].Point.Z - Keys[0].Point.Z);
Tangents[end].InTangent.X = (1.0f - Tightness[0])*(Keys[end].Point.X - Keys[end-1].Point.X);
Tangents[end].InTangent.Y = (1.0f - Tightness[0])*(Keys[end].Point.Y - Keys[end-1].Point.Y);
Tangents[end].InTangent.Z = (1.0f - Tightness[0])*(Keys[end].Point.Z - Keys[end-1].Point.Z);
}
float total_time = (Keys[1].Time - Keys[0].Time) + (Keys[end].Time - Keys[end-1].Time);
float in_factor = 2.0f * (Keys[end].Time - Keys[end-1].Time) / total_time;
float out_factor = 2.0f * (Keys[1].Time - Keys[0].Time) / total_time;
Tangents[end].InTangent *= in_factor;
Tangents[0].OutTangent *= out_factor;
// inner knots
for (int i=1; i<Keys.Count()-1; i++) {
Tangents[i].InTangent.X = (1.0f - Tightness[i])*(Keys[i+1].Point.X - Keys[i-1].Point.X);
Tangents[i].InTangent.Y = (1.0f - Tightness[i])*(Keys[i+1].Point.Y - Keys[i-1].Point.Y);
Tangents[i].InTangent.Z = (1.0f - Tightness[i])*(Keys[i+1].Point.Z - Keys[i-1].Point.Z);
Tangents[i].OutTangent = Tangents[i].InTangent;
float in_factor = 2.0f * (Keys[i].Time - Keys[i-1].Time) / (Keys[i+1].Time - Keys[i-1].Time);
float out_factor = 2.0f * (Keys[i+1].Time - Keys[i].Time) / (Keys[i+1].Time - Keys[i-1].Time);
Tangents[i].InTangent *= in_factor; // compensating for the un-even keys
Tangents[i].OutTangent *= out_factor;
}
TangentsDirty = false;
}
const PersistFactoryClass & CardinalSpline3DClass::Get_Factory(void) const
{
return _CardinalSpline3DFactory;
}
bool CardinalSpline3DClass::Save(ChunkSaveClass &csave)
{
csave.Begin_Chunk(CARDINAL3D_CHUNK_HERMITE3D);
HermiteSpline3DClass::Save(csave);
csave.End_Chunk();
csave.Begin_Chunk(CARDINAL3D_CHUNK_TIGHTNESSKEYS);
for (int i=0; i<Tightness.Count(); i++) {
float tightness = Tightness[i];
csave.Write(&(tightness),sizeof(tightness));
}
csave.End_Chunk();
return true;
}
bool CardinalSpline3DClass::Load(ChunkLoadClass &cload)
{
int i;
float tightness;
// reset the array of tightness keys
Tightness.Delete_All();
// read in the chunks
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID())
{
case CARDINAL3D_CHUNK_HERMITE3D:
HermiteSpline3DClass::Load(cload);
break;
case CARDINAL3D_CHUNK_TIGHTNESSKEYS:
for (i=0; i<Keys.Count(); i++) {
cload.Read(&(tightness),sizeof(tightness));
Tightness.Add(tightness);
}
break;
default:
WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",__FILE__,__LINE__));
break;
}
cload.Close_Chunk();
}
return true;
}
/*
** CardinalSpline1DClass Implementation
*/
int CardinalSpline1DClass::Add_Key(float point,float t)
{
int index = HermiteSpline1DClass::Add_Key(point,t);
float tightness = 0.5f;
Tightness.Insert(index,tightness);
return index;
}
void CardinalSpline1DClass::Remove_Key(int i)
{
Tightness.Delete(i);
HermiteSpline1DClass::Remove_Key(i);
}
void CardinalSpline1DClass::Clear_Keys(void)
{
Tightness.Clear();
HermiteSpline1DClass::Clear_Keys();
}
void CardinalSpline1DClass::Set_Tightness(int i,float tightness)
{
WWASSERT(i >= 0);
WWASSERT(i < Tightness.Count());
Tightness[i] = tightness;
TangentsDirty = true;
}
float CardinalSpline1DClass::Get_Tightness(int i)
{
return Tightness[i];
}
void CardinalSpline1DClass::Update_Tangents(void)
{
if (Keys.Count() < 2) {
for (int i=0; i<Keys.Count(); i++) {
Tangents[0].InTangent = 0;
Tangents[0].OutTangent = 0;
}
}
// First and Last Key:
// Only need to compute the OutTangent for key[0] and the InTangent for key[end]
int end = Keys.Count() - 1;
Tangents[0].InTangent = 0;
Tangents[end].OutTangent = 0;
if (IsLooping) {
// This really only works if the start and end points have the same position...
Tangents[0].OutTangent = (1.0f - Tightness[0])*(Keys[1].Point - Keys[end-1].Point);
Tangents[end].InTangent = Tangents[0].OutTangent;
} else {
Tangents[0].OutTangent = (1.0f - Tightness[0])*(Keys[1].Point - Keys[0].Point);
Tangents[end].InTangent = (1.0f - Tightness[0])*(Keys[end].Point - Keys[end-1].Point);
}
float total_time = (Keys[1].Time - Keys[0].Time) + (Keys[end].Time - Keys[end-1].Time);
float in_factor = 2.0f * (Keys[end].Time - Keys[end-1].Time) / total_time;
float out_factor = 2.0f * (Keys[1].Time - Keys[0].Time) / total_time;
Tangents[end].InTangent *= in_factor;
Tangents[0].OutTangent *= out_factor;
// inner knots
for (int i=1; i<Keys.Count()-1; i++) {
Tangents[i].InTangent = (1.0f - Tightness[i])*(Keys[i+1].Point - Keys[i-1].Point);
Tangents[i].OutTangent = Tangents[i].InTangent;
float in_factor = 2.0f * (Keys[i].Time - Keys[i-1].Time) / (Keys[i+1].Time - Keys[i-1].Time);
float out_factor = 2.0f * (Keys[i+1].Time - Keys[i].Time) / (Keys[i+1].Time - Keys[i-1].Time);
Tangents[i].InTangent *= in_factor; // compensating for the un-even keys
Tangents[i].OutTangent *= out_factor;
}
TangentsDirty = false;
}
const PersistFactoryClass & CardinalSpline1DClass::Get_Factory(void) const
{
return _CardinalSpline1DFactory;
}
bool CardinalSpline1DClass::Save(ChunkSaveClass &csave)
{
csave.Begin_Chunk(CARDINAL1D_CHUNK_HERMITE1D);
HermiteSpline1DClass::Save(csave);
csave.End_Chunk();
csave.Begin_Chunk(CARDINAL1D_CHUNK_TIGHTNESSKEYS);
for (int i=0; i<Tightness.Count(); i++) {
float tightness = Tightness[i];
csave.Write(&(tightness),sizeof(tightness));
}
csave.End_Chunk();
return true;
}
bool CardinalSpline1DClass::Load(ChunkLoadClass &cload)
{
int i;
float tightness;
// reset the array of tangents
Tightness.Delete_All();
// read in the chunks
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID())
{
case CARDINAL1D_CHUNK_HERMITE1D:
HermiteSpline1DClass::Load(cload);
break;
case CARDINAL1D_CHUNK_TIGHTNESSKEYS:
for (i=0; i<Keys.Count(); i++) {
cload.Read(&(tightness),sizeof(tightness));
Tightness.Add(tightness);
}
break;
default:
WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",__FILE__,__LINE__));
break;
}
cload.Close_Chunk();
}
return true;
}

View File

@@ -0,0 +1,104 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /VSS_Sync/wwmath/cardinalspline.h $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 6/13/01 2:18p $*
* *
* $Revision:: 4 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef CARDINALSPLINE_H
#define CARDINALSPLINE_H
#include "hermitespline.h"
/**
** CardinalSpline3DClass
** 3-Dimensional cardinal splines
*/
class CardinalSpline3DClass : public HermiteSpline3DClass
{
public:
virtual int Add_Key(const Vector3 & point,float t);
virtual void Remove_Key(int i);
virtual void Clear_Keys(void);
virtual void Set_Tightness(int i,float tightness);
virtual float Get_Tightness(int i);
virtual void Update_Tangents(void);
// save-load support
virtual const PersistFactoryClass & Get_Factory(void) const;
virtual bool Save(ChunkSaveClass &csave);
virtual bool Load(ChunkLoadClass &cload);
protected:
DynamicVectorClass<float> Tightness;
};
/**
** CardinalSpline1DClass
** 1-Dimensional cardinal splines
*/
class CardinalSpline1DClass : public HermiteSpline1DClass
{
public:
virtual int Add_Key(float point,float t);
virtual void Remove_Key(int i);
virtual void Clear_Keys(void);
virtual void Set_Tightness(int i,float tightness);
virtual float Get_Tightness(int i);
virtual void Update_Tangents(void);
// save-load support
virtual const PersistFactoryClass & Get_Factory(void) const;
virtual bool Save(ChunkSaveClass &csave);
virtual bool Load(ChunkLoadClass &cload);
protected:
DynamicVectorClass<float> Tightness;
};
#endif

View File

@@ -0,0 +1,75 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/castres.h $*
* *
* Author:: Greg_h *
* *
* $Modtime:: 11/14/00 2:59p $*
* *
* $Revision:: 10 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef CASTRES_H
#define CASTRES_H
#include "always.h"
#include "vector3.h"
#include "bittype.h"
/**
** CastResultStruct
** Result of a volume or ray cast operation will be stored in the following structure
** NOTE: If you can avoid it, do not enable ComputeContactPoint. When casting rays, it is more
** efficient to use the resulting Fraction to compute the contact point outside of the
** collision detection code. In the case of AABox sweeping for character collision detection,
** you don't usually need the actual point of contact, etc etc.
**
** The default state of ComputeContactPoint is *false*
*/
struct CastResultStruct
{
CastResultStruct(void) { Reset(); }
void Reset(void) { StartBad = false; Fraction = 1.0f; Normal.Set(0,0,0); SurfaceType = 0; ComputeContactPoint = false; ContactPoint.Set(0,0,0); }
bool StartBad; // was the inital configuration interpenetrating something?
float Fraction; // fraction of the move up until collision
Vector3 Normal; // surface normal at the collision point
uint32 SurfaceType; // surface type of polygon at collision point (see W3D_SURFACE_TYPES in w3d_file.h)
bool ComputeContactPoint; // This signals the collision code to compute the point of collision
Vector3 ContactPoint; // This will be set to the point of collision if ComputeContactPoint is true
};
#endif

View File

@@ -0,0 +1,354 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/catmullromspline.cpp $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 3/08/00 8:50p $*
* *
* $Revision:: 6 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* CatmullRomSpline3DClass::Update_Tangents -- computes the tangents at each key *
* CatmullRomSpline3DClass::Get_Factory -- returns the factory for CatmullRomSpline3D *
* CatmullRomSpline3DClass::Save -- save this curve *
* CatmullRomSpline3DClass::Load -- load this curve *
* CatmullRomSpline1DClass::Update_Tangents -- Computes the tangents at each key *
* CatmullRomSpline1DClass::Get_Factory -- returns the factory for CatmullRomSpline1D *
* CatmullRomSpline1DClass::Save -- Save this curve *
* CatmullRomSpline1DClass::Load -- Load this curve *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "catmullromspline.h"
#include "persistfactory.h"
#include "wwmathids.h"
#include "wwhack.h"
/*
** Force-Link this module because the linker can't detect that we actually need it...
*/
DECLARE_FORCE_LINK(catmullromspline);
/*
** Save-Load stuff
*/
SimplePersistFactoryClass<CatmullRomSpline3DClass,WWMATH_CHUNKID_CATMULLROMSPLINE3D> _CatmullRomSpline3DFactory;
SimplePersistFactoryClass<CatmullRomSpline1DClass,WWMATH_CHUNKID_CATMULLROMSPLINE1D> _CatmullRomSpline1DFactory;
enum
{
// ID's used by CatmullRomSpline3D
CATMULLROM3D_CHUNK_HERMITE3D = 0x00020727,
// ID's used by CatmullRomSpline1D
CATMULLROM1D_CHUNK_HERMITE1D = 0x00020729,
};
/*
** Catmull-Rom 3D spline implementation
*/
/***********************************************************************************************
* CatmullRomSpline3DClass::Update_Tangents -- computes the tangents at each key *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* I'm not sure about the tangents for the endpoints of the curve. *
* *
* HISTORY: *
* 3/7/2000 gth : Created. *
*=============================================================================================*/
void CatmullRomSpline3DClass::Update_Tangents(void)
{
if (Keys.Count() < 2) {
for (int i=0; i<Keys.Count(); i++) {
Tangents[0].InTangent.Set(0,0,0);
Tangents[0].OutTangent.Set(0,0,0);
}
}
// first and last knot
int end = Keys.Count() - 1;
Tangents[0].InTangent.Set(0,0,0);
Tangents[end].OutTangent.Set(0,0,0);
if (IsLooping) {
// This really only works if the start and end points have the same position...
Tangents[0].OutTangent.X = 0.5f*(Keys[1].Point.X - Keys[end-1].Point.X);
Tangents[0].OutTangent.Y = 0.5f*(Keys[1].Point.Y - Keys[end-1].Point.Y);
Tangents[0].OutTangent.Z = 0.5f*(Keys[1].Point.Z - Keys[end-1].Point.Z);
Tangents[end].InTangent = Tangents[0].OutTangent;
} else {
// TODO: second derivative = 0... what is formula? I'm making this up...
Tangents[0].OutTangent.X = 0.25f*(Keys[1].Point.X - Keys[0].Point.X);
Tangents[0].OutTangent.Y = 0.25f*(Keys[1].Point.Y - Keys[0].Point.Y);
Tangents[0].OutTangent.Z = 0.25f*(Keys[1].Point.Z - Keys[0].Point.Z);
Tangents[end].InTangent.X = 0.25f*(Keys[end].Point.X - Keys[end-1].Point.X);
Tangents[end].InTangent.Y = 0.25f*(Keys[end].Point.Y - Keys[end-1].Point.Y);
Tangents[end].InTangent.Z = 0.25f*(Keys[end].Point.Z - Keys[end-1].Point.Z);
}
float total_time = (Keys[1].Time - Keys[0].Time) + (Keys[end].Time - Keys[end-1].Time);
float in_factor = 2.0f * (Keys[end].Time - Keys[end-1].Time) / total_time;
float out_factor = 2.0f * (Keys[1].Time - Keys[0].Time) / total_time;
Tangents[end].InTangent *= in_factor;
Tangents[0].OutTangent *= out_factor;
// inner knots
for (int i=1; i<Keys.Count()-1; i++) {
Tangents[i].InTangent.X = 0.5f*(Keys[i+1].Point.X - Keys[i-1].Point.X);
Tangents[i].InTangent.Y = 0.5f*(Keys[i+1].Point.Y - Keys[i-1].Point.Y);
Tangents[i].InTangent.Z = 0.5f*(Keys[i+1].Point.Z - Keys[i-1].Point.Z);
Tangents[i].OutTangent = Tangents[i].InTangent;
float in_factor = 2.0f * (Keys[i].Time - Keys[i-1].Time) / (Keys[i+1].Time - Keys[i-1].Time);
float out_factor = 2.0f * (Keys[i+1].Time - Keys[i].Time) / (Keys[i+1].Time - Keys[i-1].Time);
Tangents[i].InTangent *= in_factor; // compensating for the un-even keys
Tangents[i].OutTangent *= out_factor;
}
TangentsDirty = false;
}
/***********************************************************************************************
* CatmullRomSpline3DClass::Get_Factory -- returns the factory for CatmullRomSpline3D *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/7/2000 gth : Created. *
*=============================================================================================*/
const PersistFactoryClass & CatmullRomSpline3DClass::Get_Factory(void) const
{
return _CatmullRomSpline3DFactory;
}
/***********************************************************************************************
* CatmullRomSpline3DClass::Save -- save this curve *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/7/2000 gth : Created. *
*=============================================================================================*/
bool CatmullRomSpline3DClass::Save(ChunkSaveClass &csave)
{
csave.Begin_Chunk(CATMULLROM3D_CHUNK_HERMITE3D);
HermiteSpline3DClass::Save(csave);
csave.End_Chunk();
return true;
}
/***********************************************************************************************
* CatmullRomSpline3DClass::Load -- load this curve *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/7/2000 gth : Created. *
*=============================================================================================*/
bool CatmullRomSpline3DClass::Load(ChunkLoadClass &cload)
{
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID())
{
case CATMULLROM3D_CHUNK_HERMITE3D:
HermiteSpline3DClass::Load(cload);
break;
default:
WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",__FILE__,__LINE__));
break;
}
cload.Close_Chunk();
}
return true;
}
/*
** The 1D Catmull-Rom implementation.
*/
/***********************************************************************************************
* CatmullRomSpline1DClass::Update_Tangents -- Computes the tangents at each key *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/7/2000 gth : Created. *
*=============================================================================================*/
void CatmullRomSpline1DClass::Update_Tangents(void)
{
if (Keys.Count() < 2) {
for (int i=0; i<Keys.Count(); i++) {
Tangents[i].InTangent = 0.0f;
Tangents[i].OutTangent = 0.0f;
}
return;
}
// first and last knot
int end = Keys.Count() - 1;
Tangents[0].InTangent = 0.0f;
Tangents[end].OutTangent = 0.0f;
if (IsLooping) {
// This really only works if the start and end points have the same position...
Tangents[0].OutTangent = 0.5f*(Keys[1].Point - Keys[end-1].Point);
Tangents[end].InTangent = Tangents[0].OutTangent;
} else {
// TODO: second derivative = 0... what is formula? I'm making this up...
Tangents[0].OutTangent = 0.25f*(Keys[1].Point - Keys[0].Point);
Tangents[end].InTangent = 0.25f*(Keys[end].Point - Keys[end-1].Point);
}
float total_time = (Keys[1].Time - Keys[0].Time) + (Keys[end].Time - Keys[end-1].Time);
float in_factor = 2.0f * (Keys[end].Time - Keys[end-1].Time) / total_time;
float out_factor = 2.0f * (Keys[1].Time - Keys[0].Time) / total_time;
Tangents[end].InTangent *= in_factor;
Tangents[0].OutTangent *= out_factor;
// inner knots
for (int i=1; i<Keys.Count()-1; i++) {
Tangents[i].InTangent = 0.5f*(Keys[i+1].Point - Keys[i-1].Point);
Tangents[i].OutTangent = Tangents[i].InTangent;
float in_factor = 2.0f * (Keys[i].Time - Keys[i-1].Time) / (Keys[i+1].Time - Keys[i-1].Time);
float out_factor = 2.0f * (Keys[i+1].Time - Keys[i].Time) / (Keys[i+1].Time - Keys[i-1].Time);
Tangents[i].InTangent *= in_factor; // compensating for the un-even keys
Tangents[i].OutTangent *= out_factor;
}
TangentsDirty = false;
}
/***********************************************************************************************
* CatmullRomSpline1DClass::Get_Factory -- returns the factory for CatmullRomSpline1D *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/7/2000 gth : Created. *
*=============================================================================================*/
const PersistFactoryClass & CatmullRomSpline1DClass::Get_Factory(void) const
{
return _CatmullRomSpline1DFactory;
}
/***********************************************************************************************
* CatmullRomSpline1DClass::Save -- Save this curve *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/7/2000 gth : Created. *
*=============================================================================================*/
bool CatmullRomSpline1DClass::Save(ChunkSaveClass &csave)
{
csave.Begin_Chunk(CATMULLROM1D_CHUNK_HERMITE1D);
HermiteSpline1DClass::Save(csave);
csave.End_Chunk();
return true;
}
/***********************************************************************************************
* CatmullRomSpline1DClass::Load -- Load this curve *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/7/2000 gth : Created. *
*=============================================================================================*/
bool CatmullRomSpline1DClass::Load(ChunkLoadClass &cload)
{
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID())
{
case CATMULLROM1D_CHUNK_HERMITE1D:
HermiteSpline1DClass::Load(cload);
break;
default:
WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",__FILE__,__LINE__));
break;
}
cload.Close_Chunk();
}
return true;
}

View File

@@ -0,0 +1,78 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/catmullromspline.h $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 3/07/00 9:51a $*
* *
* $Revision:: 6 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef CATMULLROMSPLINE_H
#define CATMULLROMSPLINE_H
#include "hermitespline.h"
/**
** CatmullRomSpline3DClass
** This is is an implementation of 3D catmull-rom splines
*/
class CatmullRomSpline3DClass : public HermiteSpline3DClass
{
public:
void Update_Tangents(void);
// save-load support
virtual const PersistFactoryClass & Get_Factory(void) const;
virtual bool Save(ChunkSaveClass &csave);
virtual bool Load(ChunkLoadClass &cload);
};
/**
** CatmullRomSpline1DClass
** This is is an implementation of 1D catmull-rom splines
*/
class CatmullRomSpline1DClass : public HermiteSpline1DClass
{
public:
void Update_Tangents(void);
// save-load support
virtual const PersistFactoryClass & Get_Factory(void) const;
virtual bool Save(ChunkSaveClass &csave);
virtual bool Load(ChunkLoadClass &cload);
};
#endif

View File

@@ -0,0 +1,68 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/colmath.cpp $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 3/16/00 2:19p $*
* *
* $Revision:: 4 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "colmath.h"
const float CollisionMath::COINCIDENCE_EPSILON = 0.000001f;
CollisionMath::ColmathStatsStruct CollisionMath::Stats;
CollisionMath::ColmathStatsStruct::ColmathStatsStruct(void)
{
Reset();
}
void CollisionMath::ColmathStatsStruct::Reset(void)
{
TotalCollisionCount = 0;
TotalCollisionHitCount = 0;
CollisionRayTriCount = 0;
CollisionRayTriHitCount = 0;
CollisionAABoxTriCount = 0;
CollisionAABoxTriHitCount = 0;
CollisionAABoxAABoxCount = 0;
CollisionAABoxAABoxHitCount = 0;
CollisionOBBoxTriCount = 0;
CollisionOBBoxTriHitCount = 0;
CollisionOBBoxAABoxCount = 0;
CollisionOBBoxAABoxHitCount = 0;
CollisionOBBoxOBBoxCount = 0;
CollisionOBBoxOBBoxHitCount = 0;
}

View File

@@ -0,0 +1,312 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/colmath.h $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 5/04/01 8:25p $*
* *
* $Revision:: 19 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef COLMATH_H
#define COLMATH_H
#ifndef ALWAYS_H
#include "always.h"
#endif
#ifndef VECTOR3_H
#include "vector3.h"
#endif
#ifndef CASTRES_H
#include "castres.h"
#endif
class AAPlaneClass;
class PlaneClass;
class LineSegClass;
class TriClass;
class SphereClass;
class AABoxClass;
class OBBoxClass;
class FrustumClass;
const float COLLISION_EPSILON = 0.001f;
/*
** #define COLMATH_STAT_TRACKING to enable stat tracking for the collision math functions
*/
#ifdef WWDEBUG
#define COLMATH_STAT_TRACKING
#endif
/**
** CollisionMath
** This is a collection of the low-level math functions for collision detection.
*/
class CollisionMath
{
public:
////////////////////////////////////////////////////////////////////////////////////////
// Intersect Functions.
// These functions simply return a bool indicating whether the two operands intersect.
////////////////////////////////////////////////////////////////////////////////////////
static bool Intersection_Test(const AABoxClass & box,const TriClass & tri);
static bool Intersection_Test(const AABoxClass & box,const AABoxClass & box2);
static bool Intersection_Test(const AABoxClass & box,const OBBoxClass & box2);
static bool Intersection_Test(const OBBoxClass & box,const TriClass & tri);
static bool Intersection_Test(const OBBoxClass & box,const AABoxClass & box2);
static bool Intersection_Test(const OBBoxClass & box,const OBBoxClass & box2);
static bool Intersection_Test(const SphereClass & sphere,const AABoxClass & box);
static bool Intersection_Test(const SphereClass & sphere,const OBBoxClass & box);
////////////////////////////////////////////////////////////////////////////////////////
// Overlap Functions.
// Classify the second operand with respect to the first operand.
// For example Overlap_Test(plane,point) tests whether 'point' is in front of or
// behind 'plane'.
// OverlapType: This enumeration is the result of an overlap test.
// It indicates whether the the object is in the positive (front/outside) space
// of the volume, the negative (back/inside) space of the volume, or both (overlapping)
////////////////////////////////////////////////////////////////////////////////////////
enum OverlapType
{
POS = 0x01,
NEG = 0x02,
ON = 0x04,
BOTH = 0x08,
OUTSIDE = POS,
INSIDE = NEG,
OVERLAPPED = BOTH,
FRONT = POS,
BACK = NEG
};
// AAPlane functions. Where is operand B with respect to this AAPlane
static OverlapType Overlap_Test(const AAPlaneClass & plane,const Vector3 & point);
static OverlapType Overlap_Test(const AAPlaneClass & plane,const LineSegClass & line);
static OverlapType Overlap_Test(const AAPlaneClass & plane,const TriClass & tri);
static OverlapType Overlap_Test(const AAPlaneClass & plane,const SphereClass & sphere);
static OverlapType Overlap_Test(const AAPlaneClass & plane,const AABoxClass & box);
static OverlapType Overlap_Test(const AAPlaneClass & plane,const OBBoxClass & box);
// Plane functions. Where is operand B with respect to the plane
static OverlapType Overlap_Test(const PlaneClass & plane,const Vector3 & point);
static OverlapType Overlap_Test(const PlaneClass & plane,const LineSegClass & line);
static OverlapType Overlap_Test(const PlaneClass & plane,const TriClass & tri);
static OverlapType Overlap_Test(const PlaneClass & plane,const SphereClass & sphere);
static OverlapType Overlap_Test(const PlaneClass & plane,const Vector3 & center,const Vector3 & extent);
inline static OverlapType Overlap_Test(const PlaneClass & plane,const AABoxClass & box);
static OverlapType Overlap_Test(const PlaneClass & plane,const OBBoxClass & box);
// Sphere functions. Where is operand B with respect to the sphere
static OverlapType Overlap_Test(const SphereClass & sphere,const Vector3 & point);
static OverlapType Overlap_Test(const SphereClass & sphere,const LineSegClass & line);
static OverlapType Overlap_Test(const SphereClass & sphere,const TriClass & tri);
static OverlapType Overlap_Test(const SphereClass & sphere,const SphereClass & sphere2);
static OverlapType Overlap_Test(const SphereClass & sphere,const AABoxClass & aabox);
static OverlapType Overlap_Test(const SphereClass & sphere,const OBBoxClass & obbox);
// AABox functions. Where is operand B with respect to the AABox
static OverlapType Overlap_Test(const AABoxClass & box,const Vector3 & point);
static OverlapType Overlap_Test(const AABoxClass & box,const LineSegClass & line);
static OverlapType Overlap_Test(const AABoxClass & box,const TriClass & tri);
static OverlapType Overlap_Test(const AABoxClass & box,const AABoxClass & box2);
static OverlapType Overlap_Test(const AABoxClass & box,const OBBoxClass & obbox);
static OverlapType Overlap_Test(const AABoxClass & box,const SphereClass & sphere);
// OBBox functions, where is operand B with respect to the OBBox
static OverlapType Overlap_Test(const OBBoxClass & box,const Vector3 & point);
static OverlapType Overlap_Test(const OBBoxClass & box,const LineSegClass & line);
static OverlapType Overlap_Test(const OBBoxClass & box,const TriClass & tri);
static OverlapType Overlap_Test(const OBBoxClass & box,const AABoxClass & box2);
static OverlapType Overlap_Test(const OBBoxClass & box,const OBBoxClass & box2);
// Frustum functions
static OverlapType Overlap_Test(const FrustumClass & frustum,const Vector3 & point);
static OverlapType Overlap_Test(const FrustumClass & frustum,const TriClass & tri);
static OverlapType Overlap_Test(const FrustumClass & frustum,const SphereClass & sphere);
static OverlapType Overlap_Test(const FrustumClass & frustum,const AABoxClass & box);
static OverlapType Overlap_Test(const FrustumClass & frustum,const OBBoxClass & box);
// Frustum functions for hierachical culling systems.
// At your root node, just pass in planes_passed = 0, then the variable will be modified to
// indicate which planes that volume was inside. You can then pass that value in for the
// test of all child nodes and optimize away some of the tests. See AABTreeCullSystemClass
// for an example usage.
static OverlapType Overlap_Test(const FrustumClass & frustum,const AABoxClass & box,int & planes_passed);
static OverlapType Overlap_Test(const FrustumClass & frustum,const OBBoxClass & box,int & planes_passed);
// Miscellaneous other Overlap tests
static OverlapType Overlap_Test(const Vector3 & min,const Vector3 & max,const LineSegClass & line);
////////////////////////////////////////////////////////////////////////////////////////
// Collision Functions.
// Collide the first operand into the last operand.
// For example Collide(box,move,tri) tests for collision between a box moving into
// a triangle.
////////////////////////////////////////////////////////////////////////////////////////
// Line segment functions. Intersect this line segment with the given object
static bool Collide(const LineSegClass & line,const AAPlaneClass & plane,CastResultStruct * result);
static bool Collide(const LineSegClass & line,const PlaneClass & plane,CastResultStruct * result);
static bool Collide(const LineSegClass & line,const TriClass & tri,CastResultStruct * result);
static bool Collide(const LineSegClass & line,const SphereClass & sphere,CastResultStruct * result);
static bool Collide(const LineSegClass & line,const AABoxClass & box,CastResultStruct * result);
static bool Collide(const LineSegClass & line,const OBBoxClass & box,CastResultStruct * result);
// AABox functions
static bool Collide(const AABoxClass & box,const Vector3 & move,const PlaneClass & plane,CastResultStruct * result);
static bool Collide(const AABoxClass & box,const Vector3 & move,const TriClass & tri,CastResultStruct * result);
static bool Collide(const AABoxClass & box,const Vector3 & move,const AABoxClass & box2,CastResultStruct * result);
static bool Collide(const AABoxClass & box,const Vector3 & move,const OBBoxClass & box2,const Vector3 & move2,CastResultStruct * result);
// OBBox functions
static bool Collide(const OBBoxClass & box,const Vector3 & move,const PlaneClass & plane,CastResultStruct * result);
static bool Collide(const OBBoxClass & box,const Vector3 & move,const TriClass & tri,const Vector3 & move2,CastResultStruct * result);
static bool Collide(const OBBoxClass & box,const Vector3 & move,const AABoxClass & box2,const Vector3 & move2,CastResultStruct * result);
static bool Collide(const OBBoxClass & box,const Vector3 & move,const OBBoxClass & box2,const Vector3 & move2,CastResultStruct * result);
////////////////////////////////////////////////////////////////////////////////////////
// Stats
// Note that these functions will only work if you have stat tracking enabled
////////////////////////////////////////////////////////////////////////////////////////
struct ColmathStatsStruct
{
ColmathStatsStruct(void);
void Reset(void);
int TotalCollisionCount;
int TotalCollisionHitCount;
int CollisionRayTriCount;
int CollisionRayTriHitCount;
int CollisionAABoxTriCount;
int CollisionAABoxTriHitCount;
int CollisionAABoxAABoxCount;
int CollisionAABoxAABoxHitCount;
int CollisionOBBoxTriCount;
int CollisionOBBoxTriHitCount;
int CollisionOBBoxAABoxCount;
int CollisionOBBoxAABoxHitCount;
int CollisionOBBoxOBBoxCount;
int CollisionOBBoxOBBoxHitCount;
};
static void Reset_Stats(void) { Stats.Reset(); }
static const ColmathStatsStruct & Get_Current_Stats(void) { return Stats; }
private:
static OverlapType eval_overlap_mask(int mask);
static OverlapType eval_overlap_collision(const CastResultStruct & res);
static const float COINCIDENCE_EPSILON;
static ColmathStatsStruct Stats;
};
inline CollisionMath::OverlapType CollisionMath::eval_overlap_mask(int mask)
{
// check if all verts are "ON"
if (mask == ON) {
return ON;
}
// check if all verts are either "ON" or "POS"
if ((mask & ~(POS | ON)) == 0) {
return POS;
}
// check if all verts are either "ON" or "BACK"
if ((mask & ~(NEG | ON)) == 0) {
return NEG;
}
// otherwise, poly spans the plane.
return BOTH;
}
inline CollisionMath::OverlapType CollisionMath::eval_overlap_collision(const CastResultStruct & res)
{
if (res.Fraction < 1.0f) {
return BOTH;
} else {
if (res.StartBad) {
return NEG;
} else {
return POS;
}
}
}
/*
** Stat tracking Macros
*/
#ifdef COLMATH_STAT_TRACKING
#define TRACK_COLLISION_RAY_TRI Stats.CollisionRayTriCount++; Stats.TotalCollisionCount++;
#define TRACK_COLLISION_RAY_TRI_HIT Stats.CollisionRayTriHitCount++; Stats.TotalCollisionHitCount;
#define TRACK_COLLISION_AABOX_TRI Stats.CollisionAABoxTriCount++; Stats.TotalCollisionCount++;
#define TRACK_COLLISION_AABOX_TRI_HIT Stats.CollisionAABoxTriHitCount++; Stats.TotalCollisionHitCount++;
#define TRACK_COLLISION_AABOX_AABOX Stats.CollisionAABoxAABoxCount++; Stats.TotalCollisionCount++;
#define TRACK_COLLISION_AABOX_AABOX_HIT Stats.CollisionAABoxAABoxHitCount++; Stats.TotalCollisionHitCount++;
#define TRACK_COLLISION_OBBOX_TRI Stats.CollisionOBBoxTriCount++; Stats.TotalCollisionCount++;
#define TRACK_COLLISION_OBBOX_TRI_HIT Stats.CollisionOBBoxTriHitCount++; Stats.TotalCollisionHitCount++;
#define TRACK_COLLISION_OBBOX_AABOX Stats.CollisionOBBoxAABoxCount++; Stats.TotalCollisionCount++;
#define TRACK_COLLISION_OBBOX_AABOX_HIT Stats.CollisionOBBoxAABoxHitCount++; Stats.TotalCollisionHitCount++;
#define TRACK_COLLISION_OBBOX_OBBOX Stats.CollisionOBBoxOBBoxCount++; Stats.TotalCollisionCount++;
#define TRACK_COLLISION_OBBOX_OBBOX_HIT Stats.CollisionOBBoxOBBoxHitCount++; Stats.TotalCollisionHitCount++;
#else
#define TRACK_COLLISION_RAY_TRI
#define TRACK_COLLISION_RAY_TRI_HIT
#define TRACK_COLLISION_AABOX_TRI
#define TRACK_COLLISION_AABOX_TRI_HIT
#define TRACK_COLLISION_AABOX_AABOX
#define TRACK_COLLISION_AABOX_AABOX_HIT
#define TRACK_COLLISION_OBBOX_TRI
#define TRACK_COLLISION_OBBOX_TRI_HIT
#define TRACK_COLLISION_OBBOX_AABOX
#define TRACK_COLLISION_OBBOX_AABOX_HIT
#define TRACK_COLLISION_OBBOX_OBBOX
#define TRACK_COLLISION_OBBOX_OBBOX_HIT
#endif
#endif // COLMATH_H

View File

@@ -0,0 +1,596 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/colmathaabox.cpp $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 8/30/01 7:40p $*
* *
* $Revision:: 22 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* CollisionMath::Intersection_Test -- Test intersection between two AABoxes *
* CollisionMath::Overlap_Test -- Tests overlap between an AABox and a sphere *
* CollisionMath::Overlap_Test -- Tests overlap between an AABox and a triangle *
* CollisionMath::Overlap_Test -- Tests overlap between an AABox and a line segment *
* CollisionMath::Collide -- Collision test for a moving AABox and a plane *
* aab_separation_test -- tests two AAB's for separation on an axis *
* CollisionMath::Collide -- Collision test for two moving AABoxes *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "colmath.h"
#include "colmathinlines.h"
#include "aaplane.h"
#include "plane.h"
#include "lineseg.h"
#include "tri.h"
#include "sphere.h"
#include "aabox.h"
#include "obbox.h"
#include "wwdebug.h"
/***********************************************************************************************
* CollisionMath::Intersection_Test -- Test intersection between two AABoxes *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/19/99 gth : Created. *
*=============================================================================================*/
bool CollisionMath::Intersection_Test(const AABoxClass & box,const AABoxClass & box2)
{
Vector3 dc = box2.Center - box.Center;
if (box.Extent.X + box2.Extent.X < WWMath::Fabs(dc.X)) return false;
if (box.Extent.Y + box2.Extent.Y < WWMath::Fabs(dc.Y)) return false;
if (box.Extent.Z + box2.Extent.Z < WWMath::Fabs(dc.Z)) return false;
return true;
}
/***********************************************************************************************
* CollisionMath::Overlap_Test -- Tests overlap between an AABox and a sphere *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/19/99 gth : Created. *
*=============================================================================================*/
CollisionMath::OverlapType CollisionMath::Overlap_Test(const AABoxClass & box,const SphereClass & sphere)
{
// TODO: this function seems incorrect. Not currently using it though
Vector3 dist = sphere.Center - box.Center;
Vector3 extent;
extent.X = box.Extent.X + sphere.Radius;
extent.Y = box.Extent.Y + sphere.Radius;
extent.Z = box.Extent.Z + sphere.Radius;
//
// Check to see if the sphere is completely outside the box
//
if (WWMath::Fabs(dist.X) > extent.X) return OUTSIDE;
if (WWMath::Fabs(dist.Y) > extent.Y) return OUTSIDE;
if (WWMath::Fabs(dist.Z) > extent.Z) return OUTSIDE;
return INSIDE;
}
/***********************************************************************************************
* CollisionMath::Overlap_Test -- Tests overlap between an AABox and a line segment *
* *
* This function uses separating axes to determine whether an AABox and a line segment overlap *
* I wrote it when we found that ray-casting was taking a huge amount of time in Renegade. *
* Here are some statistics comparing this function to the previous function which used the *
* same algorithm that Collide(box,ray) uses. *
* *
* collide algorithm (Gems) separating axis (this one) *
* ----------------------------------------------------------------------------------------- *
* number of tests 10000 10000 *
* avg cycles 641 464 *
* outside cycles 652 390 *
* overlap cycles 610 683 *
* *
* The idea for this test is that it will early-exit when it can while the old test can't. *
* When we are passing a ray down our culling trees, it is common to encounter boxes that *
* are not intersecting the ray. The faster we can reject these, the better! *
* *
* INPUT: *
* box - box to test *
* line - line to test *
* *
* OUTPUT: *
* overlap status of the line with respect to the box, either INSIDE, OUTSIDE, or OVERLAPPED *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/19/99 gth : Created. *
*=============================================================================================*/
CollisionMath::OverlapType CollisionMath::Overlap_Test(const AABoxClass & box,const LineSegClass & line)
{
// If both endpoints are in, return INSIDE
// If either was inside, return OVERLAPPED
int inside_point_count = 0;
if (CollisionMath::Overlap_Test(box,line.Get_P0()) == INSIDE) inside_point_count++;
if (CollisionMath::Overlap_Test(box,line.Get_P1()) == INSIDE) inside_point_count++;
if (inside_point_count == 2) {
return INSIDE;
} else if (inside_point_count == 1) {
return OVERLAPPED;
} else {
// Here I'm using the separating axis theorem to test if the line-segment
// and the box can be separated by a plane. Any two convex objects that are
// not intersecting can be separated by a plane defined by either a
// face normal from one of the objects or the cross-product of an edge from
// each object. In the case of an axis-aligned box and a line-segment, we
// have to check the three coordinate axes and the cross product between
// each and the direction vector of the line segment.
//
// Here is the general test for an arbitrary axis:
// -----------------------------------------------
// box_proj = fabs(extent.X*axis.X) + fabs(extent.Y*axis.Y) + fabs(extent.Z*axis.Z)
// p0_proj = fabs(dot_product(dp0,axis))
// dp_proj = fabs(dot_product(dp,axis))
// if (p0_proj > 0) {
// if (p0_proj > box_proj - WWMath::Min(dp_proj,0.0f)) return OUTSIDE;
// } else {
// if (-p0_proj > box_proj + WWMath::Max(dp_proj,0.0f)) return OUTSIDE;
// }
//
// In practice, there are optimizations you can make on each of the axes that
// we need to test (see below).
float box_proj,p0_proj,dp_proj;
Vector3 dp0;
Vector3::Subtract(line.Get_P0(),box.Center,&dp0);
// Project box and line onto the x-axis
// Since I know the axis is the x-axis, just take the x components of each
// vector instead of doing the dot-product. The projection of 'dp' is only
// counted if it points towards the center of the box (i.e. it decreases the
// chances of them being separated...)
box_proj = box.Extent.X;
p0_proj = dp0.X;
dp_proj = line.Get_DP().X;
if (p0_proj > 0) {
if (p0_proj > box_proj - WWMath::Min(dp_proj,0.0f)) return OUTSIDE;
} else {
if (-p0_proj > box_proj + WWMath::Max(dp_proj,0.0f)) return OUTSIDE;
}
// Project box and line onto the y-axis
box_proj = box.Extent.Y;
p0_proj = dp0.Y;
dp_proj = line.Get_DP().Y;
if (p0_proj > 0) {
if (p0_proj > box_proj - WWMath::Min(dp_proj,0.0f)) return OUTSIDE;
} else {
if (-p0_proj > box_proj + WWMath::Max(dp_proj,0.0f)) return OUTSIDE;
}
// Project box and line onto the z-axis
box_proj = box.Extent.Z;
p0_proj = dp0.Z;
dp_proj = line.Get_DP().Z;
if (p0_proj > 0) {
if (p0_proj > box_proj - WWMath::Min(dp_proj,0.0f)) return OUTSIDE;
} else {
if (-p0_proj > box_proj + WWMath::Max(dp_proj,0.0f)) return OUTSIDE;
}
// Project box and line onto (x cross line)
// I'm manually optimizing the cross-product and taking advantage of the fact
// that 'dp' will always project to zero for this axis.
Vector3 axis;
axis.Set(0,-line.Get_Dir().Z,line.Get_Dir().Y); // == (1,0,0) cross (x,y,z)
box_proj = WWMath::Fabs(axis.Y*box.Extent.Y) + WWMath::Fabs(axis.Z*box.Extent.Z);
p0_proj = Vector3::Dot_Product(axis,dp0);
if (WWMath::Fabs(p0_proj) > box_proj) return OUTSIDE;
// Project box and line onto (y cross line)
axis.Set(line.Get_Dir().Z,0,-line.Get_Dir().X); // == (0,1,0) cross (x,y,z)
box_proj = WWMath::Fabs(axis.X*box.Extent.X) + WWMath::Fabs(axis.Z*box.Extent.Z);
p0_proj = Vector3::Dot_Product(axis,dp0);
if (WWMath::Fabs(p0_proj) > box_proj) return OUTSIDE;
// Project box and line onto (z cross line)
axis.Set(-line.Get_Dir().Y,line.Get_Dir().X,0); // == (0,0,1) cross (x,y,z)
box_proj = WWMath::Fabs(axis.X*box.Extent.X) + WWMath::Fabs(axis.Y*box.Extent.Y);
p0_proj = Vector3::Dot_Product(axis,dp0);
if (WWMath::Fabs(p0_proj) > box_proj) return OUTSIDE;
}
return OVERLAPPED;
}
#if 0 // Alternate Overlap Test for AABox-Ray
CollisionMath::OverlapType CollisionMath::Overlap_Test(const AABoxClass & box,const LineSegClass & line)
{
// If both endpoints are in, return INSIDE
// If either was inside, return OVERLAPPED
int inside_point_count = 0;
if (CollisionMath::Overlap_Test(box,line.Get_P0()) == INSIDE) inside_point_count++;
if (CollisionMath::Overlap_Test(box,line.Get_P1()) == INSIDE) inside_point_count++;
if (inside_point_count == 2) {
return INSIDE;
} else if (inside_point_count == 1) {
return OVERLAPPED;
} else {
// Now we know that both points are outside of the box so we
// have to detect if the ray penetrates the box.
// I found this algorithm in one of the GEMS books...
Vector3 boxmin,boxmax;
Vector3::Subtract(box.Center,box.Extent,&boxmin);
Vector3::Add(box.Center,box.Extent,&boxmax);
float candidateplane[3]; // candidate intersection plane distance for each axis
float maxt[3]; // t value along the ray for each axis
Vector3 coord; // intersection point
const int BOX_SIDE_NEGATIVE = -1;
const int BOX_SIDE_MIDDLE = 0;
const int BOX_SIDE_POSITIVE = 1;
int quadrant[3];
for (int i=0; i<3; i++) {
if (line.Get_P0()[i] < boxmin[i]) {
quadrant[i] = BOX_SIDE_NEGATIVE;
candidateplane[i] = boxmin[i];
} else if (line.Get_P0()[i] > boxmax[i]) {
quadrant[i] = BOX_SIDE_POSITIVE;
candidateplane[i] = boxmax[i];
} else {
quadrant[i] = BOX_SIDE_MIDDLE;
}
}
// Calculate the 3 distances to the candidate planes
for (i=0; i<3; i++) {
if (quadrant[i] != BOX_SIDE_MIDDLE && line.Get_DP()[i] != 0.0f) {
maxt[i] = (candidateplane[i] - line.Get_P0()[i]) / line.Get_DP()[i];
} else {
maxt[i] = -1.0f;
}
}
// Get the largest of the maxt's for the final choice of intersection
int intersection_plane = 0;
for (i=1; i<3; i++) {
if (maxt[i] > maxt[intersection_plane]) {
intersection_plane = i;
}
}
// If the ray is "in front" of all of the planes, return
if (maxt[intersection_plane] < 0.0f) {
return OUTSIDE;
}
// If the intersection is beyond the end of the ray, return
if (maxt[intersection_plane] > 1.0f) {
return OUTSIDE;
}
// Check if the ray is inside the box, i.e. on the two planes which
// are not the intersection planes, the intersection point should
// be between the min and max of the box.
for (i=0; i<3; i++) {
if (intersection_plane != i) {
coord[i] = line.Get_P0()[i] + maxt[intersection_plane] * line.Get_DP()[i];
if ((coord[i] < boxmin[i]) || (coord[i] > boxmax[i])) {
return OUTSIDE; // ray is outside the box
}
} else {
coord[i] = candidateplane[i];
}
}
}
return OVERLAPPED;
}
#endif // Alternate Overlap Test for AABox-Ray
/***********************************************************************************************
* CollisionMath::Overlap_Test -- Tests overlap between an AABox and a triangle *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/19/99 gth : Created. *
*=============================================================================================*/
CollisionMath::OverlapType CollisionMath::Overlap_Test(const AABoxClass & box,const TriClass & tri)
{
CastResultStruct res;
CollisionMath::Collide(box,Vector3(0,0,0),tri,&res);
return eval_overlap_collision(res);
}
/***********************************************************************************************
* CollisionMath::Collide -- Collision test for a moving AABox and a plane *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/19/99 gth : Created. *
*=============================================================================================*/
bool CollisionMath::Collide
(
const AABoxClass & box,
const Vector3 & move_vector,
const PlaneClass & plane,
CastResultStruct * result
)
{
float frac;
float extent = box.Project_To_Axis(plane.N);
float dist = Vector3::Dot_Product(plane.N,box.Center) + plane.D;
float move = Vector3::Dot_Product(plane.N,move_vector);
if (dist > extent) {
if (dist + move > extent) {
// entire move ok!
frac = 1.0f;
} else {
// partial move allowed
frac = (extent - dist) / move;
}
} else if (dist < -extent) {
if (dist + move < -extent) {
// entire move ok!
frac = 1.0f;
} else {
// partial move allowed
frac = (-extent - dist) / move;
}
} else {
result->StartBad = true;
result->Normal = plane.N;
return true;
}
if (frac < result->Fraction) {
result->Fraction = frac;
result->Normal = plane.N;
if (result->ComputeContactPoint) {
Vector3 move_dir(move_vector);
move_dir.Normalize();
float move_extent = Vector3::Dot_Product(box.Extent,move_dir);
result->ContactPoint = box.Center + result->Fraction * move_vector + move_extent * move_dir;
}
return true;
}
return false;
}
/*
** AABCollisionStruct
** Contains all of the intermediate and temporary values used by
** the set of functions used in detecting collisions for aab's
*/
struct AABCollisionStruct
{
AABCollisionStruct(const AABoxClass &box0,const Vector3 &move0,const AABoxClass & box1,const Vector3 &move1) :
StartBad(true), // Startbad is true until one of the axes clears it
AxisId(-1), // AxisId will be the axis that allowed the longest move
MaxFrac(0.0f), // MaxFrac is the longest allowed move so far
Box0(box0),
Box1(box1)
{
Vector3::Subtract(box1.Center,box0.Center,&C); // vector from center of box0 to center of box1
Vector3::Subtract(move1,move0,&M); // move vector relative to stationary box0
}
bool StartBad; // Inital configuration is intersecting?
float MaxFrac; // Longest move allowed so far
int AxisId; // Last separating axis
int Side; // which side of the interval
Vector3 C; // Vector from the center0 to center1
Vector3 M; // Move vector relative to stationary box0
const AABoxClass & Box0;
const AABoxClass & Box1;
private:
//not implemented
AABCollisionStruct(const AABCollisionStruct&);
AABCollisionStruct & operator = (const AABCollisionStruct&);
};
/***********************************************************************************************
* aab_separation_test -- tests two AAB's for separation on an axis *
* *
* This function is very similar to the obb_separation_test. If a flaw is found in either, *
* we should update the other... *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/19/99 gth : Created. *
*=============================================================================================*/
static inline bool aab_separation_test
(
AABCollisionStruct & context,
int axis
)
{
// ra = box0 projection onto the axis
// rb = box1 projection onto the axis
// u0 = projected distance between the box centers at t0
// u1 = projected distance between the box centers at t1
float ra = context.Box0.Extent[axis];
float rb = context.Box1.Extent[axis];
float u0 = context.C[axis];
float u1 = u0 + context.M[axis];
float tmp;
float rsum = ra+rb;
if ( u0 + WWMATH_EPSILON > rsum ) {
context.StartBad = false;
if ( u1 > rsum ) {
context.MaxFrac = 1.0f;
return true;
} else {
tmp = (rsum-u0)/(u1-u0);
if ( tmp > context.MaxFrac ) {
context.MaxFrac = tmp;
context.AxisId = axis;
context.Side = +1;
}
}
} else if ( u0 - WWMATH_EPSILON < -rsum ) {
context.StartBad = false;
if ( u1 < -rsum ) {
context.MaxFrac = 1.0f;
return true;
} else {
tmp = (-rsum-u0)/(u1-u0);
if ( tmp > context.MaxFrac ) {
context.MaxFrac = tmp;
context.AxisId = axis;
context.Side = -1;
}
}
}
return false;
}
/***********************************************************************************************
* CollisionMath::Collide -- Collision test for two moving AABoxes *
* *
* this function sweeps two AABoxes and finds the first time of collision. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* currently there is no parateter for the movement of the second box. the internal code *
* can handle it though. *
* *
* HISTORY: *
* 11/19/99 gth : Created. *
*=============================================================================================*/
bool CollisionMath::Collide(const AABoxClass & box,const Vector3 & move,const AABoxClass & box2,CastResultStruct * result)
{
/*
** Test the X-axis
** ra = box projection onto the axis
** rb = box2 projection onto the axis
** u0 = projected distance between the box centers at t0
** u1 = projected distance between the box centers at t1
*/
AABCollisionStruct context(box,move,box2,Vector3(0,0,0));
if (aab_separation_test(context,0)) {
goto exit;
}
if (aab_separation_test(context,1)) {
goto exit;
}
if (aab_separation_test(context,2)) {
goto exit;
}
exit:
if (context.StartBad) {
result->StartBad = true;
result->Fraction = 0.0f;
return true;
}
if (context.MaxFrac < result->Fraction) {
result->Fraction = context.MaxFrac;
result->Normal.Set(0,0,0);
result->Normal[context.AxisId] = -context.Side;
if (result->ComputeContactPoint) {
//WWASSERT(0); // TODO
WWDEBUG_SAY(("AABox-AABox collision does not currently support contact point computation\r\n"));
}
return true;
}
return false;
}

View File

@@ -0,0 +1,110 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/colmathaabox.h $*
* *
* Original Author:: Greg Hjelstrom *
* *
* $Author:: Jani_p $*
* *
* $Modtime:: 5/08/01 5:01p $*
* *
* $Revision:: 8 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* CollisionMath::Overlap_Test -- test overlap between an AABox and a point *
* CollisionMath::Overlap_Test -- Tests overlap between two AABoxes *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef COLMATHAABOX_H
#define COLMATHAABOX_H
#include "always.h"
#include "aabox.h"
#include "vector3.h"
#include "lineseg.h"
/***********************************************************************************************
* CollisionMath::Overlap_Test -- test overlap between an AABox and a point *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/14/2000 gth : Created. *
*=============================================================================================*/
WWINLINE CollisionMath::OverlapType CollisionMath::Overlap_Test(const AABoxClass & box,const Vector3 & point)
{
if (WWMath::Fabs(point.X - box.Center.X) > box.Extent.X) return POS;
if (WWMath::Fabs(point.Y - box.Center.Y) > box.Extent.Y) return POS;
if (WWMath::Fabs(point.Z - box.Center.Z) > box.Extent.Z) return POS;
return NEG;
}
/***********************************************************************************************
* CollisionMath::Overlap_Test -- Tests overlap between two AABoxes *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/19/99 gth : Created. *
*=============================================================================================*/
WWINLINE CollisionMath::OverlapType CollisionMath::Overlap_Test(const AABoxClass & box,const AABoxClass & box2)
{
Vector3 dc;
Vector3::Subtract(box2.Center,box.Center,&dc);
if (box.Extent.X + box2.Extent.X < WWMath::Fabs(dc.X)) return POS;
if (box.Extent.Y + box2.Extent.Y < WWMath::Fabs(dc.Y)) return POS;
if (box.Extent.Z + box2.Extent.Z < WWMath::Fabs(dc.Z)) return POS;
if ( (dc.X + box2.Extent.X <= box.Extent.X) &&
(dc.Y + box2.Extent.Y <= box.Extent.Y) &&
(dc.Z + box2.Extent.Z <= box.Extent.Z) &&
(dc.X - box2.Extent.X >= -box.Extent.X) &&
(dc.Y - box2.Extent.Y >= -box.Extent.Y) &&
(dc.Z - box2.Extent.Z >= -box.Extent.Z))
{
return NEG; // inside;
}
return BOTH;
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,190 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/colmathfrustum.cpp $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 3/29/00 5:40p $*
* *
* $Revision:: 7 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "colmath.h"
#include "colmathinlines.h"
#include "aaplane.h"
#include "plane.h"
#include "lineseg.h"
#include "tri.h"
#include "sphere.h"
#include "aabox.h"
#include "obbox.h"
#include "frustum.h"
#include "wwdebug.h"
// TODO: Most of these overlap functions actually do not catch all cases of when
// the primitive is outside of the frustum...
// Frustum functions
CollisionMath::OverlapType
CollisionMath::Overlap_Test(const FrustumClass & frustum,const Vector3 & point)
{
int mask = 0;
for (int i = 0; i < 6; i++) {
int result = CollisionMath::Overlap_Test(frustum.Planes[i],point);
if (result == OUTSIDE) {
return OUTSIDE;
}
mask |= result;
}
if (mask == INSIDE) {
return INSIDE;
}
return OVERLAPPED;
}
CollisionMath::OverlapType
CollisionMath::Overlap_Test(const FrustumClass & frustum,const TriClass & tri)
{
int mask = 0;
// TODO: doesn't catch all cases...
for (int i = 0; i < 6; i++) {
int result = CollisionMath::Overlap_Test(frustum.Planes[i],tri);
if (result == OUTSIDE) {
return OUTSIDE;
}
mask |= result;
}
if (mask == INSIDE) {
return INSIDE;
}
return OVERLAPPED;
}
CollisionMath::OverlapType
CollisionMath::Overlap_Test(const FrustumClass & frustum,const SphereClass & sphere)
{
int mask = 0;
// TODO: doesn't catch all cases...
for (int i = 0; i < 6; i++) {
int result = CollisionMath::Overlap_Test(frustum.Planes[i],sphere);
if (result == OUTSIDE) {
return OUTSIDE;
}
mask |= result;
}
if (mask == INSIDE) {
return INSIDE;
}
return OVERLAPPED;
}
CollisionMath::OverlapType
CollisionMath::Overlap_Test(const FrustumClass & frustum,const AABoxClass & box)
{
int mask = 0;
// TODO: doesn't catch all cases...
for (int i = 0; i < 6; i++) {
int result = CollisionMath::Overlap_Test(frustum.Planes[i],box);
if (result == OUTSIDE) {
return OUTSIDE;
}
mask |= result;
}
if (mask == INSIDE) {
return INSIDE;
}
return OVERLAPPED;
}
CollisionMath::OverlapType
CollisionMath::Overlap_Test(const FrustumClass & frustum,const OBBoxClass & box)
{
int mask = 0;
// TODO: doesn't catch all cases...
for (int i = 0; i < 6; i++) {
int result = CollisionMath::Overlap_Test(frustum.Planes[i],box);
if (result == OUTSIDE) {
return OUTSIDE;
}
mask |= result;
}
if (mask == INSIDE) {
return INSIDE;
}
return OVERLAPPED;
}
CollisionMath::OverlapType
CollisionMath::Overlap_Test(const FrustumClass & frustum,const OBBoxClass & box,int & planes_passed)
{
int mask = 0;
// TODO: doesn't catch all cases...
for (int i = 0; i < 6; i++) {
int plane_bit = (1<<i);
// only check this plane if we have to
if ((planes_passed & plane_bit) == 0) {
int result = CollisionMath::Overlap_Test(frustum.Planes[i],box);
if (result == OUTSIDE) {
return OUTSIDE;
} else if (result == INSIDE) {
planes_passed |= plane_bit;
}
mask |= result;
} else {
mask |= INSIDE;
}
}
if (mask == INSIDE) {
return INSIDE;
}
return OVERLAPPED;
}

View File

@@ -0,0 +1,105 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/colmathfrustum.h $*
* *
* Original Author:: Greg Hjelstrom *
* *
* $Author:: Jani_p $*
* *
* $Modtime:: 5/04/01 8:25p $*
* *
* $Revision:: 3 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* CollisionMath::Overlap_Test -- test a frustum and an AABox for overlap *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef COLMATHFRUSTUM_H
#define COLMATHFRUSTUM_H
#include "always.h"
#include "aabox.h"
#include "vector3.h"
#include "lineseg.h"
#include "frustum.h"
/*
** Inline collision functions dealing with frustums
*/
/***********************************************************************************************
* CollisionMath::Overlap_Test -- test a frustum and an AABox for overlap *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/29/2000 gth : Created. *
*=============================================================================================*/
inline
CollisionMath::OverlapType
CollisionMath::Overlap_Test(const FrustumClass & frustum,const AABoxClass & box,int & planes_passed)
{
int mask = 0;
// TODO: doesn't catch all cases...
for (int i = 0; i < 6; i++) {
int plane_bit = (1<<i);
// only check this plane if we have to
if ((planes_passed & plane_bit) == 0) {
int result = CollisionMath::Overlap_Test(frustum.Planes[i],box);
if (result == OUTSIDE) {
return OUTSIDE;
} else if (result == INSIDE) {
planes_passed |= plane_bit;
}
mask |= result;
} else {
mask |= INSIDE;
}
}
if (mask == INSIDE) {
return INSIDE;
}
return OVERLAPPED;
}
#endif

View File

@@ -0,0 +1,53 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/colmathinlines.h $*
* *
* Original Author:: Greg Hjelstrom *
* *
* $Author:: Greg_h $*
* *
* $Modtime:: 3/29/00 5:36p $*
* *
* $Revision:: 2 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef COLMATHINLINES_H
#define COLMATHINLINES_H
#include "colmathaabox.h"
#include "colmathfrustum.h"
#include "colmathline.h"
#include "colmathplane.h"
#endif

View File

@@ -0,0 +1,484 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /VSS_Sync/wwmath/colmathline.cpp $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 8/29/01 10:25p $*
* *
* $Revision:: 10 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "colmath.h"
#include "aaplane.h"
#include "plane.h"
#include "lineseg.h"
#include "tri.h"
#include "sphere.h"
#include "aabox.h"
#include "obbox.h"
#include "wwdebug.h"
/*
** Structure used in the line->box test. There was a lot of common code between the axis-
** aligned and oriented box tests so I package all of the truely relevant information into
** this struct and pass it into a function local to this module. In the case of oriented
** boxes, the ray must be transformed into the box's coordinate system prior to the call
** and the normal is calculated slightly differently.
*/
struct BoxTestStruct
{
Vector3 Min;
Vector3 Max;
Vector3 P0;
Vector3 DP;
float Fraction;
bool Inside;
int Axis;
int Side;
};
/*
** Enumeration which can be used to categorize a point with respect to the projection
** of a box onto an axis. The point will either be to the negative side of the span, in the
** span, or on the positive side of the span.
*/
enum BoxSideType {
BOX_SIDE_NEGATIVE = 0,
BOX_SIDE_POSITIVE = 1,
BOX_SIDE_MIDDLE = 2
};
/*
** Table of normals for an axis aligned box.
** access like this:
**
** _box_normal[AXIS][SIDE]
**
** <AXIS> is 0,1,2 meaning x,y,z
** <SIDE> is BOX_SIDE_NEGATIVE or BOX_SIDE_POSITIVE
*/
static Vector3 _box_normal[3][2] =
{
// plane = 0 (x axis)
{
Vector3(-1,0,0), // Left
Vector3(1,0,0) // Right
},
// plane = 1 (y axis)
{
Vector3(0,-1,0),
Vector3(0,1,0)
},
// plane = 2 (z axis)
{
Vector3(0,0,-1),
Vector3(0,0,1)
}
};
/*
** Local function prototypes
*/
inline bool Test_Aligned_Box(BoxTestStruct * test);
bool CollisionMath::Collide(const LineSegClass & line,const AAPlaneClass & plane,CastResultStruct * result)
{
float num,den,t;
den = line.Get_DP()[plane.Normal];
/*
** Check if line is parallel to this plane
*/
if (den == 0.0f) {
return false;
}
num = -(line.Get_P0()[plane.Normal] - plane.Dist);
t = num/den;
/*
** If t is not between 0 and 1, the line containing the segment intersects
** the plane but the segment does not
*/
if ((t < 0.0f) || (t > 1.0f)) {
return false;
}
/*
** Ok, we hit the plane!
*/
if (t < result->Fraction) {
result->Fraction = t;
result->Normal.Set(0,0,0);
result->Normal[plane.Normal] = 1.0f;
return true;
}
return false;
}
bool CollisionMath::Collide(const LineSegClass & line,const PlaneClass & plane,CastResultStruct * result)
{
float num,den,t;
den = Vector3::Dot_Product(plane.N,line.Get_DP());
/*
** If the denominator is zero, the ray is parallel to the plane
*/
if (den == 0.0f) {
return false;
}
num = -(Vector3::Dot_Product(plane.N,line.Get_P0()) - plane.D);
t = num/den;
/*
** If t is not between 0 and 1, the line containing the segment intersects
** the plane but the segment does not
*/
if ((t < 0.0f) || (t > 1.0f)) {
return false;
}
/*
** Ok, we hit the plane!
*/
if (t < result->Fraction) {
result->Fraction = t;
result->Normal = plane.N;
/*
** if user is asking for the point, compute it.
*/
if (result->ComputeContactPoint) {
result->ContactPoint = line.Get_P0() + result->Fraction * line.Get_DP();
}
return true;
}
return false;
}
bool CollisionMath::Collide(const LineSegClass & line,const TriClass & tri,CastResultStruct * res)
{
TRACK_COLLISION_RAY_TRI;
/*
** Compute intersection of the line with the plane of the polygon
*/
PlaneClass plane(*tri.N,*tri.V[0]);
Vector3 ipoint;
float num,den,t;
den = Vector3::Dot_Product(plane.N,line.Get_DP());
/*
** If the denominator is zero, the ray is parallel to the plane
*/
if (den == 0.0f) {
return false;
}
num = -(Vector3::Dot_Product(plane.N,line.Get_P0()) - plane.D);
t = num/den;
/*
** If t is not between 0 and 1, the line containing the segment intersects
** the plane but the segment does not
*/
if ((t < 0.0f) || (t > 1.0f)) {
return false;
}
ipoint = line.Get_P0() + t*line.Get_DP();
/*
** Check if this point is inside the triangle
*/
if (!tri.Contains_Point(ipoint)) {
return false;
}
/*
** Ok, we hit the triangle, set the collision results
*/
if (t < res->Fraction) {
res->Fraction = t;
res->Normal = plane.N;
if (res->ComputeContactPoint) {
res->ContactPoint = line.Get_P0() + res->Fraction * line.Get_DP();
}
TRACK_COLLISION_RAY_TRI_HIT;
return true;
}
return false;
}
bool CollisionMath::Collide(const LineSegClass & line,const SphereClass & sphere,CastResultStruct * res)
{
// this game from graphics gems 1, page 388
// intersection of a ray with a sphere
Vector3 dc = sphere.Center - line.Get_P0();
float clen = Vector3::Dot_Product((dc) , line.Get_Dir());
float disc = (sphere.Radius * sphere.Radius) - (dc.Length2() - clen*clen);
if (disc < 0.0f) {
return false;
} else {
float d = WWMath::Sqrt(disc);
float frac = (clen - d) / line.Get_Length();
if (frac<0.0f)
frac = (clen + d) / line.Get_Length();
if (frac<0.0f) return false;
if (frac < res->Fraction) {
res->Fraction = frac;
Vector3 p = line.Get_P0() + (clen - d)*line.Get_Dir();
Vector3 norm = p - sphere.Center;
norm /= sphere.Radius;
res->Normal = norm;
if (res->ComputeContactPoint) {
res->ContactPoint = line.Get_P0() + res->Fraction * line.Get_DP();
}
return true;
}
}
return false;
}
bool CollisionMath::Collide(const LineSegClass & line,const AABoxClass & box,CastResultStruct * res)
{
// set up the test struct
BoxTestStruct test;
test.Min = box.Center - box.Extent;
test.Max = box.Center + box.Extent;
test.P0 = line.Get_P0();
test.DP = line.Get_DP();
// check ray against the box, exit if the ray totally missed the box,
if (!Test_Aligned_Box(&test)) {
return false;
}
// if ray starts inside the box, note that fact and bail.
if (test.Inside) {
res->StartBad = true;
return true;
}
// Now, if this intersection is before any current intersection
// that we've found, install our intersection
if (test.Fraction < res->Fraction) {
res->Fraction = test.Fraction;
assert(test.Side != BOX_SIDE_MIDDLE);
res->Normal = _box_normal[test.Axis][test.Side];
if (res->ComputeContactPoint) {
res->ContactPoint = line.Get_P0() + res->Fraction * line.Get_DP();
}
return true;
}
return false;
}
bool CollisionMath::Collide(const LineSegClass & line,const OBBoxClass & box,CastResultStruct * result)
{
// set up the test struct
BoxTestStruct test;
test.Min = box.Center - box.Extent;
test.Max = box.Center + box.Extent;
test.P0 = (box.Basis.Transpose() * (line.Get_P0() - box.Center)) + box.Center;
test.DP = box.Basis.Transpose() * line.Get_DP();
// check ray against the box, exit if the ray totally missed the box,
if (!Test_Aligned_Box(&test)) {
return false;
}
// if ray starts inside the box, don't collide
if (test.Inside) {
result->StartBad = true;
return true;
}
// Now, if this intersection is before any current intersection
// that we've found, install our intersection
if (test.Fraction < result->Fraction) {
result->Fraction = test.Fraction;
assert(test.Side != BOX_SIDE_MIDDLE);
switch (test.Axis) {
case 0:
result->Normal.Set(box.Basis[0][0],box.Basis[1][0],box.Basis[2][0]);
break;
case 1:
result->Normal.Set(box.Basis[0][1],box.Basis[1][1],box.Basis[2][1]);
break;
case 2:
result->Normal.Set(box.Basis[0][2],box.Basis[1][2],box.Basis[2][2]);
break;
}
if (test.Side == BOX_SIDE_NEGATIVE) {
result->Normal = -result->Normal;
}
if (result->ComputeContactPoint) {
result->ContactPoint = line.Get_P0() + result->Fraction * line.Get_DP();
}
return true;
}
return false;
}
/***********************************************************************************************
* Test_Aligned_Box -- used as the guts of the Box intersection tests *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/20/98 GTH : Created. *
*=============================================================================================*/
inline bool Test_Aligned_Box(BoxTestStruct * test)
{
int i;
// candidate intersection plane distance for each axis
float candidateplane[3];
// t value along the ray for each axis
float maxt[3];
// intersection point
Vector3 coord;
// which "side" of the box for each axis
int quadrant[3];
bool inside = true;
for (i=0; i<3; i++) {
if (test->P0[i] < test->Min[i]) {
quadrant[i] = BOX_SIDE_NEGATIVE;
candidateplane[i] = test->Min[i];
inside = false;
} else if (test->P0[i] > test->Max[i]) {
quadrant[i] = BOX_SIDE_POSITIVE;
candidateplane[i] = test->Max[i];
inside = false;
} else {
quadrant[i] = BOX_SIDE_MIDDLE;
}
}
/*
** Ray started out inside the box...
*/
if (inside) {
test->Fraction = 0.0f;
test->Inside = true;
return true;
}
/*
** Calculate the 3 distances to the candidate planes
*/
for (i=0; i<3; i++) {
if (quadrant[i] != BOX_SIDE_MIDDLE && test->DP[i] != 0.0f) {
maxt[i] = (candidateplane[i] - test->P0[i]) / test->DP[i];
} else {
maxt[i] = -1.0f;
}
}
/*
** Get the largest of the maxt's for the final choice of intersection
*/
int intersection_plane = 0;
for (i=1; i<3; i++) {
if (maxt[i] > maxt[intersection_plane]) {
intersection_plane = i;
}
}
/*
** If the ray is "in front" of all of the planes, return
*/
if (maxt[intersection_plane] < 0.0f) {
return false;
}
/*
** Check if the ray is inside the box, i.e. on the two planes which
** are not the intersection planes, the intersection point should
** be between the min and max of the box.
*/
for (i=0; i<3; i++) {
if (intersection_plane != i) {
coord[i] = test->P0[i] + maxt[intersection_plane] * test->DP[i];
if ((coord[i] < test->Min[i]) || (coord[i] > test->Max[i])) {
return false; // ray is outside the box
}
} else {
coord[i] = candidateplane[i];
}
}
/*
** Fill in the intersection values
*/
test->Fraction = maxt[intersection_plane];
test->Inside = false;
test->Axis = intersection_plane;
test->Side = quadrant[intersection_plane];
return true;
}

View File

@@ -0,0 +1,64 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/colmathline.h $*
* *
* Original Author:: Greg Hjelstrom *
* *
* $Author:: Greg_h $*
* *
* $Modtime:: 3/16/00 3:46p $*
* *
* $Revision:: 1 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#ifndef COLMATHLINE_H
#define COLMATHLINE_H
/*
** Inline collision functions dealing with line segments
*/
inline CollisionMath::OverlapType CollisionMath::Overlap_Test
(
const Vector3 & min,
const Vector3 & max,
const LineSegClass & line
)
{
AABoxClass box;
box.Init_Min_Max(min,max);
return CollisionMath::Overlap_Test(box,line);
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,171 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/colmathobbox.cpp $*
* *
* Org Author:: Greg Hjelstrom *
* *
* Author : Kenny Mitchell *
* *
* $Modtime:: 06/26/02 4:04p $*
* *
* $Revision:: 9 $*
* *
* 06/26/02 KM Matrix name change to avoid MAX conflicts *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "colmath.h"
#include "aaplane.h"
#include "plane.h"
#include "lineseg.h"
#include "tri.h"
#include "sphere.h"
#include "aabox.h"
#include "obbox.h"
#include "wwdebug.h"
// OBBox functions, where is operand B with respect to the OBBox
CollisionMath::OverlapType
CollisionMath::Overlap_Test(const OBBoxClass & box,const Vector3 & point)
{
// transform point into box coordinate system
Vector3 localpoint;
Matrix3x3::Transpose_Rotate_Vector(box.Basis,(point - box.Center),&localpoint);
// if the point is outside any of the extents, it is outside the box
if (WWMath::Fabs(localpoint.X) > box.Extent.X) {
return OUTSIDE;
}
if (WWMath::Fabs(localpoint.Y) > box.Extent.Y) {
return OUTSIDE;
}
if (WWMath::Fabs(localpoint.Z) > box.Extent.Z) {
return OUTSIDE;
}
return INSIDE;
}
CollisionMath::OverlapType
CollisionMath::Overlap_Test(const OBBoxClass & box,const LineSegClass & line)
{
CastResultStruct res;
Collide(line,box,&res);
return eval_overlap_collision(res);
}
CollisionMath::OverlapType
CollisionMath::Overlap_Test(const OBBoxClass & box,const TriClass & tri)
{
CastResultStruct res;
Collide(box,Vector3(0,0,0),tri,Vector3(0,0,0),&res);
return eval_overlap_collision(res);
}
CollisionMath::OverlapType
CollisionMath::Overlap_Test(const AABoxClass & aabox,const OBBoxClass & obbox)
{
if (CollisionMath::Intersection_Test(aabox,obbox)) {
return BOTH; // inside or overlapping
} else {
return OUTSIDE;
}
}
CollisionMath::OverlapType
CollisionMath::Overlap_Test(const OBBoxClass & obbox,const AABoxClass & aabox)
{
if (CollisionMath::Intersection_Test(obbox,aabox)) {
return BOTH; // inside or overlapping
} else {
return OUTSIDE;
}
}
CollisionMath::OverlapType
CollisionMath::Overlap_Test(const OBBoxClass & box,const OBBoxClass & box2)
{
CastResultStruct res;
Collide(box,Vector3(0,0,0),box2,Vector3(0,0,0),&res);
return eval_overlap_collision(res);
}
bool CollisionMath::Collide
(
const OBBoxClass & box,
const Vector3 & move_vector,
const PlaneClass & plane,
CastResultStruct * result
)
{
float frac;
float extent = box.Project_To_Axis(plane.N);
float dist = Vector3::Dot_Product(plane.N,box.Center) + plane.D;
float move = Vector3::Dot_Product(plane.N,move_vector);
if (dist > extent) {
if (dist + move > extent) {
// entire move ok!
frac = 1.0f;
} else {
// partial move allowed
frac = (extent - dist) / move;
}
} else if (dist < -extent) {
if (dist + move < -extent) {
// entire move ok!
frac = 1.0f;
} else {
// partial move allowed
frac = (-extent - dist) / move;
}
} else {
result->StartBad = true;
result->Normal = plane.N;
return true;
}
if (frac < result->Fraction) {
result->Fraction = frac;
result->Normal = plane.N;
if (result->ComputeContactPoint) {
Vector3 move_dir(move_vector);
move_dir.Normalize();
float move_extent = Vector3::Dot_Product(move_dir,box.Extent);
result->ContactPoint = box.Center + result->Fraction*move_vector + move_extent*move_dir;
}
return true;
}
return false;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,197 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/colmathplane.cpp $*
* *
* Org Author:: Greg Hjelstrom *
* *
* Author : Kenny Mitchell *
* *
* $Modtime:: 06/26/02 4:04p $*
* *
* $Revision:: 10 $*
* *
* 06/26/02 KM Matrix name change to avoid MAX conflicts *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "colmath.h"
#include "colmathplane.h"
#include "aaplane.h"
#include "plane.h"
#include "lineseg.h"
#include "tri.h"
#include "sphere.h"
#include "aabox.h"
#include "obbox.h"
#include "wwdebug.h"
CollisionMath::OverlapType
CollisionMath::Overlap_Test(const AAPlaneClass & plane,const Vector3 & point)
{
float delta = point[plane.Normal] - plane.Dist;
if (delta > COINCIDENCE_EPSILON) {
return POS;
}
if (delta < -COINCIDENCE_EPSILON) {
return NEG;
}
return ON;
}
CollisionMath::OverlapType
CollisionMath::Overlap_Test(const AAPlaneClass & plane,const LineSegClass & line)
{
int mask = 0;
mask |= CollisionMath::Overlap_Test(plane,line.Get_P0());
mask |= CollisionMath::Overlap_Test(plane,line.Get_P1());
return eval_overlap_mask(mask);
}
CollisionMath::OverlapType
CollisionMath::Overlap_Test(const AAPlaneClass & plane,const TriClass & tri)
{
int mask = 0;
mask |= CollisionMath::Overlap_Test(plane,*tri.V[0]);
mask |= CollisionMath::Overlap_Test(plane,*tri.V[1]);
mask |= CollisionMath::Overlap_Test(plane,*tri.V[2]);
return eval_overlap_mask(mask);
}
CollisionMath::OverlapType
CollisionMath::Overlap_Test(const AAPlaneClass & plane,const SphereClass & sphere)
{
float delta = sphere.Center[plane.Normal] - plane.Dist;
if (delta > sphere.Radius) {
return POS;
}
if (delta < sphere.Radius) {
return NEG;
}
return BOTH;
}
CollisionMath::OverlapType
CollisionMath::Overlap_Test(const AAPlaneClass & plane,const AABoxClass & box)
{
float delta;
int mask = 0;
// check the 'min' side of the box
delta = (box.Center[plane.Normal] - box.Extent[plane.Normal]) - plane.Dist;
if (delta > WWMATH_EPSILON) {
mask |= POS;
} else if (delta < -WWMATH_EPSILON) {
mask |= NEG;
} else {
mask |= ON;
}
// check the 'max' side of the box
delta = (box.Center[plane.Normal] + box.Extent[plane.Normal]) - plane.Dist;
if (delta > WWMATH_EPSILON) {
mask |= POS;
} else if (delta < -WWMATH_EPSILON) {
mask |= NEG;
} else {
mask |= ON;
}
return eval_overlap_mask(mask);
}
CollisionMath::OverlapType
CollisionMath::Overlap_Test(const AAPlaneClass & /*plane*/,const OBBoxClass & /*box*/)
{
// TODO
WWASSERT(0);
return POS;
}
// Plane functions. Where is operand B with respect to the plane
CollisionMath::OverlapType
CollisionMath::Overlap_Test(const PlaneClass & plane,const LineSegClass & line)
{
int mask = 0;
mask |= CollisionMath::Overlap_Test(plane,line.Get_P0());
mask |= CollisionMath::Overlap_Test(plane,line.Get_P1());
return eval_overlap_mask(mask);
}
CollisionMath::OverlapType
CollisionMath::Overlap_Test(const PlaneClass & plane,const TriClass & tri)
{
int mask = 0;
mask |= CollisionMath::Overlap_Test(plane,*tri.V[0]);
mask |= CollisionMath::Overlap_Test(plane,*tri.V[1]);
mask |= CollisionMath::Overlap_Test(plane,*tri.V[2]);
return eval_overlap_mask(mask);
}
CollisionMath::OverlapType
CollisionMath::Overlap_Test(const PlaneClass & plane,const SphereClass & sphere)
{
float dist = Vector3::Dot_Product(sphere.Center,plane.N) - plane.D;
if (dist > sphere.Radius) {
return POS;
}
if (dist < -sphere.Radius) {
return NEG;
}
return BOTH;
}
CollisionMath::OverlapType
CollisionMath::Overlap_Test(const PlaneClass & plane,const OBBoxClass & box)
{
// rotate the plane normal into box coordinates
Vector3 local_normal;
Vector3 posfarpt;
Vector3 negfarpt;
Matrix3x3::Transpose_Rotate_Vector(box.Basis,plane.N,&local_normal);
get_far_extent(local_normal,box.Extent,&posfarpt);
// transform the two extreme box coordinates into world space
Matrix3x3::Rotate_Vector(box.Basis,posfarpt,&posfarpt);
negfarpt = -posfarpt;
posfarpt += box.Center;
negfarpt += box.Center;
// overlap test
if (Overlap_Test(plane,negfarpt) == POS) {
return POS;
}
if (Overlap_Test(plane,posfarpt) == NEG) {
return NEG;
}
return BOTH;
}

View File

@@ -0,0 +1,154 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/colmathplane.h $*
* *
* Original Author:: Greg Hjelstrom *
* *
* $Author:: Greg_h $*
* *
* $Modtime:: 3/29/00 4:42p $*
* *
* $Revision:: 2 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* get_far_extent -- gets extents of a box projected onto an axis *
* CollisionMath::Overlap_Test -- Tests overlap between a plane and a point *
* CollisionMath::Overlap_Test -- Tests overlap between a plane and an AABox *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#ifndef COLMATHPLANE_H
#define COLMATHPLANE_H
#include "always.h"
#include "plane.h"
#include "aabox.h"
/*
** Inline collision functions dealing with planes
** This module is meant to be included only in .CPP files after you include colmath.h
** It is not automatically included in order to reduce file dependencies...
*/
/***********************************************************************************************
* get_far_extent -- gets extents of a box projected onto an axis *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/29/2000 gth : Created. *
*=============================================================================================*/
inline void get_far_extent(const Vector3 & normal,const Vector3 & extent,Vector3 * posfarpt)
{
if (WWMath::Fast_Is_Float_Positive(normal.X)) {
posfarpt->X = extent.X;
} else {
posfarpt->X = -extent.X;
}
if (WWMath::Fast_Is_Float_Positive(normal.Y)) {
posfarpt->Y = extent.Y;
} else {
posfarpt->Y = -extent.Y;
}
if (WWMath::Fast_Is_Float_Positive(normal.Z)) {
posfarpt->Z = extent.Z;
} else {
posfarpt->Z = -extent.Z;
}
}
/***********************************************************************************************
* CollisionMath::Overlap_Test -- Tests overlap between a plane and a point *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/29/2000 gth : Created. *
*=============================================================================================*/
inline
CollisionMath::OverlapType
CollisionMath::Overlap_Test(const PlaneClass & plane,const Vector3 & point)
{
float delta = Vector3::Dot_Product(point,plane.N) - plane.D;
if (delta > COINCIDENCE_EPSILON) {
return POS;
}
if (delta < -COINCIDENCE_EPSILON) {
return NEG;
}
return ON;
}
/***********************************************************************************************
* CollisionMath::Overlap_Test -- Tests overlap between a plane and an AABox *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/29/2000 gth : Created. *
*=============================================================================================*/
inline
CollisionMath::OverlapType
CollisionMath::Overlap_Test(const PlaneClass & plane,const AABoxClass & box)
{
// First, we determine the the near and far points of the box in the
// direction of the plane normal
Vector3 posfarpt;
Vector3 negfarpt;
get_far_extent(plane.N,box.Extent,&posfarpt);
negfarpt = -posfarpt;
posfarpt += box.Center;
negfarpt += box.Center;
if (Overlap_Test(plane,negfarpt) == POS) {
return POS;
}
if (Overlap_Test(plane,posfarpt) == NEG) {
return NEG;
}
return BOTH;
}
#endif

View File

@@ -0,0 +1,249 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/colmathsphere.cpp $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 4/25/01 2:05p $*
* *
* $Revision:: 6 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* CollisionMath::Intersection_Test -- Sphere - AAbox intersection *
* CollisionMath::Intersection_Test -- Sphere - OBBox intersection *
* CollisionMath::Overlap_Test -- Sphere - Point overlap test *
* CollisionMath::Overlap_Test -- sphere line overlap test *
* CollisionMath::Overlap_Test -- sphere triangle overlap test *
* CollisionMath::Overlap_Test -- Sphere - Sphere overlap test *
* CollisionMath::Overlap_Test -- Sphere - AABox overlap test *
* CollisionMath::Overlap_Test -- Sphere - OBBox overlap test *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "colmath.h"
#include "aaplane.h"
#include "plane.h"
#include "lineseg.h"
#include "tri.h"
#include "sphere.h"
#include "aabox.h"
#include "obbox.h"
#include "wwdebug.h"
// Sphere Intersection fucntions. Does the sphere intersect the passed in object
/***********************************************************************************************
* CollisionMath::Intersection_Test -- Sphere - AAbox intersection *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/25/2001 gth : Created. *
*=============================================================================================*/
bool CollisionMath::Intersection_Test(const SphereClass & sphere,const AABoxClass & box)
{
/*
** Simple but slightly inaccurate test, expand the box by the sphere's radius, then
** test whether the sphere is contained in that new box. This is actually testing
** against a cube which encloses the sphere...
*/
Vector3 dc = box.Center - sphere.Center;
if (WWMath::Fabs(dc.X) < box.Extent.X + sphere.Radius) return false;
if (WWMath::Fabs(dc.Y) < box.Extent.Y + sphere.Radius) return false;
if (WWMath::Fabs(dc.Z) < box.Extent.Z + sphere.Radius) return false;
return true;
}
/***********************************************************************************************
* CollisionMath::Intersection_Test -- Sphere - OBBox intersection *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/25/2001 gth : Created. *
*=============================================================================================*/
bool CollisionMath::Intersection_Test(const SphereClass & sphere,const OBBoxClass & box)
{
/*
** Compute the sphere's position in the box's coordinate system
*/
Matrix3D tm(box.Basis,box.Center);
Vector3 box_rel_center;
Matrix3D::Inverse_Transform_Vector(tm,sphere.Center,&box_rel_center);
if (box.Extent.X < WWMath::Fabs(box_rel_center.X)) return false;
if (box.Extent.Y < WWMath::Fabs(box_rel_center.Y)) return false;
if (box.Extent.Z < WWMath::Fabs(box_rel_center.Z)) return false;
return true;
}
// Sphere Overlap functions. Where is operand B with respect to the sphere
/***********************************************************************************************
* CollisionMath::Overlap_Test -- Sphere - Point overlap test *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/25/2001 gth : Created. *
*=============================================================================================*/
CollisionMath::OverlapType
CollisionMath::Overlap_Test(const SphereClass & sphere,const Vector3 & point)
{
float r2 = (point - sphere.Center).Length2();
if (r2 < sphere.Radius * sphere.Radius - COINCIDENCE_EPSILON) {
return NEG;
}
if (r2 > sphere.Radius * sphere.Radius + COINCIDENCE_EPSILON) {
return POS;
}
return ON;
}
/***********************************************************************************************
* CollisionMath::Overlap_Test -- sphere line overlap test *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/25/2001 gth : Created. *
*=============================================================================================*/
CollisionMath::OverlapType
CollisionMath::Overlap_Test(const SphereClass & /*sphere*/,const LineSegClass & /*line*/)
{
WWASSERT(0); //TODO
return POS;
}
/***********************************************************************************************
* CollisionMath::Overlap_Test -- sphere triangle overlap test *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/25/2001 gth : Created. *
*=============================================================================================*/
CollisionMath::OverlapType
CollisionMath::Overlap_Test(const SphereClass & /*sphere*/,const TriClass & /*tri*/)
{
WWASSERT(0); //TODO
return POS;
}
/***********************************************************************************************
* CollisionMath::Overlap_Test -- Sphere - Sphere overlap test *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/25/2001 gth : Created. *
*=============================================================================================*/
CollisionMath::OverlapType
CollisionMath::Overlap_Test(const SphereClass & sphere,const SphereClass & sphere2)
{
CollisionMath::OverlapType retval = OUTSIDE;
float radius = sphere.Radius + sphere2.Radius;
float dist2 = (sphere2.Center - sphere.Center).Length2();
if (dist2 == 0 && sphere.Radius == sphere2.Radius) {
retval = OVERLAPPED;
} else if (dist2 <= radius * radius - COINCIDENCE_EPSILON) {
retval = INSIDE;
}
return retval;
}
/***********************************************************************************************
* CollisionMath::Overlap_Test -- Sphere - AABox overlap test *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/25/2001 gth : Created. *
*=============================================================================================*/
CollisionMath::OverlapType
CollisionMath::Overlap_Test(const SphereClass & sphere,const AABoxClass & aabox)
{
// TODO: overlap function that detects containment?
return ( Intersection_Test(sphere,aabox) ? BOTH : POS );
}
/***********************************************************************************************
* CollisionMath::Overlap_Test -- Sphere - OBBox overlap test *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/25/2001 gth : Created. *
*=============================================================================================*/
CollisionMath::OverlapType
CollisionMath::Overlap_Test(const SphereClass & sphere,const OBBoxClass & obbox)
{
// TODO: overlap function that detects containment?
return ( Intersection_Test(sphere,obbox) ? BOTH : POS );
}

View File

@@ -0,0 +1,154 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /VSS_Sync/wwmath/cullsys.cpp $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 10/16/00 11:42a $*
* *
* $Revision:: 5 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "cullsys.h"
#include "wwdebug.h"
#include "wwprofile.h"
/*************************************************************************
**
** CullableClass Implementation
**
*************************************************************************/
CullableClass::CullableClass(void) :
CullLink(NULL),
NextCollected(NULL)
{
CullBox.Init(Vector3(0,0,0),Vector3(1,1,1));
}
CullableClass::~CullableClass(void)
{
// the cull system that contains us is responsible for any culling link
// so we better be out of it and it should have cleared our pointer before
// we are deleted.
WWASSERT(CullLink == NULL);
}
void CullableClass::Set_Cull_Box(const AABoxClass & box,bool just_loaded)
{
CullBox = box;
WWPROFILE("Cullable::Set_Cull_Box");
// Just_loaded flag allows us to update the box without notifying the
// culling system. Use this when you've saved and loaded the linkage
// so you know you're in the right node of the culling system...
if (!just_loaded) {
CullSystemClass * sys = Get_Culling_System();
if (sys != NULL) {
sys->Update_Culling(this);
}
}
}
void CullableClass::Set_Culling_System(CullSystemClass * sys)
{
if (CullLink) {
CullLink->Set_Culling_System(sys);
}
}
CullSystemClass * CullableClass::Get_Culling_System(void) const
{
if (CullLink) {
return CullLink->Get_Culling_System();
}
return NULL;
}
/*************************************************************************
**
** CullSystemClass Implementation
**
** The base CullSystemClass mainly contains code for maintaining the
** current collection list and iterating through it.
**
*************************************************************************/
CullSystemClass::CullSystemClass(void) :
CollectionHead(NULL)
{
}
CullSystemClass::~CullSystemClass(void)
{
}
// NOTE: THE Get_() functions currently are the same as the Peek_() functions (e.g., they do not
// add a Ref). This is wrong and will be fixed.
CullableClass * CullSystemClass::Get_First_Collected_Object_Internal(void)
{
return CollectionHead;
}
CullableClass * CullSystemClass::Get_Next_Collected_Object_Internal(CullableClass * obj)
{
if (obj != NULL) {
return obj->NextCollected;
}
return NULL;
}
CullableClass * CullSystemClass::Peek_First_Collected_Object_Internal(void)
{
return CollectionHead;
}
CullableClass * CullSystemClass::Peek_Next_Collected_Object_Internal(CullableClass * obj)
{
if (obj != NULL) {
return obj->NextCollected;
}
return NULL;
}
void CullSystemClass::Reset_Collection(void)
{
CollectionHead = NULL;
}
void CullSystemClass::Add_To_Collection(CullableClass * obj)
{
WWASSERT(obj != NULL);
obj->NextCollected = CollectionHead;
CollectionHead = obj;
}

View File

@@ -0,0 +1,201 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/cullsys.h $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 5/08/01 6:33p $*
* *
* $Revision:: 5 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef CULLSYS_H
#define CULLSYS_H
#include "wwdebug.h"
#include "stdlib.h"
#include "refcount.h"
#include "aabox.h"
class CullableClass;
class CullSystemClass;
class FrustumClass;
/*
** CullLinkClass
** This class will serve as a base class for the various types of linkage information
** that the Cullable instances will need. Each CullableClass will have a pointer to
** a class derived from CullLinkClass where the different culling systems can store
** things.
*/
class CullLinkClass
{
public:
WWINLINE CullLinkClass(CullSystemClass * system) { System = system; WWASSERT(System); }
virtual ~CullLinkClass(void) { WWASSERT(System == NULL); }
WWINLINE void Set_Culling_System(CullSystemClass * sys) { System = sys; }
WWINLINE CullSystemClass * Get_Culling_System(void) { return System; }
protected:
CullSystemClass * System;
};
/*
** CullableClass
** This is the base class for any object that can be inserted into a culling system
** This class provides an axis aligned bounding box and some linkage variables which
** allow it to be processed by any culling system.
*/
class CullableClass : public RefCountClass
{
public:
CullableClass(void);
virtual ~CullableClass(void);
/*
** Access to the culling box for this object. When you set the cull box, you are
** basically guaranteeing that the object is contained within the given box. The
** object will automatically be updated in whatever culling system it is currently
** contained in (if any)
*/
WWINLINE const AABoxClass & Get_Cull_Box(void) const { return CullBox; }
void Set_Cull_Box(const AABoxClass & box,bool just_loaded = false);
/*
** These functions are used by various culling systems to manage the linkage
** pointers. *The average user should NEVER call these*
*/
void Set_Culling_System(CullSystemClass * sys);
CullSystemClass * Get_Culling_System(void) const;
WWINLINE void Set_Cull_Link(CullLinkClass * c) { CullLink = c; }
WWINLINE CullLinkClass * Get_Cull_Link(void) const { return CullLink; }
private:
WWINLINE void Set_Next_Collected(CullableClass * c) { NextCollected = c; }
WWINLINE CullableClass * Get_Next_Collected(void) { return NextCollected; }
/*
** Culling Data
** Each object can be linked into various types of culling systems.
** Each culling system can use its own linkage data structure (derived
** from CullLinkClass) to keep track of the object. The CullData pointer
** will point to one of the culling link objects and NULL
** if its not in any system.
*/
CullLinkClass * CullLink;
/*
** Bounding Box
** Any objects derived from Cullable should update the bounding box
** whenever the object moves or changes size. In order to do this,
** call Set_Cull_Box...
*/
AABoxClass CullBox;
/*
** NextCollected
** This pointer is used by the culling system to keep a singly linked
** list of cullable object that have been "collected".
*/
CullableClass * NextCollected;
// Not Implemented:
CullableClass(const CullableClass & src);
CullableClass & operator = (const CullableClass & src);
friend class CullSystemClass;
};
/*
** CullSystemClass
** Base class of any culling system. This interface exists so that things can
** be shuffled around without having explicit knowledge of what system they are in.
*/
class CullSystemClass
{
public:
CullSystemClass(void);
virtual ~CullSystemClass(void);
/*
** Collect_Objects. Updates the internal collection list with the
** objects that overlap the given primitive.
** WARNING: This builds an internal list that is only valid until
** another list is built, only one list can be valid at any time.
** WARNING: Always call Reset_Collection if you want to start a
** fresh collection!
*/
void Reset_Collection(void);
virtual void Collect_Objects(const Vector3 & point) = 0;
virtual void Collect_Objects(const AABoxClass & box) = 0;
virtual void Collect_Objects(const OBBoxClass & box) = 0;
virtual void Collect_Objects(const FrustumClass & frustum) = 0;
/*
** This object has moved or changed size, update it
*/
virtual void Update_Culling(CullableClass * obj) = 0;
protected:
/*
** Iterate through the collected objects
*/
CullableClass * Get_First_Collected_Object_Internal(void);
CullableClass * Get_Next_Collected_Object_Internal(CullableClass * obj);
CullableClass * Peek_First_Collected_Object_Internal(void);
CullableClass * Peek_Next_Collected_Object_Internal(CullableClass * obj);
/*
** Build the list of collected objects
*/
void Add_To_Collection(CullableClass * obj);
/*
** Pointer to the head of the current collection of objects
*/
CullableClass * CollectionHead;
friend class CullableClass;
};
#endif

View File

@@ -0,0 +1,62 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/culltype.h $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 10/23/98 2:34p $*
* *
* $Revision:: 3 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef CULLTYPE_H
#define CULLTYPE_H
#include "always.h"
/*
** CullType is an enumeration of the possible results of a culling
** operation. It is placed here so that all of the different cull functions
** (which are scattered throughout WWMath, WW3D, WWPhys, etc) can
** communicate the result in a consistent way
*/
typedef enum CULLTYPE
{
CULL_OUTSIDE = 0, // the object was completely outside the culling volume
CULL_INTERSECTING, // the object intersects an edge of the culling volume
CULL_INSIDE // the object is completely inside the culling volume
};
#endif

View File

@@ -0,0 +1,591 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /VSS_Sync/wwmath/curve.cpp $*
* *
* Original Author:: Greg Hjelstrom *
* *
* $Author:: Vss_sync $*
* *
* $Modtime:: 6/13/01 2:18p $*
* *
* $Revision:: 9 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "curve.h"
#include "wwdebug.h"
#include "persistfactory.h"
#include "wwmathids.h"
#include "wwhack.h"
/*
** Force-Link this module because the linker can't detect that we actually need it...
*/
DECLARE_FORCE_LINK(curve);
/*
** Persist factories and chunk-id's used to save and load.
*/
SimplePersistFactoryClass<LinearCurve3DClass,WWMATH_CHUNKID_LINEARCURVE3D> _LinearCurve3DFactory;
SimplePersistFactoryClass<LinearCurve1DClass,WWMATH_CHUNKID_LINEARCURVE1D> _LinearCurve1DFactory;
enum
{
// ID's used by Curve3D
CURVE3D_CHUNK_VARIABLES = 0x00020651,
CURVE3D_CHUNK_KEYS,
CURVE3D_VARIABLE_ISLOOPING = 0x00,
CURVE3D_VARIABLE_KEYCOUNT,
// ID's used by LinearCurve3D
LINEARCURVE3D_CHUNK_CURVE3D = 0x00020653,
// ID's used by Curve1D
CURVE1D_CHUNK_VARIABLES = 0x00020655,
CURVE1D_CHUNK_KEYS,
CURVE1D_VARIABLE_ISLOOPING = 0x00,
CURVE1D_VARIABLE_KEYCOUNT,
// ID's used by LinearCurve1D
LINEARCURVE1D_CHUNK_CURVE1D = 0x00020657,
};
/***********************************************************************************************
**
** Curve3DCLass Implementation
**
***********************************************************************************************/
Curve3DClass::Curve3DClass(void) :
IsLooping(false)
{
}
Curve3DClass::Curve3DClass(const Curve3DClass & that)
{
*this = that;
}
Curve3DClass::~Curve3DClass(void)
{
}
Curve3DClass & Curve3DClass::operator = (const Curve3DClass & that)
{
IsLooping = that.IsLooping;
Keys = that.Keys;
return *this;
}
bool Curve3DClass::Is_Looping(void)
{
return IsLooping;
}
void Curve3DClass::Set_Looping(bool onoff)
{
IsLooping = onoff;
}
float Curve3DClass::Get_Start_Time(void)
{
if (Keys.Count() > 0) {
return Keys[0].Time;
} else {
return 0.0f;
}
}
float Curve3DClass::Get_End_Time(void)
{
if (Keys.Count() > 0) {
return Keys[Keys.Count() - 1].Time;
} else {
return 0.0f;
}
}
int Curve3DClass::Key_Count(void)
{
return Keys.Count();
}
void Curve3DClass::Get_Key(int i,Vector3 * set_point,float * set_t)
{
assert(i >= 0);
assert(i < Keys.Count());
if (set_point != NULL) {
*set_point = Keys[i].Point;
}
if (set_t != NULL) {
*set_t = Keys[i].Time;
}
}
void Curve3DClass::Set_Key(int i,const Vector3 & point)
{
assert(i >= 0);
assert(i < Keys.Count());
Keys[i].Point = point;
}
int Curve3DClass::Add_Key(const Vector3 & point,float t)
{
int idx = 0;
while (idx < Keys.Count() && Keys[idx].Time < t) {
idx++;
}
KeyClass newkey;
newkey.Point = point;
newkey.Time = t;
Keys.Insert(idx,newkey);
return idx;
}
void Curve3DClass::Remove_Key(int i)
{
assert(i >= 0);
assert(i < Keys.Count());
Keys.Delete(i);
}
void Curve3DClass::Clear_Keys(void)
{
Keys.Clear();
}
void Curve3DClass::Find_Interval(float time,int * i0,int * i1,float * t)
{
WWASSERT(time >= Keys[0].Time);
WWASSERT(time <= Keys[Keys.Count()-1].Time);
int i=0;
while (time > Keys[i+1].Time) {
i++;
}
*i0 = i;
*i1 = i+1;
*t = (time - Keys[i].Time) / (Keys[i+1].Time - Keys[i].Time);
}
bool Curve3DClass::Save(ChunkSaveClass & csave)
{
int keycount = Keys.Count();
csave.Begin_Chunk(CURVE3D_CHUNK_VARIABLES);
WRITE_MICRO_CHUNK(csave,CURVE3D_VARIABLE_ISLOOPING,IsLooping);
WRITE_MICRO_CHUNK(csave,CURVE3D_VARIABLE_KEYCOUNT,keycount);
csave.End_Chunk();
// Saving the keys, Note that if the format of a key changes we'll
// need a new chunk. (I didn't wrap each variable in its own chunk)
csave.Begin_Chunk(CURVE3D_CHUNK_KEYS);
for (int i=0; i<keycount; i++) {
csave.Write(&(Keys[i].Point),sizeof(Keys[i].Point));
csave.Write(&(Keys[i].Time),sizeof(Keys[i].Time));
}
csave.End_Chunk();
return true;
}
bool Curve3DClass::Load(ChunkLoadClass & cload)
{
int i;
int keycount = 0;
KeyClass newkey;
// reset the curve
Keys.Delete_All();
// read in the chunks
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID())
{
case CURVE3D_CHUNK_VARIABLES:
while (cload.Open_Micro_Chunk()) {
switch(cload.Cur_Micro_Chunk_ID()) {
READ_MICRO_CHUNK(cload,CURVE3D_VARIABLE_ISLOOPING,IsLooping);
READ_MICRO_CHUNK(cload,CURVE3D_VARIABLE_KEYCOUNT,keycount);
}
cload.Close_Micro_Chunk();
}
break;
case CURVE3D_CHUNK_KEYS:
for (i=0; i<keycount; i++) {
cload.Read(&(newkey.Point),sizeof(newkey.Point));
cload.Read(&(newkey.Time),sizeof(newkey.Time));
Keys.Add(newkey);
}
break;
default:
WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",__FILE__,__LINE__));
break;
}
cload.Close_Chunk();
}
return true;
}
/***********************************************************************************************
**
** LinearCurve3DClass Implementation
** Linear curve, linearly interpolates the keys
**
***********************************************************************************************/
void LinearCurve3DClass::Evaluate(float time,Vector3 * set_val)
{
if (time < Keys[0].Time) {
*set_val = Keys[0].Point;
return;
}
if (time >= Keys[Keys.Count() - 1].Time) {
*set_val = Keys[Keys.Count() - 1].Point;
return;
}
int i0,i1;
float t;
Find_Interval(time,&i0,&i1,&t);
*set_val = Keys[i0].Point + t * (Keys[i1].Point - Keys[i0].Point);
}
const PersistFactoryClass & LinearCurve3DClass::Get_Factory(void) const
{
return _LinearCurve3DFactory;
}
bool LinearCurve3DClass::Save(ChunkSaveClass & csave)
{
csave.Begin_Chunk(LINEARCURVE3D_CHUNK_CURVE3D);
Curve3DClass::Save(csave);
csave.End_Chunk();
return true;
}
bool LinearCurve3DClass::Load(ChunkLoadClass & cload)
{
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID())
{
case LINEARCURVE3D_CHUNK_CURVE3D:
Curve3DClass::Load(cload);
break;
default:
WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",__FILE__,__LINE__));
break;
}
cload.Close_Chunk();
}
return true;
}
/***********************************************************************************************
**
** Curve1DClass
**
***********************************************************************************************/
Curve1DClass::Curve1DClass(void) :
IsLooping(false)
{
}
Curve1DClass::Curve1DClass(const Curve1DClass & that)
{
*this = that;
}
Curve1DClass::~Curve1DClass(void)
{
}
Curve1DClass & Curve1DClass::operator = (const Curve1DClass & that)
{
IsLooping = that.IsLooping;
Keys = that.Keys;
return *this;
}
bool Curve1DClass::Is_Looping(void)
{
return IsLooping;
}
void Curve1DClass::Set_Looping(bool onoff)
{
IsLooping = onoff;
}
float Curve1DClass::Get_Start_Time(void)
{
if (Keys.Count() > 0) {
return Keys[0].Time;
} else {
return 0.0f;
}
}
float Curve1DClass::Get_End_Time(void)
{
if (Keys.Count() > 0) {
return Keys[Keys.Count() - 1].Time;
} else {
return 0.0f;
}
}
int Curve1DClass::Key_Count(void)
{
return Keys.Count();
}
void Curve1DClass::Get_Key(int i,float * set_point,float * set_t,unsigned int * extra)
{
assert(i >= 0);
assert(i < Keys.Count());
if (set_point != NULL) {
*set_point = Keys[i].Point;
}
if (set_t != NULL) {
*set_t = Keys[i].Time;
}
if (extra != NULL) {
*extra = Keys[i].Extra;
}
}
void Curve1DClass::Set_Key(int i,float point,unsigned int extra)
{
assert(i >= 0);
assert(i < Keys.Count());
Keys[i].Point = point;
Keys[i].Extra = extra;
}
int Curve1DClass::Add_Key(float point,float t,unsigned int extra)
{
int idx = 0;
while (idx < Keys.Count() && Keys[idx].Time < t) {
idx++;
}
KeyClass newkey;
newkey.Point = point;
newkey.Time = t;
newkey.Extra = extra;
Keys.Insert(idx,newkey);
return idx;
}
void Curve1DClass::Remove_Key(int i)
{
assert(i >= 0);
assert(i < Keys.Count());
Keys.Delete(i);
}
void Curve1DClass::Clear_Keys(void)
{
Keys.Clear();
}
void Curve1DClass::Find_Interval(float time,int * i0,int * i1,float * t)
{
if (IsLooping) {
if (time < Keys[0].Time) {
*i0 = Keys.Count() - 1;
*i1 = 0;
float interval = 1.0f - Keys[*i0].Time + Keys[*i1].Time;
*t = (1.0f - Keys[*i0].Time + time) / interval;
return;
}
else if (time > Keys[Keys.Count() - 1].Time) {
*i0 = Keys.Count() - 1;
*i1 = 0;
float interval = 1.0f - Keys[*i0].Time + Keys[*i1].Time;
*t = (time - Keys[*i0].Time) / interval;
return;
}
}
else {
WWASSERT(time >= Keys[0].Time);
WWASSERT(time <= Keys[Keys.Count()-1].Time);
}
int i=0;
while (time > Keys[i+1].Time) {
i++;
}
*i0 = i;
*i1 = i+1;
*t = (time - Keys[i].Time) / (Keys[i+1].Time - Keys[i].Time);
}
bool Curve1DClass::Save(ChunkSaveClass & csave)
{
int keycount = Keys.Count();
csave.Begin_Chunk(CURVE1D_CHUNK_VARIABLES);
WRITE_MICRO_CHUNK(csave,CURVE1D_VARIABLE_ISLOOPING,IsLooping);
WRITE_MICRO_CHUNK(csave,CURVE1D_VARIABLE_KEYCOUNT,keycount);
csave.End_Chunk();
// Saving the keys, Note that if the format of a key changes we'll
// need a new chunk. (I didn't wrap each variable in its own chunk)
csave.Begin_Chunk(CURVE1D_CHUNK_KEYS);
for (int i=0; i<keycount; i++) {
csave.Write(&(Keys[i].Point),sizeof(Keys[i].Point));
csave.Write(&(Keys[i].Time),sizeof(Keys[i].Time));
csave.Write(&(Keys[i].Extra),sizeof(Keys[i].Extra));
}
csave.End_Chunk();
return true;
}
bool Curve1DClass::Load(ChunkLoadClass & cload)
{
int i;
int keycount = 0;
KeyClass newkey;
// reset the curve
Keys.Delete_All();
// read in the chunks
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID())
{
case CURVE1D_CHUNK_VARIABLES:
while (cload.Open_Micro_Chunk()) {
switch(cload.Cur_Micro_Chunk_ID()) {
READ_MICRO_CHUNK(cload,CURVE1D_VARIABLE_ISLOOPING,IsLooping);
READ_MICRO_CHUNK(cload,CURVE1D_VARIABLE_KEYCOUNT,keycount);
}
cload.Close_Micro_Chunk();
}
break;
case CURVE1D_CHUNK_KEYS:
for (i=0; i<keycount; i++) {
cload.Read(&(newkey.Point),sizeof(newkey.Point));
cload.Read(&(newkey.Time),sizeof(newkey.Time));
cload.Read(&(newkey.Extra),sizeof(newkey.Extra));
Keys.Add(newkey);
}
break;
default:
WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",__FILE__,__LINE__));
break;
}
cload.Close_Chunk();
}
return true;
}
/***********************************************************************************************
**
** LinearCurve1DClass, linearly interpolates the keys
**
***********************************************************************************************/
void LinearCurve1DClass::Evaluate(float time,float * set_val)
{
if (!IsLooping) {
if (time < Keys[0].Time) {
*set_val = Keys[0].Point;
return;
}
if (time >= Keys[Keys.Count() - 1].Time) {
*set_val = Keys[Keys.Count() - 1].Point;
return;
}
}
int i0,i1;
float t;
Find_Interval(time,&i0,&i1,&t);
*set_val = Keys[i0].Point + t * (Keys[i1].Point - Keys[i0].Point);
}
const PersistFactoryClass & LinearCurve1DClass::Get_Factory(void) const
{
return _LinearCurve1DFactory;
}
bool LinearCurve1DClass::Save(ChunkSaveClass & csave)
{
csave.Begin_Chunk(LINEARCURVE1D_CHUNK_CURVE1D);
Curve1DClass::Save(csave);
csave.End_Chunk();
return true;
}
bool LinearCurve1DClass::Load(ChunkLoadClass & cload)
{
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID())
{
case LINEARCURVE1D_CHUNK_CURVE1D:
Curve1DClass::Load(cload);
break;
default:
WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",__FILE__,__LINE__));
break;
}
cload.Close_Chunk();
}
return true;
}

View File

@@ -0,0 +1,179 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /VSS_Sync/wwmath/curve.h $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 6/13/01 2:18p $*
* *
* $Revision:: 8 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef CURVE_H
#define CURVE_H
#ifndef ALWAYS_H
#include "always.h"
#endif
#ifndef VECTOR_H
#include "vector.h"
#endif
#ifndef VECTOR3_H
#include "vector3.h"
#endif
#ifndef PERSIST_H
#include "persist.h"
#endif
class ChunkLoadClass;
class ChunkSaveClass;
class Curve3DClass : public PersistClass
{
public:
Curve3DClass(void);
Curve3DClass(const Curve3DClass & that);
virtual ~Curve3DClass(void);
Curve3DClass & operator = (const Curve3DClass & that);
virtual void Evaluate(float time,Vector3 * set_val) = 0;
virtual bool Is_Looping(void);
virtual void Set_Looping(bool onoff);
virtual int Key_Count(void);
virtual void Get_Key(int i,Vector3 * set_point,float * set_t);
virtual void Set_Key(int i,const Vector3 & point);
virtual int Add_Key(const Vector3 & point,float t);
virtual void Remove_Key(int i);
virtual void Clear_Keys(void);
float Get_Start_Time(void);
float Get_End_Time(void);
// persistant object support
virtual bool Save (ChunkSaveClass &csave);
virtual bool Load (ChunkLoadClass &cload);
protected:
void Find_Interval(float time,int * i0,int * i1,float * t);
class KeyClass
{
public:
Vector3 Point;
float Time;
bool operator == (const KeyClass & that) { return ((Point == that.Point) && (Time == that.Time)); }
bool operator != (const KeyClass & that) { return !KeyClass::operator==(that); }
};
bool IsLooping;
DynamicVectorClass<KeyClass> Keys;
};
class LinearCurve3DClass : public Curve3DClass
{
public:
virtual void Evaluate(float time,Vector3 * set_val);
// persistant object support
virtual const PersistFactoryClass & Get_Factory(void) const;
virtual bool Save(ChunkSaveClass &csave);
virtual bool Load(ChunkLoadClass &cload);
};
/*
** 1-Dimensional curve classes.
*/
class Curve1DClass : public PersistClass
{
public:
Curve1DClass(void);
Curve1DClass(const Curve1DClass & that);
virtual ~Curve1DClass(void);
Curve1DClass & operator = (const Curve1DClass & that);
virtual void Evaluate(float time,float * set_val) = 0;
virtual bool Is_Looping(void);
virtual void Set_Looping(bool onoff);
virtual int Key_Count(void);
virtual void Get_Key(int i,float * set_point,float * set_t,unsigned int * extra=NULL);
virtual void Set_Key(int i,float point,unsigned int extra=0);
virtual int Add_Key(float point,float t,unsigned int extra=0);
virtual void Remove_Key(int i);
virtual void Clear_Keys(void);
float Get_Start_Time(void);
float Get_End_Time(void);
// persistant object support
virtual bool Save (ChunkSaveClass &csave);
virtual bool Load (ChunkLoadClass &cload);
protected:
void Find_Interval(float time,int * i0,int * i1,float * t);
class KeyClass
{
public:
float Point;
float Time;
unsigned int Extra;
bool operator == (const KeyClass & that) { return ((Point == that.Point) && (Time == that.Time) && (Extra == that.Extra)); }
bool operator != (const KeyClass & that) { return !KeyClass::operator==(that); }
};
bool IsLooping;
DynamicVectorClass<KeyClass> Keys;
};
class LinearCurve1DClass : public Curve1DClass
{
public:
virtual void Evaluate(float time,float * set_val);
// persistant object support
virtual const PersistFactoryClass & Get_Factory(void) const;
virtual bool Save(ChunkSaveClass &csave);
virtual bool Load(ChunkLoadClass &cload);
};
#endif //CURVE_H

View File

@@ -0,0 +1,373 @@
/*
** Command & Conquer Generals Zero Hour(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/>.
*/
/* $Header: /Commando/Code/wwmath/euler.cpp 5 4/27/01 11:51a Jani_p $ */
/***********************************************************************************************
*** Confidential - Westwood Studios ***
***********************************************************************************************
* *
* Project Name : Commando / G Math Library *
* *
* $Archive:: /Commando/Code/wwmath/euler.cpp $*
* *
* Author:: Greg_h *
* *
* $Modtime:: 4/23/01 6:08p $*
* *
* $Revision:: 5 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* EulerAnglesClass::EulerAnglesClass -- constructor *
* EulerAnglesClass::Get_Angle -- returns angle 'i' of the euler angles *
* EulerAnglesClass::From_Matrix -- computes the equivalent euler angles for the given matrix*
* EulerAnglesClass::To_Matrix -- Builds a matrix from the given euler angles *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "euler.h"
#include <float.h>
/*********************************************************************
There are 24 possible conventions for Euler angles. They can
be designated by:
EulerAxis = Axis used initially
EulerParity = parity of axis permutation (even = x,y,z)
EulerRepeat = is last axis a repeat of the initial axis?
EulerFrame = frame from which axes are taken (rotating or static)
For example, in this system an euler angle convention of:
Rotate_X(a0);
Rotate_Y(a1);
Rotate_Z(a2);
would be described as EulerOrderXYZr.
*********************************************************************/
#define EULER_FRAME_STATIC 0x00000000
#define EULER_FRAME_ROTATING 0x00000001
#define EULER_FRAME(order) ((unsigned)(order) & 1)
#define EULER_REPEAT_NO 0x00000000
#define EULER_REPEAT_YES 0x00000001
#define EULER_REPEAT(order) (((unsigned)(order) >> 1) & 1)
#define EULER_PARITY_EVEN 0x00000000
#define EULER_PARITY_ODD 0x00000001
#define EULER_PARITY(order) (((unsigned)(order) >> 2) & 1)
#define EULER_BUILD_ORDER(i,p,r,f) (((((((i) << 1) + (p)) << 1) + (r)) << 1) + (f))
/* static axes */
int EulerOrderXYZs = EULER_BUILD_ORDER(0, EULER_PARITY_EVEN, EULER_REPEAT_NO, EULER_FRAME_STATIC);
int EulerOrderXYXs = EULER_BUILD_ORDER(0, EULER_PARITY_EVEN, EULER_REPEAT_YES, EULER_FRAME_STATIC);
int EulerOrderXZYs = EULER_BUILD_ORDER(0, EULER_PARITY_ODD, EULER_REPEAT_NO, EULER_FRAME_STATIC);
int EulerOrderXZXs = EULER_BUILD_ORDER(0, EULER_PARITY_ODD, EULER_REPEAT_YES, EULER_FRAME_STATIC);
int EulerOrderYZXs = EULER_BUILD_ORDER(1, EULER_PARITY_EVEN, EULER_REPEAT_NO, EULER_FRAME_STATIC);
int EulerOrderYZYs = EULER_BUILD_ORDER(1, EULER_PARITY_EVEN, EULER_REPEAT_YES, EULER_FRAME_STATIC);
int EulerOrderYXZs = EULER_BUILD_ORDER(1, EULER_PARITY_ODD, EULER_REPEAT_NO, EULER_FRAME_STATIC);
int EulerOrderYXYs = EULER_BUILD_ORDER(1, EULER_PARITY_ODD, EULER_REPEAT_YES, EULER_FRAME_STATIC);
int EulerOrderZXYs = EULER_BUILD_ORDER(2, EULER_PARITY_EVEN, EULER_REPEAT_NO, EULER_FRAME_STATIC);
int EulerOrderZXZs = EULER_BUILD_ORDER(2, EULER_PARITY_EVEN, EULER_REPEAT_YES, EULER_FRAME_STATIC);
int EulerOrderZYXs = EULER_BUILD_ORDER(2, EULER_PARITY_ODD, EULER_REPEAT_NO, EULER_FRAME_STATIC);
int EulerOrderZYZs = EULER_BUILD_ORDER(2, EULER_PARITY_ODD, EULER_REPEAT_YES, EULER_FRAME_STATIC);
/* rotating axes */
int EulerOrderZYXr = EULER_BUILD_ORDER(0, EULER_PARITY_EVEN, EULER_REPEAT_NO, EULER_FRAME_ROTATING);
int EulerOrderXYXr = EULER_BUILD_ORDER(0, EULER_PARITY_EVEN, EULER_REPEAT_YES, EULER_FRAME_ROTATING);
int EulerOrderYZXr = EULER_BUILD_ORDER(0, EULER_PARITY_ODD, EULER_REPEAT_NO, EULER_FRAME_ROTATING);
int EulerOrderXZXr = EULER_BUILD_ORDER(0, EULER_PARITY_ODD, EULER_REPEAT_YES, EULER_FRAME_ROTATING);
int EulerOrderXZYr = EULER_BUILD_ORDER(1, EULER_PARITY_EVEN, EULER_REPEAT_NO, EULER_FRAME_ROTATING);
int EulerOrderYZYr = EULER_BUILD_ORDER(1, EULER_PARITY_EVEN, EULER_REPEAT_YES, EULER_FRAME_ROTATING);
int EulerOrderZXYr = EULER_BUILD_ORDER(1, EULER_PARITY_ODD, EULER_REPEAT_NO, EULER_FRAME_ROTATING);
int EulerOrderYXYr = EULER_BUILD_ORDER(1, EULER_PARITY_ODD, EULER_REPEAT_YES, EULER_FRAME_ROTATING);
int EulerOrderYXZr = EULER_BUILD_ORDER(2, EULER_PARITY_EVEN, EULER_REPEAT_NO, EULER_FRAME_ROTATING);
int EulerOrderZXZr = EULER_BUILD_ORDER(2, EULER_PARITY_EVEN, EULER_REPEAT_YES, EULER_FRAME_ROTATING);
int EulerOrderXYZr = EULER_BUILD_ORDER(2, EULER_PARITY_ODD, EULER_REPEAT_NO, EULER_FRAME_ROTATING);
int EulerOrderZYZr = EULER_BUILD_ORDER(2, EULER_PARITY_ODD, EULER_REPEAT_YES, EULER_FRAME_ROTATING);
#define EULER_SAFE "\000\001\002\000"
#define EULER_NEXT "\001\002\000\001"
#define EULER_AXIS_I(order) ((int)(EULER_SAFE[(((unsigned)(ord)>>3) & 3)]))
#define EULER_AXIS_J(order) ((int)(EULER_NEXT[EULER_AXIS_I(order) + (EULER_PARITY(order) == EULER_PARITY_ODD)]))
#define EULER_AXIS_K(order) ((int)(EULER_NEXT[EULER_AXIS_I(order) + (EULER_PARITY(order) != EULER_PARITY_ODD)]))
#define EULER_AXIS_H(order) ((EULER_REPEAT(order) == EULER_REPEAT_NO) ? EULER_AXIS_K(order) : EULER_AXIS_I(order))
/* local functions */
static void _euler_unpack_order(int order,int &i,int &j,int &k,int &h,int &n,int &s,int &f);
static int _euler_axis_i(int order);
static int _euler_axis_j(int order);
static int _euler_axis_k(int order);
static int _euler_axis_h(int order);
/***********************************************************************************************
* EulerAnglesClass::EulerAnglesClass -- constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
EulerAnglesClass::EulerAnglesClass(const Matrix3D & M,int order)
{
this->From_Matrix(M,order);
}
/***********************************************************************************************
* EulerAnglesClass::Get_Angle -- returns angle 'i' of the euler angles *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
double EulerAnglesClass::Get_Angle(int i)
{
return Angle[i];
}
/***********************************************************************************************
* EulerAnglesClass::From_Matrix -- computes the equivalent euler angles for the given matrix *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
void EulerAnglesClass::From_Matrix(const Matrix3D & M, int order)
{
int i,j,k,h,n,s,f;
Order = order;
_euler_unpack_order(order,i,j,k,h,n,s,f);
if (s == EULER_REPEAT_YES) {
double sy = sqrt(M[i][j]*M[i][j] + M[i][k]*M[i][k]);
if (sy > 16*FLT_EPSILON) {
Angle[0] = WWMath::Atan2(M[i][j],M[i][k]);
Angle[1] = WWMath::Atan2(sy,M[i][i]);
Angle[2] = WWMath::Atan2(M[j][i],-M[k][i]);
} else {
Angle[0] = WWMath::Atan2(-M[j][k],M[j][j]);
Angle[1] = WWMath::Atan2(sy,M[i][i]);
Angle[2] = 0.0;
}
} else {
double cy = sqrt(M[i][i]*M[i][i] + M[j][i]*M[j][i]);
if (cy > 16*FLT_EPSILON) {
Angle[0] = WWMath::Atan2(M[k][j],M[k][k]);
Angle[1] = WWMath::Atan2(-M[k][i],cy);
Angle[2] = WWMath::Atan2(M[j][i],M[i][i]);
} else {
Angle[0] = WWMath::Atan2(-M[j][k],M[j][j]);
Angle[1] = WWMath::Atan2(-M[k][i],cy);
Angle[2] = 0;
}
}
if (n==EULER_PARITY_ODD) { Angle[0] = -Angle[0]; Angle[1] = -Angle[1]; Angle[2] = -Angle[2]; }
if (f==EULER_FRAME_ROTATING) { double t = Angle[0]; Angle[0] = Angle[2]; Angle[2] = t; }
// Trying to "clean" up the eulers, special cased for XYZr
if (order == EulerOrderXYZr) {
const double PI = 3.141592654;
double x2 = PI + Angle[0];
double y2 = PI - Angle[1];
double z2 = PI + Angle[2];
if (x2 > PI) {
x2 = x2 - 2*PI;
}
if (y2 > PI) {
y2 = y2 - 2*PI;
}
if (z2 > PI) {
z2 = z2 - 2*PI;
}
double mag0 = Angle[0]*Angle[0] + Angle[1]*Angle[1] + Angle[2]*Angle[2];
double mag1 = x2*x2 + y2*y2 + z2*z2;
if (mag1 < mag0) {
Angle[0] = x2;
Angle[1] = y2;
Angle[2] = z2;
}
}
}
/***********************************************************************************************
* EulerAnglesClass::To_Matrix -- Builds a matrix from the given euler angles *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
void EulerAnglesClass::To_Matrix(Matrix3D & M)
{
M.Make_Identity();
double a0,a1,a2;
double ti,tj,th,ci,cj,ch,si,sj,sh,cc,cs,sc,ss;
int i,j,k,h,n,s,f;
a0 = Angle[0];
a1 = Angle[1];
a2 = Angle[2];
_euler_unpack_order(Order,i,j,k,h,n,s,f);
if (f == EULER_FRAME_ROTATING) {
double t = a0; a0 = a2; a2 = t;
}
if (n == EULER_PARITY_ODD) {
a0 = -a0; a1 = -a1; a2 = -a2;
}
ti = a0; tj = a1; th = a2;
ci = WWMath::Cos(ti); cj = WWMath::Cos(tj); ch = WWMath::Cos(th);
si = WWMath::Sin(ti); sj = WWMath::Sin(tj); sh = WWMath::Sin(th);
cc = ci*ch;
cs = ci*sh;
sc = si*ch;
ss = si*sh;
if (s == EULER_REPEAT_YES) {
M[i][i] = (float)(cj); M[i][j] = (float)(sj*si); M[i][k] = (float)(sj*ci);
M[j][i] = (float)(sj*sh); M[j][j] = (float)(-cj*ss+cc); M[j][k] = (float)(-cj*cs-sc);
M[k][i] = (float)(-sj*ch); M[k][j] = (float)(cj*sc+cs); M[k][k] = (float)(cj*cc-ss);
} else {
M[i][i] = (float)(cj*ch); M[i][j] = (float)(sj*sc-cs); M[i][k] = (float)(sj*cc+ss);
M[j][i] = (float)(cj*sh); M[j][j] = (float)(sj*ss+cc); M[j][k] = (float)(sj*cs-sc);
M[k][i] = (float)(-sj); M[k][j] = (float)(cj*si); M[k][k] = (float)(cj*ci);
}
}
/*
** Local functions
*/
static int _euler_safe[] = { 0,1,2,0 };
static int _euler_next[] = { 1,2,0,1 };
int _euler_axis_i(int order)
{
return _euler_safe[ (order>>3) & 3 ];
}
int _euler_axis_j(int order)
{
int index = _euler_axis_i(order);
if (EULER_PARITY(order) == 1) {
index++;
}
return _euler_next[ index ];
}
int _euler_axis_k(int order)
{
int index = _euler_axis_i(order);
if (EULER_PARITY(order) != 1) {
index++;
}
return _euler_next[ index ];
}
int _euler_axis_h(int order)
{
if (EULER_REPEAT(order) == 1) {
return _euler_axis_k(order);
} else {
return _euler_axis_i(order);
}
}
void _euler_unpack_order(int order,int &i,int &j,int &k,int &h,int &n,int &s,int &f)
{
f = order & 1;
order >>= 1;
s = order & 1;
order >>= 1;
n = order & 1;
order >>= 1;
i = _euler_safe[order & 3];
j = _euler_next[i+n];
k = _euler_next[i+1-n];
h = (s ? k : i);
}

View File

@@ -0,0 +1,127 @@
/*
** Command & Conquer Generals Zero Hour(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/>.
*/
/* $Header: /Commando/Code/wwmath/euler.h 5 5/05/01 5:48p Jani_p $ */
/***********************************************************************************************
*** Confidential - Westwood Studios ***
***********************************************************************************************
* *
* Project Name : Commando / G Math Library *
* *
* $Archive:: /Commando/Code/wwmath/euler.h $*
* *
* Author:: Greg_h *
* *
* $Modtime:: 5/04/01 8:37p $*
* *
* $Revision:: 5 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef EULER_H
#define EULER_H
#include "always.h"
#include "matrix3d.h"
#include "quat.h"
/*********************************************************************
Euler Order Types
When creating an EulerAngles object, use one of the below
constants to describe the axis convention.
XYZ - order of the axes
s/r - whether the rotations are applied to a static or
rotating frame.
*********************************************************************/
/* static axes */
extern int EulerOrderXYZs;
extern int EulerOrderXYXs;
extern int EulerOrderXZYs;
extern int EulerOrderXZXs;
extern int EulerOrderYZXs;
extern int EulerOrderYZYs;
extern int EulerOrderYXZs;
extern int EulerOrderYXYs;
extern int EulerOrderZXYs;
extern int EulerOrderZXZs;
extern int EulerOrderZYXs;
extern int EulerOrderZYZs;
/* rotating axes */
extern int EulerOrderXYZr;
extern int EulerOrderXYXr;
extern int EulerOrderXZYr;
extern int EulerOrderXZXr;
extern int EulerOrderYZXr;
extern int EulerOrderYZYr;
extern int EulerOrderYXZr;
extern int EulerOrderYXYr;
extern int EulerOrderZXYr;
extern int EulerOrderZXZr;
extern int EulerOrderZYXr;
extern int EulerOrderZYZr;
/*********************************************************************
EulerAnglesClass
The purpose for this class is mainly for conversion. You can
choose a convention for the order of your rotations and then
convert matrices into a set of euler angles. You don't really
want to use this at run-time to convert matrices into angles.
The guts of this implementation is based on the article in Graphics
Gems IV by Ken Shoemake. The original article is on page 222.
*********************************************************************/
class EulerAnglesClass
{
public:
EulerAnglesClass(void) : Order(0) { Angle[0] = 0.0; Angle[1] = 0.0; Angle[2] = 0.0; };
EulerAnglesClass(const Matrix3D & from,int order);
void From_Matrix(const Matrix3D & from,int order);
void To_Matrix(Matrix3D & M);
double Get_Angle(int i);
private:
double Angle[3];
int Order;
};
#endif /*EULER_H*/

View File

@@ -0,0 +1,168 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/frustum.cpp $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 4/01/01 12:47p $*
* *
* $Revision:: 5 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* FrustumClass::Init -- Initialize a frustum object *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "frustum.h"
/***********************************************************************************************
* FrustumClass::Init -- Initialize a frustum object *
* *
* This function initializes a frustum from the description of a camera *
* *
* INPUT: *
* camera - camera transform, note that the camera looks down the -Z axis *
* vpmin - min corner of the z=-1.0 view plane (not necessarily the near clip plane) *
* vpmax - max corner of the z=-1.0 view plane (not necessarily the near clip plane) *
* znear - near clip plane (should be negative, negated if it is not) *
* zfar - far clip plane (should be negative, negated if it is not) *
* *
* OUTPUT: *
* *
* WARNINGS: *
* The vpmin and vpmax variables are the min and max of a view-plane at z=-1.0 *
* *
* *
* HISTORY: *
* 2/17/2000 gth : Created. *
*=============================================================================================*/
void FrustumClass::Init
(
const Matrix3D & camera,
const Vector2 & vpmin,
const Vector2 & vpmax,
float znear,
float zfar
)
{
int i;
// Store the camera transform
CameraTransform = camera;
// Forward is negative Z in our viewspace coordinate system.
// Just flip the sign if the user passed in positive values.
if ((znear > 0.0f) && (zfar > 0.0f)) {
znear = -znear;
zfar = -zfar;
}
// Calculate the corners of the camera frustum.
// Generate the camera-space frustum corners by linearly
// extrapolating the viewplane to the near and far z clipping planes.
// The camera frustum corners are defined in the following order:
// When looking at the frustum from the position of the camera, the near four corners are
// numbered: upper left 0, upper right 1, lower left 2, lower right 3. The far plane's
// Frustum corners are numbered from 4 to 7 in an analogous fashion.
// (remember: the camera space has x going to the right, y up and z backwards).
//calculate a proper z-vector assuming our right-handed coordinate system
Vector3 zv;
Vector3::Cross_Product(CameraTransform.Get_X_Vector(),CameraTransform.Get_Y_Vector(),&zv);
//compare correct z-vector with the one in the camera matrix. If they point in
//opposite directions, we have a reflected camera matrix.
if (Vector3::Dot_Product(CameraTransform.Get_Z_Vector(),zv) < 0)
{ //flip the frustum corners horizontally for a reflected matrix
Corners[1].Set(vpmin.X, vpmax.Y, 1.0);
Corners[5] = Corners[1];
Corners[1] *= znear;
Corners[5] *= zfar;
Corners[0].Set(vpmax.X, vpmax.Y, 1.0);
Corners[4] = Corners[0];
Corners[0] *= znear;
Corners[4] *= zfar;
Corners[3].Set(vpmin.X, vpmin.Y, 1.0);
Corners[7] = Corners[3];
Corners[3] *= znear;
Corners[7] *= zfar;
Corners[2].Set(vpmax.X, vpmin.Y, 1.0);
Corners[6] = Corners[2];
Corners[2] *= znear;
Corners[6] *= zfar;
}
else
{ //normal camera
Corners[0].Set(vpmin.X, vpmax.Y, 1.0);
Corners[4] = Corners[0];
Corners[0] *= znear;
Corners[4] *= zfar;
Corners[1].Set(vpmax.X, vpmax.Y, 1.0);
Corners[5] = Corners[1];
Corners[1] *= znear;
Corners[5] *= zfar;
Corners[2].Set(vpmin.X, vpmin.Y, 1.0);
Corners[6] = Corners[2];
Corners[2] *= znear;
Corners[6] *= zfar;
Corners[3].Set(vpmax.X, vpmin.Y, 1.0);
Corners[7] = Corners[3];
Corners[3] *= znear;
Corners[7] *= zfar;
}
// Transform the eight corners of the view frustum from camera space to world space.
for (i = 0; i < 8; i++) {
Matrix3D::Transform_Vector(CameraTransform, Corners[i], &(Corners[i]));
}
// Create the six frustum bounding planes from the eight corner Corners.
// The bounding planes are oriented so that their normals point outward
PlaneClass frustum_planes[6];
Planes[0].Set(Corners[0], Corners[3], Corners[1]); // near
Planes[1].Set(Corners[0], Corners[5], Corners[4]); // bottom
Planes[2].Set(Corners[0], Corners[6], Corners[2]); // right
Planes[3].Set(Corners[2], Corners[7], Corners[3]); // top
Planes[4].Set(Corners[1], Corners[7], Corners[5]); // left
Planes[5].Set(Corners[4], Corners[7], Corners[6]); // far
// find the bounding box of the entire frustum (may be used for sloppy quick rejection)
BoundMin = BoundMax = Corners[0];
for (i=1; i<8;i++) {
if (Corners[i].X < BoundMin.X) BoundMin.X = Corners[i].X;
if (Corners[i].X > BoundMax.X) BoundMax.X = Corners[i].X;
if (Corners[i].Y < BoundMin.Y) BoundMin.Y = Corners[i].Y;
if (Corners[i].Y > BoundMax.Y) BoundMax.Y = Corners[i].Y;
if (Corners[i].Z < BoundMin.Z) BoundMin.Z = Corners[i].Z;
if (Corners[i].Z > BoundMax.Z) BoundMax.Z = Corners[i].Z;
}
}

View File

@@ -0,0 +1,73 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/frustum.h $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 3/29/00 10:20a $*
* *
* $Revision:: 3 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef FRUSTUM_H
#define FRUSTUM_H
#include "vector3.h"
#include "plane.h"
class FrustumClass
{
public:
void Init( const Matrix3D & camera,
const Vector2 & viewport_min,
const Vector2 & viewport_max,
float znear,
float zfar );
const Vector3 & Get_Bound_Min(void) const { return BoundMin; }
const Vector3 & Get_Bound_Max(void) const { return BoundMax; }
public:
Matrix3D CameraTransform;
PlaneClass Planes[6];
Vector3 Corners[8];
Vector3 BoundMin;
Vector3 BoundMax;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,706 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/gridcull.h $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 5/10/01 10:42a $*
* *
* $Revision:: 14 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* GridCullSystemClass::clamp_indices_to_grid -- constrains indices to be a valid location *
* GridCullSystemClass::map_point_to_cell -- determines which cell the point is in *
* GridCullSystemClass::map_point_to_address -- determines the address of a point in the gri *
* GridCullSystemClass::map_indices_to_address -- computes the address for given index tripl *
* GridCullSystemClass::total_cell_count -- returns the total number of cells in the grid *
* GridCullSystemClass::compute_box -- computes the bounding box for a grid cell *
* GridCullSystemClass::compute_box -- computes bounding box for a range of grid cells *
* GridCullSystemClass::init_volume -- inits volume to contain the given range *
* GridCullSystemClass::init_volume -- inits volume to contain the given line segment *
* GridCullSystemClass::init_volume -- inits volume to contain the given box *
* GridCullSystemClass::init_volume -- inits volume to contain the given oriented box *
* GridCullSystemClass::init_volume -- inits volume to contain the given frustum *
* GridCullSystemClass::VolumeStruct::VolumeStruct -- constructor *
* GridCullSystemClass::VolumeStruct::VolumeStruct -- constructor *
* GridCullSystemClass::VolumeStruct::Is_Leaf -- check if volume is a leaf *
* GridCullSystemClass::VolumeStruct::Is_Empty -- check if volume is empty (or invalid) *
* GridCullSystemClass::VolumeStruct::Split -- split this volume *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#include "cullsys.h"
#include "mempool.h"
#include "frustum.h"
#include "aabox.h"
#include "lineseg.h"
#include "obbox.h"
#include <string.h>
class ChunkLoadClass;
class ChunkSaveClass;
/*
** GridCullSystemClass
** This is a culling system designed for dynamic objects (objects which are moving or
** changing bounding box size). It features O(1) insertion as opposed to
** the AABTree, QuadTree, or Octree insertion times which are O(logn). It's disadvantages
** compared to the above mentioned systems are that it must uniformly divide space. The
** AABTree conforms to the geometry placed in it and can therefore cull things more
** efficiently. In actual use, this system is like an AAB-tree which is uniformly subdivided,
** its just that we can jump straight to the leaves of the tree for insertion...
**
** The bounding volume for each grid cell is considered to be the volume of the cell, expanded
** by the maximum object size. Inserting an object into the grid is a matter of determining
** which cell its center point is in. Objects which are larger than the maximum size are
** allowed but they are simply put into a linked list which is iterated with each call to
** the culling system (linearly culled rather than logarithmic...)
*/
class GridCullSystemClass : public CullSystemClass
{
public:
GridCullSystemClass(void);
~GridCullSystemClass(void);
virtual void Collect_Objects(const Vector3 & point);
virtual void Collect_Objects(const AABoxClass & box);
virtual void Collect_Objects(const OBBoxClass & box);
virtual void Collect_Objects(const FrustumClass & frustum);
virtual void Re_Partition(const Vector3 & min,const Vector3 & max,float objdim);
virtual void Update_Culling(CullableClass * obj);
virtual void Load(ChunkLoadClass & cload);
virtual void Save(ChunkSaveClass & csave);
virtual int Get_Object_Count (void) const { return ObjCount; }
/*
** Statistics
*/
struct StatsStruct
{
int NodeCount;
int NodesAccepted;
int NodesTriviallyAccepted;
int NodesRejected;
};
void Reset_Statistics(void);
const StatsStruct & Get_Statistics(void);
void Get_Min_Cell_Size (Vector3 &size) const { size = MinCellSize; }
void Set_Min_Cell_Size (const Vector3 &size) { MinCellSize = size; }
int Get_Termination_Count (void) const { return TerminationCellCount; }
void Set_Termination_Count (int count) { TerminationCellCount = count; }
protected:
void Collect_And_Unlink_All(void);
void Add_Object_Internal(CullableClass * obj);
void Remove_Object_Internal(CullableClass * obj);
enum
{
TERMINATION_CELL_COUNT = 16384, // algorithm terminates if we ever have more than this many cells.
UNGRIDDED_ADDRESS = 0xFFFFFFFF // address given to objs that didn't fit in grid
};
// Constants which control the division of space:
Vector3 MinCellSize; // min dimensions for a cell (don't go below this...)
float MaxObjExtent; // max extent/radius (objects bigger than this are just put in a list)
int TerminationCellCount;
// Constants that define the division of space
Vector3 Origin;
Vector3 CellDim;
Vector3 OOCellDim;
int CellCount[3];
// 3D array of pointers to objects in each cell
CullableClass ** Cells;
// list of objs that were outside or too big for the grid.
CullableClass * NoGridList;
// number of objects in the system
int ObjCount;
// statistics
StatsStruct Stats;
// Structure used to define a volume in the grid. The volume spans from the cell indexed
// by Min[0],Min[1],Min[2] to the cell indexed by Max[0]-1,Max[1]-1,Max[2]-1.
struct VolumeStruct
{
VolumeStruct(void);
VolumeStruct(int i0,int j0,int k0,int i1,int j1,int k1);
bool Is_Leaf(void) const;
bool Is_Empty(void) const;
void Split(VolumeStruct & v0,VolumeStruct & v1) const;
int Min[3];
int Max[3];
};
void link_object(CullableClass * obj);
void link_object(CullableClass * obj,int address);
void unlink_object(CullableClass * obj);
void link_object_to_list(CullableClass ** head,CullableClass * obj);
void unlink_object_from_list(CullableClass ** head,CullableClass * obj);
bool map_point_to_cell(const Vector3 & pt,int & set_i,int & set_j,int & set_k);
bool map_point_to_address(const Vector3 & pt,int & set_address);
WWINLINE int map_indices_to_address(int i,int j,int k);
void clamp_indices_to_grid(int * i,int * j,int * k);
int total_cell_count(void);
void compute_box(int i,int j,int k,AABoxClass * set_box);
void compute_box(const VolumeStruct & area, AABoxClass * set_box);
void init_volume(const Vector3 & bound_min,const Vector3 & bound_max,VolumeStruct * set_volume);
void init_volume(const Vector3 & point,VolumeStruct * set_volume);
void init_volume(const LineSegClass & line,VolumeStruct * set_volume);
void init_volume(const AABoxClass & box,VolumeStruct * set_volume);
void init_volume(const OBBoxClass & box,VolumeStruct * set_volume);
void init_volume(const FrustumClass & frustum,VolumeStruct * set_volume);
void collect_objects_in_leaf(const Vector3 & point,CullableClass * head);
void collect_objects_in_leaf(const AABoxClass & aabox,CullableClass * head);
void collect_objects_in_leaf(const OBBoxClass & obbox,CullableClass * head);
void collect_objects_in_leaf(const FrustumClass & frustum,CullableClass * head);
};
/*
** Macros for gathering statistics. Placed here in the header file so that
** derived classes can use them as well.
*/
#ifdef WWDEBUG
#define GRIDCULL_NODE_ACCEPTED Stats.NodesAccepted ++;
#define GRIDCULL_NODE_TRIVIALLY_ACCEPTED Stats.NodesTriviallyAccepted ++;
#define GRIDCULL_NODE_REJECTED Stats.NodesRejected ++;
#else
#define GRIDCULL_NODE_ACCEPTED
#define GRIDCULL_NODE_TRIVIALLY_ACCEPTED
#define GRIDCULL_NODE_REJECTED
#endif
/*
** TypedGridCullSystemClass
** This class simply enforces that a certain type of object is inserted into the grid cull system.
** It exposes the add, remove, and collection iterating functions and is intended to be the class
** actually used by the end user.
*/
template <class T> class TypedGridCullSystemClass : public GridCullSystemClass
{
public:
virtual void Add_Object(T * obj) { Add_Object_Internal(obj); }
virtual void Remove_Object(T * obj) { Remove_Object_Internal(obj); }
T * Get_First_Collected_Object(void) { return (T*)Get_First_Collected_Object_Internal(); }
T * Get_Next_Collected_Object(T * obj) { return (T*)Get_Next_Collected_Object_Internal(obj); }
};
/*
** GridLinkClass
** This structure is used to link cullable objects into a Grid culling system
** This class is should only be used by classes which derive from GridCullSystemClass
** not normal users.
*/
class GridLinkClass : public CullLinkClass, public AutoPoolClass<GridLinkClass,256>
{
public:
GridLinkClass(GridCullSystemClass * system);
virtual ~GridLinkClass(void);
int GridAddress; // address in the grid.
CullableClass * Prev; // prev object in this cell
CullableClass * Next; // next object in this cell
};
/*
** GridListIterator
** This is just a simple iterator that contains the code for traversing the
** list of objects either in a cell in the grid or in the NoGridList...
** This class should only be used by classes which derive from GridCullSystemClass
** not normal users.
*/
class GridListIterator
{
public:
GridListIterator(CullableClass * head) { First(head); }
void First(CullableClass * head) { Head = head; CurObj = head; }
void First(void) { CurObj = Head; }
void Next(void) { if (CurObj) { CurObj = ((GridLinkClass *)CurObj->Get_Cull_Link())->Next; } }
void Prev(void) { if (CurObj) { CurObj = ((GridLinkClass *)CurObj->Get_Cull_Link())->Prev; } }
bool Is_Done(void) { return (CurObj == NULL); }
CullableClass * Get_Obj(void) { if (CurObj) { CurObj->Add_Ref(); } return CurObj; }
CullableClass * Peek_Obj(void) { return CurObj; }
private:
CullableClass * Head; // head of the list we're working in
CullableClass * CurObj; // node we're currently at.
};
/***********************************************************************************************
* GridCullSystemClass::clamp_indices_to_grid -- constrains indices to be a valid location *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/30/2000 gth : Created. *
*=============================================================================================*/
WWINLINE void GridCullSystemClass::clamp_indices_to_grid(int * i,int * j,int * k)
{
if (*i < 0) *i = 0;
if (*i >= CellCount[0]) *i = CellCount[0] - 1;
if (*j < 0) *j = 0;
if (*j >= CellCount[1]) *j = CellCount[1] - 1;
if (*k < 0) *k = 0;
if (*k >= CellCount[2]) *k = CellCount[2] - 1;
}
/***********************************************************************************************
* GridCullSystemClass::map_point_to_cell -- determines which cell the point is in *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/30/2000 gth : Created. *
*=============================================================================================*/
WWINLINE bool GridCullSystemClass::map_point_to_cell(const Vector3 & pt,int & set_i,int & set_j,int & set_k)
{
Vector3 dp = pt - Origin;
set_i = floor(dp.X * OOCellDim.X);
set_j = floor(dp.Y * OOCellDim.Y);
set_k = floor(dp.Z * OOCellDim.Z);
if ( (set_i >= 0) && (set_j >= 0) && (set_k >= 0) &&
(set_i < CellCount[0]) && (set_j < CellCount[1]) && (set_k < CellCount[2]) )
{
return true;
}
return false;
}
/***********************************************************************************************
* GridCullSystemClass::map_point_to_address -- determines the address of a point in the grid *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/30/2000 gth : Created. *
*=============================================================================================*/
WWINLINE bool GridCullSystemClass::map_point_to_address(const Vector3 & pt,int &set_address)
{
int i,j,k;
bool res = map_point_to_cell(pt,i,j,k);
if (res) {
set_address = map_indices_to_address(i,j,k);
return true;
} else {
set_address = UNGRIDDED_ADDRESS;
return false;
}
}
/***********************************************************************************************
* GridCullSystemClass::map_indices_to_address -- computes the address for given index triplet *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/30/2000 gth : Created. *
*=============================================================================================*/
WWINLINE int GridCullSystemClass::map_indices_to_address(int i,int j,int k)
{
return i + j*CellCount[0] + k*CellCount[0]*CellCount[1];
}
/***********************************************************************************************
* GridCullSystemClass::total_cell_count -- returns the total number of cells in the grid *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/30/2000 gth : Created. *
*=============================================================================================*/
WWINLINE int GridCullSystemClass::total_cell_count(void)
{
return CellCount[0] * CellCount[1] * CellCount[2];
}
/***********************************************************************************************
* GridCullSystemClass::compute_box -- computes the bounding box for a grid cell *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/30/2000 gth : Created. *
*=============================================================================================*/
WWINLINE void GridCullSystemClass::compute_box(int i,int j,int k,AABoxClass * set_box)
{
WWASSERT(set_box != NULL);
WWASSERT((i >= 0) && (j >= 0) && (k >= 0));
WWASSERT((i < CellCount[0]) && (j < CellCount[1]) && (k < CellCount[2]));
Vector3 min,max;
min.X = Origin.X + i * CellDim.X - MaxObjExtent;
min.Y = Origin.Y + j * CellDim.Y - MaxObjExtent;
min.Z = Origin.Z + k * CellDim.Z - MaxObjExtent;
max.X = min.X + CellDim.X + 2.0f*MaxObjExtent;
max.Y = min.Y + CellDim.Y + 2.0f*MaxObjExtent;
max.Z = min.Z + CellDim.Z + 2.0f*MaxObjExtent;
set_box->Init((min+max)*0.5f, (min-max)*0.5f);
}
/***********************************************************************************************
* GridCullSystemClass::compute_box -- computes bounding box for a range of grid cells *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/30/2000 gth : Created. *
*=============================================================================================*/
WWINLINE void GridCullSystemClass::compute_box(const GridCullSystemClass::VolumeStruct & vol, AABoxClass * set_box)
{
WWASSERT(set_box != NULL);
WWASSERT((vol.Min[0] >= 0) && (vol.Min[1] >= 0) && (vol.Min[2] >= 0));
WWASSERT((vol.Max[0] <= CellCount[0]) && (vol.Max[1] <= CellCount[1]) && (vol.Max[2] <= CellCount[2]));
Vector3 min,max;
min.X = Origin.X + vol.Min[0] * CellDim.X - MaxObjExtent;
min.Y = Origin.Y + vol.Min[1] * CellDim.Y - MaxObjExtent;
min.Z = Origin.Z + vol.Min[2] * CellDim.Z - MaxObjExtent;
max.X = Origin.X + vol.Max[0] * CellDim.X + MaxObjExtent;
max.Y = Origin.Y + vol.Max[1] * CellDim.Y + MaxObjExtent;
max.Z = Origin.Z + vol.Max[2] * CellDim.Z + MaxObjExtent;
Vector3 center((max.X+min.X)*0.5f,(max.Y+min.Y)*0.5f,(max.Z+min.Z)*0.5f);
Vector3 extent((max.X-min.X)*0.5f,(max.Y-min.Y)*0.5f,(max.Z-min.Z)*0.5f);
set_box->Init(center,extent);
}
/***********************************************************************************************
* GridCullSystemClass::init_volume -- inits volume to contain the given range *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/30/2000 gth : Created. *
*=============================================================================================*/
WWINLINE void GridCullSystemClass::init_volume
(
const Vector3 & bound_min,
const Vector3 & bound_max,
VolumeStruct * set_vol
)
{
// expand the box by the maximum size of any object
Vector3 grid_min = bound_min;
grid_min.X -= MaxObjExtent;
grid_min.Y -= MaxObjExtent;
grid_min.Z -= MaxObjExtent;
Vector3 grid_max = bound_max;
grid_max.X += MaxObjExtent;
grid_max.Y += MaxObjExtent;
grid_max.Z += MaxObjExtent;
// now compute the grid coordinates of the corners of the box
GridCullSystemClass::map_point_to_cell(grid_min,set_vol->Min[0],set_vol->Min[1],set_vol->Min[2]);
GridCullSystemClass::map_point_to_cell(grid_max,set_vol->Max[0],set_vol->Max[1],set_vol->Max[2]);
// now clamp the volume to the actual grid
clamp_indices_to_grid(&(set_vol->Min[0]),&(set_vol->Min[1]),&(set_vol->Min[2]));
clamp_indices_to_grid(&(set_vol->Max[0]),&(set_vol->Max[1]),&(set_vol->Max[2]));
// increment the outer edge of the box due to the way we traverse the grid...
set_vol->Max[0] ++;
set_vol->Max[1] ++;
set_vol->Max[2] ++;
}
/***********************************************************************************************
* GridCullSystemClass::init_volume -- inits volume to contain the given line segment *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/30/2000 gth : Created. *
*=============================================================================================*/
WWINLINE void GridCullSystemClass::init_volume(const LineSegClass & line,VolumeStruct * set_volume)
{
Vector3 min_pt,max_pt;
min_pt.X = WWMath::Min(line.Get_P0().X,line.Get_P1().X);
max_pt.X = WWMath::Max(line.Get_P0().X,line.Get_P1().X);
min_pt.Y = WWMath::Min(line.Get_P0().Y,line.Get_P1().Y);
max_pt.Y = WWMath::Max(line.Get_P0().Y,line.Get_P1().Y);
min_pt.Z = WWMath::Min(line.Get_P0().Z,line.Get_P1().Z);
max_pt.Z = WWMath::Max(line.Get_P0().Z,line.Get_P1().Z);
init_volume(min_pt,max_pt,set_volume);
}
/***********************************************************************************************
* GridCullSystemClass::init_volume -- inits volume to contain the given box *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/30/2000 gth : Created. *
*=============================================================================================*/
WWINLINE void GridCullSystemClass::init_volume(const AABoxClass & box,VolumeStruct * set_volume)
{
init_volume(box.Center - box.Extent,box.Center + box.Extent,set_volume);
}
/***********************************************************************************************
* GridCullSystemClass::init_volume -- inits volume to contain the given oriented box *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/30/2000 gth : Created. *
*=============================================================================================*/
WWINLINE void GridCullSystemClass::init_volume(const OBBoxClass & box,VolumeStruct * set_volume)
{
Vector3 aaextent;
box.Compute_Axis_Aligned_Extent(&aaextent);
init_volume(box.Center - aaextent,box.Center + aaextent,set_volume);
}
/***********************************************************************************************
* GridCullSystemClass::init_volume -- inits volume to contain the given frustum *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/30/2000 gth : Created. *
*=============================================================================================*/
WWINLINE void GridCullSystemClass::init_volume(const FrustumClass & frustum,VolumeStruct * set_volume)
{
init_volume(frustum.Get_Bound_Min(),frustum.Get_Bound_Max(),set_volume);
}
/***********************************************************************************************
* GridCullSystemClass::VolumeStruct::VolumeStruct -- constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*=============================================================================================*/
WWINLINE GridCullSystemClass::VolumeStruct::VolumeStruct(void)
{
}
/***********************************************************************************************
* GridCullSystemClass::VolumeStruct::VolumeStruct -- constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/30/2000 gth : Created. *
*=============================================================================================*/
WWINLINE GridCullSystemClass::VolumeStruct::VolumeStruct(int i0,int j0,int k0,int i1,int j1,int k1)
{
Min[0] = i0;
Min[1] = j0;
Min[2] = k0;
Max[0] = i1;
Max[1] = j1;
Max[2] = k1;
WWASSERT(Max[0] > Min[0]);
WWASSERT(Max[1] > Min[1]);
WWASSERT(Max[2] > Min[2]);
}
/***********************************************************************************************
* GridCullSystemClass::VolumeStruct::Is_Leaf -- check if volume is a leaf *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/30/2000 gth : Created. *
*=============================================================================================*/
WWINLINE bool GridCullSystemClass::VolumeStruct::Is_Leaf(void) const
{
return ((Max[0]-Min[0] == 1) && (Max[1]-Min[1] == 1) && (Max[2]-Min[2] == 1));
}
/***********************************************************************************************
* GridCullSystemClass::VolumeStruct::Is_Empty -- check if volume is empty (or invalid) *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/30/2000 gth : Created. *
*=============================================================================================*/
WWINLINE bool GridCullSystemClass::VolumeStruct::Is_Empty(void) const
{
return ((Max[0]-Min[0] <= 0) || (Max[1]-Min[1] <= 0) || (Max[2]-Min[2] <= 0));
}
/***********************************************************************************************
* GridCullSystemClass::VolumeStruct::Split -- split this volume *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/30/2000 gth : Created. *
*=============================================================================================*/
WWINLINE void GridCullSystemClass::VolumeStruct::Split(VolumeStruct & v0,VolumeStruct & v1) const
{
// find the longest dimension
int split_axis = 0;
int delta[3];
delta[0] = Max[0] - Min[0];
delta[1] = Max[1] - Min[1];
delta[2] = Max[2] - Min[2];
if (delta[1] > delta[split_axis]) split_axis = 1;
if (delta[2] > delta[split_axis]) split_axis = 2;
WWASSERT(delta[split_axis] > 0);
// split the volume perpendicularly to that dimension
memcpy(&v0,this,sizeof(VolumeStruct));
memcpy(&v1,this,sizeof(VolumeStruct));
v0.Max[split_axis] = v1.Min[split_axis] = Min[split_axis] + (delta[split_axis] >> 1);
}

View File

@@ -0,0 +1,437 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /VSS_Sync/wwmath/hermitespline.cpp $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 6/13/01 2:18p $*
* *
* $Revision:: 11 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "hermitespline.h"
#include "wwmathids.h"
#include "persistfactory.h"
#include "wwhack.h"
/*
** Force-Link this module because the linker can't detect that we actually need it...
*/
DECLARE_FORCE_LINK(hermitespline);
/*
** Save-Load stuff
*/
SimplePersistFactoryClass<HermiteSpline3DClass,WWMATH_CHUNKID_HERMITESPLINE3D> _HermiteSpline3DFactory;
SimplePersistFactoryClass<HermiteSpline1DClass,WWMATH_CHUNKID_HERMITESPLINE1D> _HermiteSpline1DFactory;
enum
{
// ID's used by HermiteSpline3D
HERMITE3D_CHUNK_CURVE3D = 0x00020727,
HERMITE3D_CHUNK_TANGENTS,
// ID's used by HermiteSpline1D
HERMITE1D_CHUNK_CURVE1D = 0x00020729,
HERMITE1D_CHUNK_TANGENTS,
};
/*
** Hermite Spline
*/
const HermiteSpline3DClass &HermiteSpline3DClass::operator= (const HermiteSpline3DClass &that)
{
//
// This is included for completeness only, it basically
// implements the default bitwise copy operator.
//
TangentsDirty = that.TangentsDirty;
Tangents = that.Tangents;
Curve3DClass::operator= (that);
return (*this);
}
void HermiteSpline3DClass::Set_Looping(bool onoff)
{
if (onoff != IsLooping) {
Curve3DClass::Set_Looping(onoff);
TangentsDirty = true;
}
}
void HermiteSpline3DClass::Evaluate(float time,Vector3 * set_val)
{
// if we're outside the range, return the start or end...
if (time < Keys[0].Time) {
*set_val = Keys[0].Point;
return;
}
if (time > Keys[Keys.Count() - 1].Time) {
*set_val = Keys[Keys.Count() - 1].Point;
return;
}
// if the tangents are marked dirty, give derived classes a chance to recompute them
if (TangentsDirty) {
Update_Tangents();
}
// ok find the segment
int i0,i1;
float t;
Find_Interval(time,&i0,&i1,&t);
float t2 = t*t;
float t3 = t2*t;
// hermite basis functions:
float h0 = 2*t3 - 3*t2 + 1;
float h1 = -2*t3 + 3*t2;
float h2 = t3 - 2*t2 + t;
float h3 = t3 - t2;
set_val->X = h0*Keys[i0].Point.X + h1*Keys[i1].Point.X +
h2*Tangents[i0].OutTangent.X + h3*Tangents[i1].InTangent.X;
set_val->Y = h0*Keys[i0].Point.Y + h1*Keys[i1].Point.Y +
h2*Tangents[i0].OutTangent.Y + h3*Tangents[i1].InTangent.Y;
set_val->Z = h0*Keys[i0].Point.Z + h1*Keys[i1].Point.Z +
h2*Tangents[i0].OutTangent.Z + h3*Tangents[i1].InTangent.Z;
}
void HermiteSpline3DClass::Evaluate_Derivative(float time,Vector3 * set_val)
{
// if we're outside the range, return the value for the start or end...
float min_time = Keys[0].Time;
float max_time = Keys[Keys.Count() - 1].Time;
time = MAX(time, min_time);
time = MIN(time, max_time);
// if the tangents are marked dirty, give derived classes a chance to recompute them
if (TangentsDirty) {
Update_Tangents();
}
// ok find the segment
int i0,i1;
float t;
Find_Interval(time,&i0,&i1,&t);
float t2 = t*t;
// derivatives of hermite basis functions:
float dh0 = 6*t2 - 6*t;
float dh1 = -6*t2 + 6*t;
float dh2 = 3*t2 - 4*t + 1;
float dh3 = 3*t2 - 2*t;
set_val->X = dh0*Keys[i0].Point.X + dh1*Keys[i1].Point.X +
dh2*Tangents[i0].OutTangent.X + dh3*Tangents[i1].InTangent.X;
set_val->Y = dh0*Keys[i0].Point.Y + dh1*Keys[i1].Point.Y +
dh2*Tangents[i0].OutTangent.Y + dh3*Tangents[i1].InTangent.Y;
set_val->Z = dh0*Keys[i0].Point.Z + dh1*Keys[i1].Point.Z +
dh2*Tangents[i0].OutTangent.Z + dh3*Tangents[i1].InTangent.Z;
}
void HermiteSpline3DClass::Set_Key(int i,const Vector3 & point)
{
Curve3DClass::Set_Key(i,point);
TangentsDirty = true;
}
int HermiteSpline3DClass::Add_Key(const Vector3 & point,float t)
{
int index = Curve3DClass::Add_Key(point,t);
TangentsDirty = true;
TangentsClass tan;
tan.InTangent.Set(0,0,0);
tan.OutTangent.Set(0,0,0);
Tangents.Insert(index,tan);
return index;
}
void HermiteSpline3DClass::Remove_Key(int i)
{
Tangents.Delete(i);
Curve3DClass::Remove_Key(i);
TangentsDirty = true;
}
void HermiteSpline3DClass::Clear_Keys(void)
{
Tangents.Clear();
Curve3DClass::Clear_Keys();
TangentsDirty = true;
}
void HermiteSpline3DClass::Set_Tangents(int i,const Vector3 & in_tan,const Vector3 & out_tan)
{
assert(i>=0);
assert(i<Keys.Count());
Tangents[i].InTangent = in_tan;
Tangents[i].OutTangent = out_tan;
}
void HermiteSpline3DClass::Get_Tangents(int i,Vector3 * set_in,Vector3 * set_out)
{
assert(i>=0);
assert(i<Keys.Count());
*set_in = Tangents[i].InTangent;
*set_out = Tangents[i].OutTangent;
}
const PersistFactoryClass & HermiteSpline3DClass::Get_Factory(void) const
{
return _HermiteSpline3DFactory;
}
bool HermiteSpline3DClass::Save(ChunkSaveClass &csave)
{
csave.Begin_Chunk(HERMITE3D_CHUNK_CURVE3D);
Curve3DClass::Save(csave);
csave.End_Chunk();
csave.Begin_Chunk(HERMITE3D_CHUNK_TANGENTS);
for (int i=0; i<Tangents.Count(); i++) {
csave.Write(&(Tangents[i].InTangent),sizeof(Tangents[i].InTangent));
csave.Write(&(Tangents[i].OutTangent),sizeof(Tangents[i].OutTangent));
}
csave.End_Chunk();
return true;
}
bool HermiteSpline3DClass::Load(ChunkLoadClass &cload)
{
int i;
TangentsClass newtangent;
// reset the array of tangents
Tangents.Delete_All();
// read in the chunks
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID())
{
case HERMITE3D_CHUNK_CURVE3D:
Curve3DClass::Load(cload);
break;
case HERMITE3D_CHUNK_TANGENTS:
for (i=0; i<Keys.Count(); i++) {
cload.Read(&(newtangent.InTangent),sizeof(newtangent.InTangent));
cload.Read(&(newtangent.OutTangent),sizeof(newtangent.OutTangent));
Tangents.Add(newtangent);
}
break;
default:
WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",__FILE__,__LINE__));
break;
}
cload.Close_Chunk();
}
WWASSERT(Keys.Count() == Tangents.Count());
return true;
}
/*
** 1-Dimensional Hermite Spline
*/
void HermiteSpline1DClass::Set_Looping(bool onoff)
{
if (onoff != IsLooping) {
Curve1DClass::Set_Looping(onoff);
TangentsDirty = true;
}
}
void HermiteSpline1DClass::Evaluate(float time,float * set_val)
{
if (Keys.Count() == 1)
{
*set_val = Keys[0].Point;
return;
}
if (!IsLooping)
{
// if we're outside the range, return the start or end...
if (time < Keys[0].Time) {
*set_val = Keys[0].Point;
return;
}
if (time > Keys[Keys.Count() - 1].Time) {
*set_val = Keys[Keys.Count() - 1].Point;
return;
}
}
// if the tangents are marked dirty, give derived classes a chance to recompute them
if (TangentsDirty) {
Update_Tangents();
}
// ok find the segment
int i0,i1;
float t;
Find_Interval(time,&i0,&i1,&t);
float t2 = t*t;
float t3 = t2*t;
// hermite basis functions:
float h0 = 2*t3 - 3*t2 + 1;
float h1 = -2*t3 + 3*t2;
float h2 = t3 - 2*t2 + t;
float h3 = t3 - t2;
*set_val = h0*Keys[i0].Point + h1*Keys[i1].Point +
h2*Tangents[i0].OutTangent + h3*Tangents[i1].InTangent;
}
void HermiteSpline1DClass::Set_Key(int i,float point,unsigned int extra)
{
Curve1DClass::Set_Key(i,point,extra);
TangentsDirty = true;
}
int HermiteSpline1DClass::Add_Key(float point,float t,unsigned int extra)
{
int index = Curve1DClass::Add_Key(point,t,extra);
TangentsDirty = true;
TangentsClass tan;
tan.InTangent = 0.0f;
tan.OutTangent = 0.0f;
Tangents.Insert(index,tan);
return index;
}
void HermiteSpline1DClass::Remove_Key(int i)
{
Tangents.Delete(i);
Curve1DClass::Remove_Key(i);
TangentsDirty = true;
}
void HermiteSpline1DClass::Clear_Keys(void)
{
Tangents.Clear();
Curve1DClass::Clear_Keys();
TangentsDirty = true;
}
void HermiteSpline1DClass::Set_Tangents(int i,float in_tan,float out_tan)
{
assert(i>=0);
assert(i<Keys.Count());
Tangents[i].InTangent = in_tan;
Tangents[i].OutTangent = out_tan;
}
void HermiteSpline1DClass::Get_Tangents(int i,float * set_in,float * set_out)
{
assert(i>=0);
assert(i<Keys.Count());
*set_in = Tangents[i].InTangent;
*set_out = Tangents[i].OutTangent;
}
const PersistFactoryClass & HermiteSpline1DClass::Get_Factory(void) const
{
return _HermiteSpline1DFactory;
}
bool HermiteSpline1DClass::Save(ChunkSaveClass &csave)
{
if (TangentsDirty) {
Update_Tangents();
}
csave.Begin_Chunk(HERMITE1D_CHUNK_CURVE1D);
Curve1DClass::Save(csave);
csave.End_Chunk();
csave.Begin_Chunk(HERMITE1D_CHUNK_TANGENTS);
for (int i=0; i<Tangents.Count(); i++) {
csave.Write(&(Tangents[i].InTangent),sizeof(Tangents[i].InTangent));
csave.Write(&(Tangents[i].OutTangent),sizeof(Tangents[i].OutTangent));
}
csave.End_Chunk();
return true;
}
bool HermiteSpline1DClass::Load(ChunkLoadClass &cload)
{
int i;
TangentsClass newtangent;
// reset the tangents array
Tangents.Delete_All();
// read in the chunks
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID())
{
case HERMITE1D_CHUNK_CURVE1D:
Curve1DClass::Load(cload);
break;
case HERMITE1D_CHUNK_TANGENTS:
for (i=0; i<Keys.Count(); i++) {
cload.Read(&(newtangent.InTangent),sizeof(newtangent.InTangent));
cload.Read(&(newtangent.OutTangent),sizeof(newtangent.OutTangent));
Tangents.Add(newtangent);
}
TangentsDirty = false;
break;
default:
WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",__FILE__,__LINE__));
break;
}
cload.Close_Chunk();
}
WWASSERT(Keys.Count() == Tangents.Count());
return true;
}

View File

@@ -0,0 +1,143 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /VSS_Sync/wwmath/hermitespline.h $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 6/13/01 2:18p $*
* *
* $Revision:: 7 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef HERMITE_H
#define HERMITE_H
#include "curve.h"
/*
** HermiteSpline3DClass
** 3-Dimensional hermite spline interpolation
** Hermite splines require you to input all of the tangents...
*/
class HermiteSpline3DClass : public Curve3DClass
{
public:
HermiteSpline3DClass(void)
: TangentsDirty (true) { }
HermiteSpline3DClass(const HermiteSpline3DClass &that)
: TangentsDirty (true) { (*this) = that; }
const HermiteSpline3DClass &operator= (const HermiteSpline3DClass &that);
virtual void Evaluate(float time,Vector3 * set_val);
virtual void Evaluate_Derivative(float time,Vector3 * set_val);
virtual void Set_Looping(bool onoff);
virtual void Set_Key(int i,const Vector3 & point);
virtual int Add_Key(const Vector3 & point,float t);
virtual void Remove_Key(int i);
virtual void Clear_Keys(void);
virtual void Set_Tangents(int i,const Vector3 & in_tan,const Vector3 & out_tan);
virtual void Get_Tangents(int i,Vector3 * set_in,Vector3 * set_out);
virtual void Update_Tangents(void) { TangentsDirty = false; }
// save-load support
virtual const PersistFactoryClass & Get_Factory(void) const;
virtual bool Save(ChunkSaveClass &csave);
virtual bool Load(ChunkLoadClass &cload);
protected:
class TangentsClass
{
public:
Vector3 InTangent;
Vector3 OutTangent;
bool operator == (const TangentsClass & that) { return ((InTangent == that.InTangent) && (OutTangent == that.OutTangent)); }
bool operator != (const TangentsClass & that) { return !TangentsClass::operator == (that); }
};
bool TangentsDirty;
DynamicVectorClass<TangentsClass> Tangents;
};
/*
** HermiteSpline1DClass
** 1-Dimensional hermite spline interpolation
** Hermite splines require you to input all of the tangents...
*/
class HermiteSpline1DClass : public Curve1DClass
{
public:
HermiteSpline1DClass (void)
: TangentsDirty (true) { }
virtual void Evaluate(float time,float * set_val);
virtual void Set_Looping(bool onoff);
virtual void Set_Key(int i,float point,unsigned int extra=0);
virtual int Add_Key(float point,float t,unsigned int extra=0);
virtual void Remove_Key(int i);
virtual void Clear_Keys(void);
virtual void Set_Tangents(int i,float in_tan,float out_tan);
virtual void Get_Tangents(int i,float * set_in,float * set_out);
// save-load support
virtual const PersistFactoryClass & Get_Factory(void) const;
virtual bool Save(ChunkSaveClass &csave);
virtual bool Load(ChunkLoadClass &cload);
protected:
class TangentsClass
{
public:
float InTangent;
float OutTangent;
bool operator == (const TangentsClass & that) { return ((InTangent == that.InTangent) && (OutTangent == that.OutTangent)); }
bool operator != (const TangentsClass & that) { return !TangentsClass::operator == (that); }
};
virtual void Update_Tangents(void) { TangentsDirty = false; }
bool TangentsDirty;
DynamicVectorClass<TangentsClass> Tangents;
};
#endif

View File

@@ -0,0 +1,231 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/lineseg.cpp $*
* *
* Author:: Greg_h *
* *
* $Modtime:: 3/16/00 3:16p $*
* *
* $Revision:: 25 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* LineSegClass::Set -- Initialize this 'lineseg' by transforming another 'lineseg' *
* LineSegClass::Set_Random -- create a random linesegment within the given space *
* LineSegClass::Find_Point_Closest_To -- Finds point on line closest to point supplied. *
* LineSegClass::Find_Intersection -- Finds the closest points on the two lines *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "lineseg.h"
//#include <stdlib.h>
#include "matrix3d.h"
/***********************************************************************************************
* LineSegClass::Set -- Initialize this 'lineseg' by transforming another 'lineseg' *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 6/17/98 GTH : Created. *
*=============================================================================================*/
void LineSegClass::Set(const LineSegClass & that,const Matrix3D & tm)
{
/*
** Transform P0 and P1
*/
Matrix3D::Transform_Vector(tm,that.P0,&P0);
Matrix3D::Transform_Vector(tm,that.P1,&P1);
/*
** Just calculate DP
*/
DP = P1 - P0;
/*
** Rotate the direction vector
*/
Matrix3D::Rotate_Vector(tm,that.Dir,&Dir);
/*
** Length should be un-changed
*/
Length = that.Length;
}
/***********************************************************************************************
* LineSegClass::Set_Random -- create a random linesegment within the given space *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/21/98 GTH : Created. *
*=============================================================================================*/
void LineSegClass::Set_Random(const Vector3 & min,const Vector3 & max)
{
float frac;
frac = WWMath::Random_Float();
P0.X = min.X + frac * (max.X - min.X);
frac = WWMath::Random_Float();
P0.Y = min.Y + frac * (max.Y - min.Y);
frac = WWMath::Random_Float();
P0.Z = min.Z + frac * (max.Z - min.Z);
frac = WWMath::Random_Float();
P1.X = min.X + frac * (max.X - min.X);
frac = WWMath::Random_Float();
P1.Y = min.Y + frac * (max.Y - min.Y);
frac = WWMath::Random_Float();
P1.Z = min.Z + frac * (max.Z - min.Z);
DP = P1 - P0;
Dir = DP;
Dir.Normalize();
Length = DP.Length();
}
/***********************************************************************************************
* LineSegClass::Find_Point_Closest_To -- Finds point on line closest to point supplied. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 07/26/1999 SKB : Created. *
*=============================================================================================*/
Vector3 LineSegClass::Find_Point_Closest_To(const Vector3 &pos) const
{
// Get a vector from one line endpoint to point in question.
Vector3 v_0_pos = (pos - P0);
float dotprod = Vector3::Dot_Product(Dir, v_0_pos);
// Check to see if point is past either of the endpoints.
// (Unable to draw a perpendicular line from the point to the line segment.)
if (dotprod <= 0.0) {
return(P0);
} else if (dotprod >= Length) {
return(P1);
}
// Find point on line seg that is closest to pos passed in.
Vector3 point = P0 + (dotprod * Dir);
return(point);
}
/***********************************************************************************************
* LineSegClass::Find_Intersection -- Finds the closest points on the two lines.. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 03/03/2000 PDS : Created. *
*=============================================================================================*/
bool
LineSegClass::Find_Intersection
(
const LineSegClass & other_line,
Vector3 * p1,
float * fraction1,
Vector3 * p2,
float * fraction2
) const
{
bool retval = false;
#ifdef ALLOW_TEMPORARIES
Vector3 cross1 = Vector3::Cross_Product (Dir, other_line.Dir);
Vector3 cross2 = Vector3::Cross_Product (other_line.P0 - P0, other_line.Dir);
float top1 = cross2 * cross1;
float bottom1 = cross1 * cross1;
Vector3 cross3 = Vector3::Cross_Product (other_line.Dir, Dir);
Vector3 cross4 = Vector3::Cross_Product (P0 - other_line.P0, Dir);
float top2 = cross4 * cross3;
float bottom2 = cross3 * cross3;
#else
Vector3 cross1, cross2, cross3, cross4;
Vector3::Cross_Product(Dir, other_line.Dir, &cross1);
Vector3::Cross_Product(other_line.P0 - P0, other_line.Dir, &cross2);
float top1 = Vector3::Dot_Product(cross2, cross1);
float bottom1 = Vector3::Dot_Product(cross1, cross1);
Vector3::Cross_Product(other_line.Dir, Dir, &cross3);
Vector3::Cross_Product(P0 - other_line.P0, Dir, &cross4);
float top2 = Vector3::Dot_Product(cross4, cross3);
float bottom2 = Vector3::Dot_Product(cross3, cross3);
#endif
//
// If either of the divisors are 0, then the lines are parallel
//
if (bottom1 != 0 && bottom2 != 0) {
float length1 = top1 / bottom1;
float length2 = top2 / bottom2;
//
// Calculate the closest points on both lines.
// Note: If the points are the same, then the lines intersect.
//
(*p1) = P0 + (Dir * length1);
(*p2) = other_line.P0 + (other_line.Dir * length2);
//
// Return the fractions if they caller wants them
//
if (fraction1 != NULL) {
(*fraction1) = length1 / Length;
}
if (fraction2 != NULL) {
(*fraction2) = length2 / Length;
}
retval = true;
}
return retval;
}

View File

@@ -0,0 +1,93 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/lineseg.h $*
* *
* Author:: Greg_h *
* *
* $Modtime:: 3/16/00 3:16p $*
* *
* $Revision:: 21 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef LINESEG_H
#define LINESEG_H
#include "always.h"
#include "vector3.h"
#include "castres.h"
class TriClass;
class AABoxClass;
class OBBoxClass;
class PlaneClass;
class SphereClass;
class Matrix3D;
class LineSegClass
{
public:
LineSegClass(void) { }
LineSegClass(const Vector3 & p0,const Vector3 & p1) : P0(p0), P1(p1) { recalculate(); }
LineSegClass(const LineSegClass & that,const Matrix3D & tm) { Set(that,tm); }
void Set(const Vector3 & p0,const Vector3 & p1) { P0 = p0; P1 = p1; recalculate(); }
void Set(const LineSegClass & that,const Matrix3D & tm);
void Set_Random(const Vector3 & min,const Vector3 & max);
const Vector3 & Get_P0() const { return P0; } // start point
const Vector3 & Get_P1() const { return P1; } // end point
const Vector3 & Get_DP() const { return DP; } // difference of the two points
const Vector3 & Get_Dir() const { return Dir; } // normalized direction.
float Get_Length() const { return Length; } // length of the segment
void Compute_Point(float t,Vector3 * set) const { Vector3::Add(P0,t*DP,set); }
Vector3 Find_Point_Closest_To(const Vector3 &pos) const;
bool Find_Intersection (const LineSegClass &other_line, Vector3 *p1, float *fraction1, Vector3 *p2, float *fraction2) const;
protected:
void recalculate(void) { DP = P1 - P0; Dir = DP; Dir.Normalize(); Length = DP.Length(); }
Vector3 P0; // start point
Vector3 P1; // end point
Vector3 DP; // difference of the two points
Vector3 Dir; // normalized direction.
float Length; // length of the segment
};
#endif

View File

@@ -0,0 +1,250 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWPhys *
* *
* $Archive:: /Commando/Code/wwmath/lookuptable.cpp $*
* *
* Original Author:: Greg Hjelstrom *
* *
* $Author:: Byon_g $*
* *
* $Modtime:: 7/23/01 6:18p $*
* *
* $Revision:: 5 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "lookuptable.h"
#include "curve.h"
#include "wwfile.h"
#include "ffactory.h"
#include "chunkio.h"
#include "persistfactory.h"
#include "vector2.h"
/*
** Static members
*/
RefMultiListClass<LookupTableClass> LookupTableMgrClass::Tables;
/*
** Save-Load stuff. Lookup tables are basically saved 1D curve classes. They are turned
** into tables at load time. For future expansion, the curve is wrapped in a chunk.
*/
enum
{
LOOKUPTABLE_CHUNK_CURVE = 03071200,
LOOKUPTABLE_CHUNK_EXTENTS,
};
/***********************************************************************************************
**
** LookupTableClass Implementation
**
***********************************************************************************************/
LookupTableClass::LookupTableClass(int sample_count) :
MinInputValue(0.0f),
MaxInputValue(0.0f),
OutputSamples(sample_count)
{
}
LookupTableClass::~LookupTableClass(void)
{
}
void LookupTableClass::Init(const char * name,Curve1DClass * curve)
{
// copy the name
Name = name;
// Store the min and max input values for the table
curve->Get_Key(0,NULL,&MinInputValue,NULL);
curve->Get_Key(curve->Key_Count()-1,NULL,&MaxInputValue,NULL);
OOMaxMinusMin = 1.0f / (MaxInputValue - MinInputValue);
// Sample the curve and store the output values
for (int i=0; i<OutputSamples.Length(); i++) {
float x = MinInputValue + (MaxInputValue - MinInputValue) * (float)i / (float)(OutputSamples.Length() - 1);
float y;
curve->Evaluate(x,&y);
OutputSamples[i] = y;
}
}
/***********************************************************************************************
**
** LookupTableManager Implementation
**
***********************************************************************************************/
void LookupTableMgrClass::Init(void)
{
// create a default table that the user can use in an emergency
LookupTableClass * default_table = NEW_REF(LookupTableClass,(2));
LinearCurve1DClass * default_curve = W3DNEW LinearCurve1DClass;
default_curve->Add_Key(0.5f,0.0f);
default_curve->Add_Key(0.5f,1.0f);
default_table->Init("DefaultTable",default_curve);
Add_Table(default_table);
delete default_curve;
default_table->Release_Ref();
}
void LookupTableMgrClass::Shutdown(void)
{
Reset();
}
void LookupTableMgrClass::Reset(void)
{
while (Tables.Peek_Head() != NULL) {
Tables.Release_Head();
}
}
bool LookupTableMgrClass::Add_Table(LookupTableClass * table)
{
return Tables.Add(table);
}
bool LookupTableMgrClass::Remove_Table(LookupTableClass * table)
{
return Tables.Remove(table);
}
LookupTableClass * LookupTableMgrClass::Get_Table(const char * name,bool try_to_load)
{
// check if we already have this table loaded...
RefMultiListIterator<LookupTableClass> it(&Tables);
for (it.First(); !it.Is_Done(); it.Next()) {
if (stricmp(it.Peek_Obj()->Get_Name(),name) == 0) {
return it.Get_Obj(); // add a reference for the user...
}
}
// otherwise we can try to load it.
LookupTableClass * new_table = NULL;
if (try_to_load) {
FileClass * file = _TheFileFactory->Get_File(name);
if (file && file->Open()) {
ChunkLoadClass cload(file);
Curve1DClass * curve = NULL;
Load_Table_Desc(cload,&curve);
if (curve != NULL) {
new_table = NEW_REF(LookupTableClass,());
new_table->Init(name,curve);
Add_Table(new_table);
delete curve;
}
}
_TheFileFactory->Return_File(file);
}
return new_table; // constructor ref is returned to user.
}
void LookupTableMgrClass::Save_Table_Desc
(
ChunkSaveClass & csave,
Curve1DClass * curve,
const Vector2 & min_corner,
const Vector2 & max_corner
)
{
// save the curve
csave.Begin_Chunk(LOOKUPTABLE_CHUNK_CURVE);
csave.Begin_Chunk(curve->Get_Factory().Chunk_ID());
curve->Get_Factory().Save(csave,curve);
csave.End_Chunk();
csave.End_Chunk();
// save the extents
csave.Begin_Chunk(LOOKUPTABLE_CHUNK_EXTENTS);
csave.Write(&(min_corner.X),sizeof(float));
csave.Write(&(min_corner.Y),sizeof(float));
csave.Write(&(max_corner.X),sizeof(float));
csave.Write(&(max_corner.Y),sizeof(float));
csave.End_Chunk();
}
void LookupTableMgrClass::Load_Table_Desc
(
ChunkLoadClass & cload,
Curve1DClass ** curve_ptr,
Vector2 * set_min_corner,
Vector2 * set_max_corner
)
{
*curve_ptr = NULL;
PersistFactoryClass * factory;
float xmin,xmax;
float ymin,ymax;
xmin = xmax = ymin = ymax = 0.0f;
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID()) {
case LOOKUPTABLE_CHUNK_CURVE:
cload.Open_Chunk();
factory = SaveLoadSystemClass::Find_Persist_Factory(cload.Cur_Chunk_ID());
WWASSERT(factory != NULL);
if (factory != NULL) {
*curve_ptr = (Curve1DClass *)factory->Load(cload);
}
cload.Close_Chunk();
break;
case LOOKUPTABLE_CHUNK_EXTENTS:
cload.Read(&xmin,sizeof(xmin));
cload.Read(&ymin,sizeof(ymin));
cload.Read(&xmax,sizeof(xmax));
cload.Read(&ymax,sizeof(ymax));
break;
default:
WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",__FILE__,__LINE__));
break;
}
cload.Close_Chunk();
}
if (set_min_corner != NULL) {
set_min_corner->Set(xmin,ymin);
}
if (set_max_corner != NULL) {
set_max_corner->Set(xmax,ymax);
}
}

View File

@@ -0,0 +1,161 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWPhys *
* *
* $Archive:: /Commando/Code/wwmath/lookuptable.h $*
* *
* Original Author:: Greg Hjelstrom *
* *
* $Author:: Greg_h $*
* *
* $Modtime:: 4/18/00 1:20p $*
* *
* $Revision:: 6 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#ifndef LOOKUPTABLE_H
#define LOOKUPTABLE_H
#include "always.h"
#include "simplevec.h"
#include "wwstring.h"
#include "refcount.h"
#include "multilist.h"
#include "wwmath.h"
class Vector2;
class Curve1DClass;
class ChunkSaveClass;
class ChunkLoadClass;
/**
** LookupTableClass
** This class contains the tabulated values for a function.
*/
class LookupTableClass : public RefCountClass, public MultiListObjectClass
{
public:
LookupTableClass(int sample_count = 256);
virtual ~LookupTableClass(void);
void Init(const char * name,Curve1DClass * curve);
float Get_Value(float input);
float Get_Value_Quick(float input);
const char * Get_Name(void) { return Name; }
protected:
StringClass Name; // name of this table, if it came from a file, this is also the filename
float MinInputValue;
float MaxInputValue;
float OOMaxMinusMin;
SimpleVecClass<float> OutputSamples;
};
inline float LookupTableClass::Get_Value(float input)
{
if (input <= MinInputValue) {
return OutputSamples[0];
}
if (input >= MaxInputValue) {
return OutputSamples[OutputSamples.Length()-1];
}
float normalized_input = (float)(OutputSamples.Length()-1) * (input - MinInputValue) * OOMaxMinusMin;
float input0 = WWMath::Floor(normalized_input);
int index0 = WWMath::Float_To_Long(input0);
int index1 = index0+1;
float lerp = normalized_input - input0;
return OutputSamples[index0] + lerp * (OutputSamples[index1] - OutputSamples[index0]);
}
inline float LookupTableClass::Get_Value_Quick(float input)
{
if (input <= MinInputValue) {
return OutputSamples[0];
}
if (input >= MaxInputValue) {
return OutputSamples[OutputSamples.Length()-1];
}
int index = (OutputSamples.Length()-1) * WWMath::Float_To_Long((input - MinInputValue) * OOMaxMinusMin);
return OutputSamples[index];
}
/**
** LookupTableMgrClass
** This class tracks all of the LookupTableClass's that have been loaded or installed.
** LookupTables can be created using the "SimpleGraph" tool. It basically allows you
** to edit a curve which will be used to generate the table. These curves are stored
** in .TBL files.
**
** NOTE: Use the SimpleGraph program to create lookup tables! Then just ask for them
** by filename and it will load the table for you (unless it is already loaded).
**
** NOTE: I add a table called "DefaultTable" so that you can revert to that if
** your table isn't found.
*/
class LookupTableMgrClass
{
public:
LookupTableMgrClass(void);
~LookupTableMgrClass(void);
// init and shutdown are automatically called from WWMath::Init, WWMath::Shutdown...
static void Init(void);
static void Shutdown(void);
static bool Add_Table(LookupTableClass * table);
static bool Remove_Table(LookupTableClass * table);
static LookupTableClass * Get_Table(const char * name,bool try_to_load = true);
static void Save_Table_Desc( ChunkSaveClass & csave,
Curve1DClass * curve,
const Vector2 & min,
const Vector2 & max );
static void Load_Table_Desc( ChunkLoadClass & cload,
Curve1DClass ** curve_ptr,
Vector2 * set_min = NULL,
Vector2 * set_max = NULL );
static void Reset(void);
protected:
static RefMultiListClass<LookupTableClass> Tables;
};
#endif // LOOKUPTABLE_H

View File

@@ -0,0 +1,393 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/matrix3.cpp $*
* *
* Org Author:: Greg_h *
* *
* Author : Kenny Mitchell *
* *
* $Modtime:: 06/26/02 4:04p $*
* *
* $Revision:: 17 $*
* *
* 06/26/02 KM Matrix name change to avoid MAX conflicts *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "matrix3.h"
#include "matrix3d.h"
#include "matrix4.h"
#include "quat.h"
/*
** Some pre-initialized Matrix3x3's
*/
const Matrix3x3 Matrix3x3::Identity
(
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0
);
const Matrix3x3 Matrix3x3::RotateX90
(
1.0, 0.0, 0.0,
0.0, 0.0, -1.0,
0.0, 1.0, 0.0
);
const Matrix3x3 Matrix3x3::RotateX180
(
1.0, 0.0, 0.0,
0.0, -1.0, 0.0,
0.0, 0.0, -1.0
);
const Matrix3x3 Matrix3x3::RotateX270
(
1.0, 0.0, 0.0,
0.0, 0.0, 1.0,
0.0, -1.0, 0.0
);
const Matrix3x3 Matrix3x3::RotateY90
(
0.0, 0.0, 1.0,
0.0, 1.0, 0.0,
-1.0, 0.0, 0.0
);
const Matrix3x3 Matrix3x3::RotateY180
(
-1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, -1.0
);
const Matrix3x3 Matrix3x3::RotateY270
(
0.0, 0.0, -1.0,
0.0, 1.0, 0.0,
1.0, 0.0, 0.0
);
const Matrix3x3 Matrix3x3::RotateZ90
(
0.0, -1.0, 0.0,
1.0, 0.0, 0.0,
0.0, 0.0, 1.0
);
const Matrix3x3 Matrix3x3::RotateZ180
(
-1.0, 0.0, 0.0,
0.0, -1.0, 0.0,
0.0, 0.0, 1.0
);
const Matrix3x3 Matrix3x3::RotateZ270
(
0.0, 1.0, 0.0,
-1.0, 0.0, 0.0,
0.0, 0.0, 1.0
);
/***********************************************************************************************
* Matrix3x3::Matrix3x3 -- Convert a Matrix3D (fake 4x4) to a Matrix3x3 *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/02/1997 GH : Created. *
*=============================================================================================*/
Matrix3x3::Matrix3x3(const Matrix3D & m)
{
Row[0].Set(m[0][0],m[0][1],m[0][2]);
Row[1].Set(m[1][0],m[1][1],m[1][2]);
Row[2].Set(m[2][0],m[2][1],m[2][2]);
}
Matrix3x3::Matrix3x3(const Matrix4x4 & m)
{
Row[0].Set(m[0][0],m[0][1],m[0][2]);
Row[1].Set(m[1][0],m[1][1],m[1][2]);
Row[2].Set(m[2][0],m[2][1],m[2][2]);
}
void Matrix3x3::Set(const Matrix3D & m)
{
Row[0].Set(m[0][0],m[0][1],m[0][2]);
Row[1].Set(m[1][0],m[1][1],m[1][2]);
Row[2].Set(m[2][0],m[2][1],m[2][2]);
}
void Matrix3x3::Set(const Matrix4x4 & m)
{
Row[0].Set(m[0][0],m[0][1],m[0][2]);
Row[1].Set(m[1][0],m[1][1],m[1][2]);
Row[2].Set(m[2][0],m[2][1],m[2][2]);
}
void Matrix3x3::Set(const Quaternion & q)
{
Row[0][0] = (float)(1.0 - 2.0 * (q[1] * q[1] + q[2] * q[2]));
Row[0][1] = (float)(2.0 * (q[0] * q[1] - q[2] * q[3]));
Row[0][2] = (float)(2.0 * (q[2] * q[0] + q[1] * q[3]));
Row[1][0] = (float)(2.0 * (q[0] * q[1] + q[2] * q[3]));
Row[1][1] = (float)(1.0 - 2.0f * (q[2] * q[2] + q[0] * q[0]));
Row[1][2] = (float)(2.0 * (q[1] * q[2] - q[0] * q[3]));
Row[2][0] = (float)(2.0 * (q[2] * q[0] - q[1] * q[3]));
Row[2][1] = (float)(2.0 * (q[1] * q[2] + q[0] * q[3]));
Row[2][2] =(float)(1.0 - 2.0 * (q[1] * q[1] + q[0] * q[0]));
}
Matrix3x3 & Matrix3x3::operator = (const Matrix3D & m)
{
Row[0].Set(m[0][0],m[0][1],m[0][2]);
Row[1].Set(m[1][0],m[1][1],m[1][2]);
Row[2].Set(m[2][0],m[2][1],m[2][2]);
return *this;
}
Matrix3x3 & Matrix3x3::operator = (const Matrix4x4 & m)
{
Row[0].Set(m[0][0],m[0][1],m[0][2]);
Row[1].Set(m[1][0],m[1][1],m[1][2]);
Row[2].Set(m[2][0],m[2][1],m[2][2]);
return *this;
}
void Matrix3x3::Multiply(const Matrix3D & a, const Matrix3x3 & b,Matrix3x3 * res)
{
#define ROWCOL(i,j) a[i][0]*b[0][j] + a[i][1]*b[1][j] + a[i][2]*b[2][j]
(*res)[0][0] = ROWCOL(0,0);
(*res)[0][1] = ROWCOL(0,1);
(*res)[0][2] = ROWCOL(0,2);
(*res)[1][0] = ROWCOL(1,0);
(*res)[1][1] = ROWCOL(1,1);
(*res)[1][2] = ROWCOL(1,2);
(*res)[2][0] = ROWCOL(2,0);
(*res)[2][1] = ROWCOL(2,1);
(*res)[2][2] = ROWCOL(2,2);
#undef ROWCOL
}
void Matrix3x3::Multiply(const Matrix3x3 & a, const Matrix3D & b,Matrix3x3 * res)
{
#define ROWCOL(i,j) a[i][0]*b[0][j] + a[i][1]*b[1][j] + a[i][2]*b[2][j]
(*res)[0][0] = ROWCOL(0,0);
(*res)[0][1] = ROWCOL(0,1);
(*res)[0][2] = ROWCOL(0,2);
(*res)[1][0] = ROWCOL(1,0);
(*res)[1][1] = ROWCOL(1,1);
(*res)[1][2] = ROWCOL(1,2);
(*res)[2][0] = ROWCOL(2,0);
(*res)[2][1] = ROWCOL(2,1);
(*res)[2][2] = ROWCOL(2,2);
#undef ROWCOL
}
Matrix3x3 operator * (const Matrix3D & a, const Matrix3x3 & b)
{
#define ROWCOL(i,j) a[i][0]*b[0][j] + a[i][1]*b[1][j] + a[i][2]*b[2][j]
return Matrix3x3(
Vector3(ROWCOL(0,0), ROWCOL(0,1), ROWCOL(0,2) ),
Vector3(ROWCOL(1,0), ROWCOL(1,1), ROWCOL(1,2) ),
Vector3(ROWCOL(2,0), ROWCOL(2,1), ROWCOL(2,2) )
);
#undef ROWCOL
}
Matrix3x3 operator * (const Matrix3x3 & a, const Matrix3D & b)
{
#define ROWCOL(i,j) a[i][0]*b[0][j] + a[i][1]*b[1][j] + a[i][2]*b[2][j]
return Matrix3x3(
Vector3(ROWCOL(0,0), ROWCOL(0,1), ROWCOL(0,2) ),
Vector3(ROWCOL(1,0), ROWCOL(1,1), ROWCOL(1,2) ),
Vector3(ROWCOL(2,0), ROWCOL(2,1), ROWCOL(2,2) )
);
#undef ROWCOL
}
#if 0
void Matrix3x3::Compute_Jacobi_Rotation(int i,int j,Matrix3x3 * r,Matrix3x3 * rinv)
{
}
void Matrix3x3::Symmetric_Eigen_Solve(void)
{
Matrix3x3 eigen_vals = *this;
Matrix3x3 eigen_vecs(1);
Matrix3x3 jr,jrinv;
while (!done) {
eigen_vals.Compute_Jacobi_Rotation(i,j,&jr,&jrinv);
eigen_vals = jr * (eigenvals) * jrinv;
eigen_vecs = eigen_vecs * jr;
}
/*
** Done! Eigen values are the diagonals of
** the eigen_vals matrix and the eigen vectors
** are the columns of the eigen_vecs matrix
*/
}
#endif
void Matrix3x3::Multiply(const Matrix3x3 & A,const Matrix3x3 & B,Matrix3x3 * set_res)
{
Matrix3x3 tmp;
Matrix3x3 * Aptr;
float tmp1,tmp2,tmp3;
// Check for aliased parameters, copy the 'A' matrix into a temporary if the
// result is going into 'A'. (in this case, this function is no better than
// the overloaded C++ operator...)
if (set_res == &A) {
tmp = A;
Aptr = &tmp;
} else {
Aptr = (Matrix3x3 *)&A;
}
tmp1 = B[0][0];
tmp2 = B[1][0];
tmp3 = B[2][0];
(*set_res)[0][0] = (float)((*Aptr)[0][0]*tmp1 + (*Aptr)[0][1]*tmp2 + (*Aptr)[0][2]*tmp3);
(*set_res)[1][0] = (float)((*Aptr)[1][0]*tmp1 + (*Aptr)[1][1]*tmp2 + (*Aptr)[1][2]*tmp3);
(*set_res)[2][0] = (float)((*Aptr)[2][0]*tmp1 + (*Aptr)[2][1]*tmp2 + (*Aptr)[2][2]*tmp3);
tmp1 = B[0][1];
tmp2 = B[1][1];
tmp3 = B[2][1];
(*set_res)[0][1] = (float)((*Aptr)[0][0]*tmp1 + (*Aptr)[0][1]*tmp2 + (*Aptr)[0][2]*tmp3);
(*set_res)[1][1] = (float)((*Aptr)[1][0]*tmp1 + (*Aptr)[1][1]*tmp2 + (*Aptr)[1][2]*tmp3);
(*set_res)[2][1] = (float)((*Aptr)[2][0]*tmp1 + (*Aptr)[2][1]*tmp2 + (*Aptr)[2][2]*tmp3);
tmp1 = B[0][2];
tmp2 = B[1][2];
tmp3 = B[2][2];
(*set_res)[0][2] = (float)((*Aptr)[0][0]*tmp1 + (*Aptr)[0][1]*tmp2 + (*Aptr)[0][2]*tmp3);
(*set_res)[1][2] = (float)((*Aptr)[1][0]*tmp1 + (*Aptr)[1][1]*tmp2 + (*Aptr)[1][2]*tmp3);
(*set_res)[2][2] = (float)((*Aptr)[2][0]*tmp1 + (*Aptr)[2][1]*tmp2 + (*Aptr)[2][2]*tmp3);
}
int Matrix3x3::Is_Orthogonal(void) const
{
Vector3 x(Row[0].X,Row[0].Y,Row[0].Z);
Vector3 y(Row[1].X,Row[1].Y,Row[1].Z);
Vector3 z(Row[2].X,Row[2].Y,Row[2].Z);
if (Vector3::Dot_Product(x,y) > WWMATH_EPSILON) return 0;
if (Vector3::Dot_Product(y,z) > WWMATH_EPSILON) return 0;
if (Vector3::Dot_Product(z,x) > WWMATH_EPSILON) return 0;
if (WWMath::Fabs(x.Length() - 1.0f) > WWMATH_EPSILON) return 0;
if (WWMath::Fabs(y.Length() - 1.0f) > WWMATH_EPSILON) return 0;
if (WWMath::Fabs(z.Length() - 1.0f) > WWMATH_EPSILON) return 0;
return 1;
}
void Matrix3x3::Re_Orthogonalize(void)
{
Vector3 x(Row[0][0],Row[0][1],Row[0][2]);
Vector3 y(Row[1][0],Row[1][1],Row[1][2]);
Vector3 z;
Vector3::Cross_Product(x,y,&z);
Vector3::Cross_Product(z,x,&y);
float len = x.Length();
if (len < WWMATH_EPSILON) {
Make_Identity();
return;
} else {
x /= len;
}
len = y.Length();
if (len < WWMATH_EPSILON) {
Make_Identity();
return;
} else {
y /= len;
}
len = z.Length();
if (len < WWMATH_EPSILON) {
Make_Identity();
return;
} else {
z /= len;
}
Row[0][0] = x.X;
Row[0][1] = x.Y;
Row[0][2] = x.Z;
Row[1][0] = y.X;
Row[1][1] = y.Y;
Row[1][2] = y.Z;
Row[2][0] = z.X;
Row[2][1] = z.Y;
Row[2][2] = z.Z;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,197 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/matrix4.cpp $*
* *
* Org Author:: Greg_h *
* *
* Author : Kenny_m *
* *
* $Modtime:: 06/26/02 4:04p $*
* *
* $Revision:: 6 $*
* *
* 06/26/02 KM Matrix name change to avoid MAX conflicts *
*---------------------------------------------------------------------------------------------*
* Functions: *
* Matrix4x4::Multiply -- Multiply two Matrix4x4's together *
* Matrix4x4::Multiply -- Multiply a Matrix3D * Matrix4x4 *
* Matrix4x4::Multiply -- Multiply a Matrix4x4 * Matrix3D *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "matrix4.h"
#include <assert.h>
/***********************************************************************************************
* Matrix4x4::Multiply -- Multiply two Matrix4x4's together *
* *
* INPUT: *
* a - first operand *
* b - second operand *
* res - pointer to matrix to store the result in (must not point to a or b) *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/13/99 gth : Created. *
*=============================================================================================*/
void Matrix4x4::Multiply(const Matrix4x4 &a,const Matrix4x4 &b,Matrix4x4 * res)
{
assert(res != &a);
assert(res != &b);
#define ROWCOL(i,j) a[i][0]*b[0][j] + a[i][1]*b[1][j] + a[i][2]*b[2][j] + a[i][3]*b[3][j]
(*res)[0][0] = ROWCOL(0,0);
(*res)[0][1] = ROWCOL(0,1);
(*res)[0][2] = ROWCOL(0,2);
(*res)[0][3] = ROWCOL(0,3);
(*res)[1][0] = ROWCOL(1,0);
(*res)[1][1] = ROWCOL(1,1);
(*res)[1][2] = ROWCOL(1,2);
(*res)[1][3] = ROWCOL(1,3);
(*res)[2][0] = ROWCOL(2,0);
(*res)[2][1] = ROWCOL(2,1);
(*res)[2][2] = ROWCOL(2,2);
(*res)[2][3] = ROWCOL(2,3);
(*res)[3][0] = ROWCOL(3,0);
(*res)[3][1] = ROWCOL(3,1);
(*res)[3][2] = ROWCOL(3,2);
(*res)[3][3] = ROWCOL(3,3);
#undef ROWCOL
}
/***********************************************************************************************
* Matrix4x4::Multiply -- Multiply a Matrix3D * Matrix4x4 *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/13/99 gth : Created. *
*=============================================================================================*/
void Matrix4x4::Multiply(const Matrix3D &a,const Matrix4x4 &b,Matrix4x4 * res)
{
assert(res != &b);
#define ROWCOL(i,j) a[i][0]*b[0][j] + a[i][1]*b[1][j] + a[i][2]*b[2][j] + a[i][3]*b[3][j]
(*res)[0][0] = ROWCOL(0,0);
(*res)[0][1] = ROWCOL(0,1);
(*res)[0][2] = ROWCOL(0,2);
(*res)[0][3] = ROWCOL(0,3);
(*res)[1][0] = ROWCOL(1,0);
(*res)[1][1] = ROWCOL(1,1);
(*res)[1][2] = ROWCOL(1,2);
(*res)[1][3] = ROWCOL(1,3);
(*res)[2][0] = ROWCOL(2,0);
(*res)[2][1] = ROWCOL(2,1);
(*res)[2][2] = ROWCOL(2,2);
(*res)[2][3] = ROWCOL(2,3);
(*res)[3][0] = b[3][0]; // last row of a is 0,0,0,1
(*res)[3][1] = b[3][1]; // this leaves the last row of b unchanged
(*res)[3][2] = b[3][2];
(*res)[3][3] = b[3][3];
#undef ROWCOL
}
/***********************************************************************************************
* Matrix4x4::Multiply -- Multiply a Matrix4x4 * Matrix3D *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*=============================================================================================*/
void Matrix4x4::Multiply(const Matrix4x4 & a,const Matrix3D & b,Matrix4x4 * res)
{
assert(res != &a);
// ROWCOL multiplies a row of 'a' by one of the first three columns of 'b' (4th entry in b is zero)
// ROWCOL4 multiplies a row of 'a' by the fourth column of 'b' (4th entry in b is one)
#define ROWCOL(i,j) a[i][0]*b[0][j] + a[i][1]*b[1][j] + a[i][2]*b[2][j]
#define ROWCOL4(i,j) a[i][0]*b[0][j] + a[i][1]*b[1][j] + a[i][2]*b[2][j] + a[i][3]
(*res)[0][0] = ROWCOL(0,0);
(*res)[0][1] = ROWCOL(0,1);
(*res)[0][2] = ROWCOL(0,2);
(*res)[0][3] = ROWCOL4(0,3);
(*res)[1][0] = ROWCOL(1,0);
(*res)[1][1] = ROWCOL(1,1);
(*res)[1][2] = ROWCOL(1,2);
(*res)[1][3] = ROWCOL4(1,3);
(*res)[2][0] = ROWCOL(2,0);
(*res)[2][1] = ROWCOL(2,1);
(*res)[2][2] = ROWCOL(2,2);
(*res)[2][3] = ROWCOL4(2,3);
(*res)[3][0] = ROWCOL(3,0);
(*res)[3][1] = ROWCOL(3,1);
(*res)[3][2] = ROWCOL(3,2);
(*res)[3][3] = ROWCOL4(3,3);
#undef ROWCOL
#undef ROWCOL4
}
int operator == (const Matrix4x4 & a, const Matrix4x4 & b)
{
unsigned* m1=(unsigned*)&a;
unsigned* m2=(unsigned*)&b;
unsigned res=0;
for (int i=0;i<4;++i) {
res|=*m1++^*m2++;
res|=*m1++^*m2++;
res|=*m1++^*m2++;
res|=*m1++^*m2++;
}
return !res;
}
int operator != (const Matrix4x4 & a, const Matrix4x4 & b)
{
return (!(a == b));
}

View File

@@ -0,0 +1,848 @@
/*
** Command & Conquer Generals Zero Hour(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/>.
*/
/* $Header: /Commando/Code/wwmath/matrix4.h 20 10/04/01 10:33a Greg_h $ */
/***********************************************************************************************
*** Confidential - Westwood Studios ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* File Name : MATRIX4.H *
* *
* Org Programmer : Greg Hjelstrom *
* *
* Author : Kenny Mitchell *
* *
* Start Date : 06/02/97 *
* *
* Last Update : June 6, 2002 [KM] *
* *
* 06/26/02 KM Matrix name change to avoid MAX conflicts *
*---------------------------------------------------------------------------------------------*
* Functions: *
* Matrix4x4::Matrix4x4 -- Constructor, optionally initialize to Identitiy matrix *
* Matrix4x4::Matrix4x4 -- Copy Constructor *
* Matrix4x4::Matrix4x4 -- Convert a Matrix3D (fake 4x4) to a Matrix4x4 *
* Matrix4x4::Matrix4x4 -- Constructor *
* Matrix4x4::Make_Identity -- Initializes the matrix to Identity *
* Matrix4x4::Init -- Initializes from the contents of the give Matrix3D *
* Matrix4x4::Init -- Initializes the rows from the given Vector4s *
* Matrix4x4::Init -- Initializes the rows from the given 16 floats *
* Matrix4x4::Init_Ortho -- Initialize to an orthographic projection matrix *
* Matrix4x4::Init_Perspective -- Initialize to a perspective projection matrix *
* Matrix4x4::Init_Perspective -- Initialize to a perspective projection matrix *
* Matrix4x4::Transpose -- Returns transpose of the matrix *
* Matrix4x4::Inverse -- returns the inverse of the matrix *
* Matrix4x4::operator = -- assignment operator *
* Matrix4x4::operator += -- "plus equals" operator *
* Matrix4x4::operator -= -- "minus equals" operator *
* Matrix4x4::operator *= -- "times equals" operator *
* Matrix4x4::operator /= -- "divide equals" operator *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef MATRIX4_H
#define MATRIX4_H
#include "always.h"
#include "vector4.h"
#include "matrix3d.h"
#include "matrix3.h"
class Matrix4x4
{
public:
/*
** Constructors
*/
Matrix4x4(void) {};
Matrix4x4(const Matrix4x4 & m);
WWINLINE explicit Matrix4x4(bool identity);
WWINLINE explicit Matrix4x4(const Matrix3D & m);
WWINLINE explicit Matrix4x4(const Matrix3x3 & m);
WWINLINE explicit Matrix4x4(const Vector4 & v0, const Vector4 & v1, const Vector4 & v2, const Vector4 & v3);
WWINLINE explicit Matrix4x4( float m11,float m12,float m13,float m14,
float m21,float m22,float m23,float m24,
float m31,float m32,float m33,float m34,
float m41,float m42,float m43,float m44 );
WWINLINE void Make_Identity(void);
WWINLINE void Init(const Matrix3D & m);
WWINLINE void Init(const Matrix3x3 & m);
WWINLINE void Init(const Vector4 & v0, const Vector4 & v1, const Vector4 & v2, const Vector4 & v3);
WWINLINE void Init( float m11,float m12,float m13,float m14,
float m21,float m22,float m23,float m24,
float m31,float m32,float m33,float m34,
float m41,float m42,float m43,float m44 );
/*
** Projection matrices. The znear and zfar parameters are positive values indicating the
** distance from the camera to the z clipping planes. See implementations for more info.
*/
WWINLINE void Init_Ortho(float left,float right,float bottom,float top,float znear,float zfar);
WWINLINE void Init_Perspective(float hfov,float vfov,float znear,float zfar);
WWINLINE void Init_Perspective(float left,float right,float bottom,float top,float znear,float zfar);
/*
** Access operators
*/
WWINLINE Vector4 & operator [] (int i) { return Row[i]; }
WWINLINE const Vector4 & operator [] (int i) const { return Row[i]; }
/*
** Transpose and Inverse
*/
WWINLINE Matrix4x4 Transpose(void) const;
WWINLINE Matrix4x4 Inverse(void) const;
/*
** Assignment operators
*/
WWINLINE Matrix4x4 & operator = (const Matrix4x4 & m);
WWINLINE Matrix4x4 & operator += (const Matrix4x4 & m);
WWINLINE Matrix4x4 & operator -= (const Matrix4x4 & m);
WWINLINE Matrix4x4 & operator *= (float d);
WWINLINE Matrix4x4 & operator /= (float d);
/*
** Negation
*/
WWINLINE friend Matrix4x4 operator - (const Matrix4x4& a);
/*
** Scalar multiplication and division
*/
WWINLINE friend Matrix4x4 operator * (const Matrix4x4& a,float d);
WWINLINE friend Matrix4x4 operator * (float d,const Matrix4x4& a);
WWINLINE friend Matrix4x4 operator / (const Matrix4x4& a,float d);
/*
** matrix addition
*/
WWINLINE friend Matrix4x4 operator + (const Matrix4x4& a, const Matrix4x4& b);
WWINLINE friend Matrix4x4 Add(const Matrix4x4& a);
/*
** matrix subtraction
*/
WWINLINE friend Matrix4x4 operator - (const Matrix4x4 & a, const Matrix4x4 & b);
WWINLINE friend Matrix4x4 Subtract(const Matrix4x4 & a, const Matrix4x4 & b);
/*
** matrix multiplication
*/
WWINLINE friend Matrix4x4 operator * (const Matrix4x4 & a, const Matrix4x4 & b);
WWINLINE friend Matrix4x4 Multiply(const Matrix4x4 & a, const Matrix4x4 & b);
WWINLINE friend Matrix4x4 operator * (const Matrix4x4 & a, const Matrix3D & b);
WWINLINE friend Matrix4x4 operator * (const Matrix3D & a, const Matrix4x4 & b);
/*
** Comparison operators
*/
friend int operator == (const Matrix4x4 & a, const Matrix4x4 & b);
friend int operator != (const Matrix4x4 & a, const Matrix4x4 & b);
/*
** Swap two matrices in place
*/
WWINLINE friend void Swap(Matrix4x4 & a,Matrix4x4 & b);
/*
** Linear Transforms
*/
WWINLINE friend Vector4 operator * (const Matrix4x4 & a, const Vector4 & v);
WWINLINE friend Vector4 operator * (const Matrix4x4 & a, const Vector3 & v);
/*
** Matrix multiplication without temporaries...
*/
static void Multiply(const Matrix4x4 &A,const Matrix4x4 &B,Matrix4x4 * set_result);
static void Multiply(const Matrix3D &A,const Matrix4x4 &B,Matrix4x4 * set_result);
static void Multiply(const Matrix4x4 &A,const Matrix3D &B,Matrix4x4 * set_result);
static WWINLINE void Transform_Vector(const Matrix4x4 & tm,const Vector3 & in,Vector3 * out);
static WWINLINE void Transform_Vector(const Matrix4x4 & tm,const Vector3 & in,Vector4 * out);
static WWINLINE void Transform_Vector(const Matrix4x4 & tm,const Vector4 & in,Vector4 * out);
protected:
Vector4 Row[4];
};
/***********************************************************************************************
* Matrix4x4::Matrix4x4 -- Constructor, optionally initialize to Identitiy matrix *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/02/1997 GH : Created. *
*=============================================================================================*/
WWINLINE Matrix4x4::Matrix4x4(bool identity)
{
if (identity) {
Make_Identity();
}
}
/***********************************************************************************************
* Matrix4x4::Matrix4x4 -- Copy Constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/02/1997 GH : Created. *
*=============================================================================================*/
WWINLINE Matrix4x4::Matrix4x4(const Matrix4x4 & m)
{
Row[0] = m.Row[0]; Row[1] = m.Row[1]; Row[2] = m.Row[2]; Row[3] = m.Row[3];
}
/***********************************************************************************************
* Matrix4x4::Matrix4x4 -- Convert a Matrix3D (fake 4x4) to a Matrix4x4 *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/02/1997 GH : Created. *
*=============================================================================================*/
WWINLINE Matrix4x4::Matrix4x4(const Matrix3D & m)
{
Init(m);
}
/***********************************************************************************************
* Matrix4x4::Matrix4x4 -- Constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/02/1997 GH : Created. *
*=============================================================================================*/
WWINLINE Matrix4x4::Matrix4x4(const Vector4 & r0, const Vector4 & r1, const Vector4 & r2, const Vector4 & r3)
{
Init(r0,r1,r2,r3);
}
/***********************************************************************************************
* Matrix4x4::Matrix4x4 -- Constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/06/2001 NH : Created. *
*=============================================================================================*/
WWINLINE Matrix4x4::Matrix4x4(float m11,float m12,float m13,float m14, float m21,float m22,float m23,float m24,
float m31,float m32,float m33,float m34, float m41,float m42,float m43,float m44 )
{
Init(m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44);
}
/***********************************************************************************************
* Matrix4x4::Make_Identity -- Initializes the matrix to Identity *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/5/99 gth : Created. *
*=============================================================================================*/
WWINLINE void Matrix4x4::Make_Identity(void)
{
Row[0].Set(1.0,0.0,0.0,0.0);
Row[1].Set(0.0,1.0,0.0,0.0);
Row[2].Set(0.0,0.0,1.0,0.0);
Row[3].Set(0.0,0.0,0.0,1.0);
}
/***********************************************************************************************
* Matrix4x4::Init -- Initializes from the contents of the give Matrix3D *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/5/99 gth : Created. *
*=============================================================================================*/
WWINLINE void Matrix4x4::Init(const Matrix3D & m)
{
Row[0] = m[0]; Row[1] = m[1]; Row[2] = m[2]; Row[3] = Vector4(0.0,0.0,0.0,1.0);
}
/***********************************************************************************************
* Matrix4x4::Init -- Initializes the rows from the given Vector4s *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/5/99 gth : Created. *
*=============================================================================================*/
WWINLINE void Matrix4x4::Init(const Vector4 & r0, const Vector4 & r1, const Vector4 & r2, const Vector4 & r3)
{
Row[0] = r0; Row[1] = r1; Row[2] = r2; Row[3] = r3;
}
/***********************************************************************************************
* Matrix4x4::Init -- Initializes the rows from the given 16 floats *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/6/01 NH : Created. *
*=============================================================================================*/
WWINLINE void Matrix4x4::Init(float m11,float m12,float m13,float m14, float m21,float m22,float m23,float m24,
float m31,float m32,float m33,float m34, float m41,float m42,float m43,float m44)
{
Row[0].Set(m11,m12,m13,m14);
Row[1].Set(m21,m22,m23,m24);
Row[2].Set(m31,m32,m33,m34);
Row[3].Set(m41,m42,m43,m44);
}
/***********************************************************************************************
* Matrix4x4::Init_Ortho -- Initialize to an orthographic projection matrix *
* *
* You can find the formulas for this in the appendix of the OpenGL programming guide. Also *
* this happens to be the same convention used by Surrender. *
* *
* The result of this projection will be that points inside the volume will have all coords *
* between -1 and +1. A point at znear will project to z=-1. A point at zfar will project *
* to z=+1... *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* Note that the znear and zfar parameters are positive distances to the clipping planes *
* even though in the camera coordinate system, the clipping planes are at negative z *
* coordinates. This holds for all of the projection initializations and is consistent *
* with OpenGL's convention. *
* *
* HISTORY: *
* 11/5/99 gth : Created. *
*=============================================================================================*/
WWINLINE void Matrix4x4::Init_Ortho
(
float left,
float right,
float bottom,
float top,
float znear,
float zfar
)
{
assert(znear >= 0.0f);
assert(zfar > znear);
Make_Identity();
Row[0][0] = 2.0f / (right - left);
Row[0][3] = -(right + left) / (right - left);
Row[1][1] = 2.0f / (top - bottom);
Row[1][3] = -(top + bottom) / (top - bottom);
Row[2][2] = -2.0f / (zfar - znear);
Row[2][3] = -(zfar + znear) / (zfar - znear);
}
/***********************************************************************************************
* Matrix4x4::Init_Perspective -- Initialize to a perspective projection matrix *
* *
* You can find the formulas for this matrix in the appendix of the OpenGL programming guide. *
* Also, this happens to be the same convention used by Surrender. *
* *
* The result of this projection will be that points inside the volume will have all coords *
* between -1 and +1. A point at znear will project to z=-1. A point at zfar will project *
* to z=+1... *
* *
* INPUT: *
* hfov - horizontal field of view (in radians) *
* vfov - vertical field of view (in radians) *
* znear - distance to near z clipping plane (positive) *
* zfar - distance to the far z clipping plane (positive) *
* *
* OUTPUT: *
* *
* WARNINGS: *
* Note that the znear and zfar parameters are positive distances to the clipping planes *
* even though in the camera coordinate system, the clipping planes are at negative z *
* coordinates. This holds for all of the projection initializations and is consistent *
* with OpenGL's convention. *
* *
* HISTORY: *
* 11/5/99 gth : Created. *
*=============================================================================================*/
WWINLINE void Matrix4x4::Init_Perspective(float hfov,float vfov,float znear,float zfar)
{
assert(znear > 0.0f);
assert(zfar > znear);
Make_Identity();
Row[0][0] = static_cast<float>(1.0 / tan(hfov*0.5));
Row[1][1] = static_cast<float>(1.0 / tan(vfov*0.5));
Row[2][2] = -(zfar + znear) / (zfar - znear);
Row[2][3] = static_cast<float>(-(2.0*zfar*znear) / (zfar - znear));
Row[3][2] = -1.0f;
Row[3][3] = 0.0f;
}
/***********************************************************************************************
* Matrix4x4::Init_Perspective -- Initialize to a perspective projection matrix *
* *
* You can find the formulas for this matrix in the appendix of the OpenGL programming guide. *
* Also, this happens to be the same convention used by Surrender. *
* *
* The result of this projection will be that points inside the volume will have all coords *
* between -1 and +1. A point at znear will project to z=-1. A point at zfar will project *
* to z=+1... *
* *
* INPUT: *
* *
* left - min x coordinate of near clip plane *
* right - max x coordinate of near clip plane *
* bottom - min y coordinate of near clip plane *
* top - max y coordinate of near clip plane *
* znear - distance to near Z clipping plane *
* zfar - distance to far Z clipping plane *
* *
* OUTPUT: *
* *
* WARNINGS: *
* Note that the znear and zfar parameters are positive distances to the clipping planes *
* even though in the camera coordinate system, the clipping planes are at negative z *
* coordinates. This holds for all of the projection initializations and is consistent *
* with OpenGL's convention. *
* *
* HISTORY: *
* 11/5/99 gth : Created. *
*=============================================================================================*/
WWINLINE void Matrix4x4::Init_Perspective
(
float left,
float right,
float bottom,
float top,
float znear,
float zfar
)
{
assert(znear > 0.0f);
assert(zfar > 0.0f);
Make_Identity();
Row[0][0] = static_cast<float>(2.0*znear / (right - left));
Row[0][2] = (right + left) / (right - left);
Row[1][1] = static_cast<float>(2.0*znear / (top - bottom));
Row[1][2] = (top + bottom) / (top - bottom);
Row[2][2] = -(zfar + znear) / (zfar - znear);
Row[2][3] = static_cast<float>(-(2.0*zfar*znear) / (zfar - znear));
Row[3][2] = -1.0f;
Row[3][3] = 0.0f;
}
/***********************************************************************************************
* Matrix4x4::Transpose -- Returns transpose of the matrix *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/02/1997 GH : Created. *
*=============================================================================================*/
WWINLINE Matrix4x4 Matrix4x4::Transpose() const
{
return Matrix4x4(
Vector4(Row[0][0], Row[1][0], Row[2][0], Row[3][0]),
Vector4(Row[0][1], Row[1][1], Row[2][1], Row[3][1]),
Vector4(Row[0][2], Row[1][2], Row[2][2], Row[3][2]),
Vector4(Row[0][3], Row[1][3], Row[2][3], Row[3][3])
);
}
/***********************************************************************************************
* Matrix4x4::Inverse -- returns the inverse of the matrix *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/02/1997 GH : Created. *
*=============================================================================================*/
WWINLINE Matrix4x4 Matrix4x4::Inverse() const // Gauss-Jordan elimination with partial pivoting
{
WWASSERT_PRINT(0,"Matrix4x4::Inverse does not work, re-implement!");
Matrix4x4 a(*this); // As a evolves from original mat into identity
Matrix4x4 b(true); // b evolves from identity into inverse(a)
int i, j, i1;
// Loop over cols of a from left to right, eliminating above and below diagonal
for (j=0; j<4; j++) {
// Find largest pivot in column j among rows j..3
i1 = j;
for (i=j+1; i<4; i++) {
if (WWMath::Fabs(a[i][j]) > WWMath::Fabs(a[i1][j])) {
i1 = i;
}
}
// Swap rows i1 and j in a and b to put pivot on diagonal
Swap(a.Row[i1], a.Row[j]);
Swap(b.Row[i1], b.Row[j]);
// Scale row j to have a unit diagonal
if (a[j][j]==0.) {
//ALGEBRA_ERROR("Matrix4x4::inverse: singular matrix; can't invert\n");
}
b.Row[j] /= a.Row[j][j];
a.Row[j] /= a.Row[j][j];
// Eliminate off-diagonal elems in col j of a, doing identical ops to b
for (i=0; i<4; i++) {
if (i != j) {
b.Row[i] -= a[i][j] * b.Row[j];
a.Row[i] -= a[i][j] * a.Row[j];
}
}
}
return b;
}
/***********************************************************************************************
* Matrix4x4::operator = -- assignment operator *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/02/1997 GH : Created. *
*=============================================================================================*/
WWINLINE Matrix4x4 & Matrix4x4::operator = (const Matrix4x4 & m)
{
Row[0] = m.Row[0]; Row[1] = m.Row[1]; Row[2] = m.Row[2]; Row[3] = m.Row[3];
return *this;
}
/***********************************************************************************************
* Matrix4x4::operator += -- "plus equals" operator *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/02/1997 GH : Created. *
*=============================================================================================*/
WWINLINE Matrix4x4& Matrix4x4::operator += (const Matrix4x4 & m)
{
Row[0] += m.Row[0]; Row[1] += m.Row[1]; Row[2] += m.Row[2]; Row[3] += m.Row[3];
return *this;
}
/***********************************************************************************************
* Matrix4x4::operator-= -- "minus equals" operator *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/02/1997 GH : Created. *
*=============================================================================================*/
WWINLINE Matrix4x4& Matrix4x4::operator -= (const Matrix4x4 & m)
{
Row[0] -= m.Row[0]; Row[1] -= m.Row[1]; Row[2] -= m.Row[2]; Row[3] -= m.Row[3];
return *this;
}
/***********************************************************************************************
* Matrix4x4::operator *= -- "times equals" operator *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/02/1997 GH : Created. *
*=============================================================================================*/
WWINLINE Matrix4x4& Matrix4x4::operator *= (float d)
{
Row[0] *= d; Row[1] *= d; Row[2] *= d; Row[3] *= d;
return *this;
}
/***********************************************************************************************
* Matrix4x4::operator /= -- "divide equals" operator *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/02/1997 GH : Created. *
*=============================================================================================*/
WWINLINE Matrix4x4& Matrix4x4::operator /= (float d)
{
float ood = d;
Row[0] *= ood; Row[1] *= ood; Row[2] *= ood; Row[3] *= ood;
return *this;
}
WWINLINE Matrix4x4 operator - (const Matrix4x4 & a)
{
return Matrix4x4(-a.Row[0], -a.Row[1], -a.Row[2], -a.Row[3]);
}
WWINLINE Matrix4x4 operator * (const Matrix4x4 & a, float d)
{
return Matrix4x4(a.Row[0] * d, a.Row[1] * d, a.Row[2] * d, a.Row[3] * d);
}
WWINLINE Matrix4x4 operator * (float d, const Matrix4x4 & a)
{
return a*d;
}
WWINLINE Matrix4x4 operator / (const Matrix4x4 & a, float d)
{
float ood = 1.0f / d;
return Matrix4x4(a.Row[0] * ood, a.Row[1] * ood, a.Row[2] * ood, a.Row[3] * ood);
}
/*
** matrix addition
*/
WWINLINE Matrix4x4 operator + (const Matrix4x4 & a, const Matrix4x4 & b)
{
return Matrix4x4(
a.Row[0] + b.Row[0],
a.Row[1] + b.Row[1],
a.Row[2] + b.Row[2],
a.Row[3] + b.Row[3]
);
}
WWINLINE Matrix4x4 Add(const Matrix4x4 & a, const Matrix4x4 & b)
{ return a+b; }
/*
** matrix subtraction
*/
WWINLINE Matrix4x4 operator - (const Matrix4x4 & a, const Matrix4x4 & b)
{
return Matrix4x4(
a.Row[0] - b.Row[0],
a.Row[1] - b.Row[1],
a.Row[2] - b.Row[2],
a.Row[3] - b.Row[3]
);
}
WWINLINE Matrix4x4 Subtract(const Matrix4x4 & a, const Matrix4x4 & b)
{ return a-b; }
/*
** matrix multiplication
*/
WWINLINE Matrix4x4 operator * (const Matrix4x4 & a, const Matrix4x4 & b)
{
#define ROWCOL(i, j) a[i][0]*b[0][j] + a[i][1]*b[1][j] + a[i][2]*b[2][j] + a[i][3]*b[3][j]
return Matrix4x4(
Vector4(ROWCOL(0,0), ROWCOL(0,1), ROWCOL(0,2), ROWCOL(0,3)),
Vector4(ROWCOL(1,0), ROWCOL(1,1), ROWCOL(1,2), ROWCOL(1,3)),
Vector4(ROWCOL(2,0), ROWCOL(2,1), ROWCOL(2,2), ROWCOL(2,3)),
Vector4(ROWCOL(3,0), ROWCOL(3,1), ROWCOL(3,2), ROWCOL(3,3))
);
#undef ROWCOL
}
WWINLINE Matrix4x4 Multiply(const Matrix4x4 & a, const Matrix4x4 & b)
{ return a*b; }
WWINLINE Matrix4x4 operator * (const Matrix4x4 & a, const Matrix3D & b)
{
// This function hand coded to handle the last row of b as 0,0,0,1
#define ROWCOL(i,j) a[i][0]*b[0][j] + a[i][1]*b[1][j] + a[i][2]*b[2][j]
#define ROWCOL_LAST(i,j) a[i][0]*b[0][j] + a[i][1]*b[1][j] + a[i][2]*b[2][j] + a[i][3]
return Matrix4x4(
Vector4(ROWCOL(0,0), ROWCOL(0,1), ROWCOL(0,2), ROWCOL_LAST(0,3)),
Vector4(ROWCOL(1,0), ROWCOL(1,1), ROWCOL(1,2), ROWCOL_LAST(1,3)),
Vector4(ROWCOL(2,0), ROWCOL(2,1), ROWCOL(2,2), ROWCOL_LAST(2,3)),
Vector4(ROWCOL(3,0), ROWCOL(3,1), ROWCOL(3,2), ROWCOL_LAST(3,3))
);
#undef ROWCOL
#undef ROWCOL_LAST
}
WWINLINE Matrix4x4 operator * (const Matrix3D & a, const Matrix4x4 & b)
{
// This function hand coded to handle the last row of a as 0,0,0,1
#define ROWCOL(i,j) a[i][0]*b[0][j] + a[i][1]*b[1][j] + a[i][2]*b[2][j] + a[i][3]*b[3][j]
return Matrix4x4(
Vector4(ROWCOL(0,0), ROWCOL(0,1), ROWCOL(0,2), ROWCOL(0,3)),
Vector4(ROWCOL(1,0), ROWCOL(1,1), ROWCOL(1,2), ROWCOL(1,3)),
Vector4(ROWCOL(2,0), ROWCOL(2,1), ROWCOL(2,2), ROWCOL(2,3)),
Vector4(b[3][0], b[3][1], b[3][2], b[3][3])
);
#undef ROWCOL
}
/*
** Multiply a Matrix4x4 by a Vector3 (assumes w=1.0!!!). Yeilds a Vector4 result
*/
WWINLINE Vector4 operator * (const Matrix4x4 & a, const Vector3 & v) {
return Vector4(
a[0][0] * v[0] + a[0][1] * v[1] + a[0][2] * v[2] + a[0][3] * 1.0f,
a[1][0] * v[0] + a[1][1] * v[1] + a[1][2] * v[2] + a[1][3] * 1.0f,
a[2][0] * v[0] + a[2][1] * v[1] + a[2][2] * v[2] + a[2][3] * 1.0f,
a[3][0] * v[0] + a[3][1] * v[1] + a[3][2] * v[2] + a[3][3] * 1.0f
);
}
/*
** Multiply a Matrix4x4 by a Vector4
*/
WWINLINE Vector4 operator * (const Matrix4x4 & a, const Vector4 & v) {
return Vector4(
a[0][0] * v[0] + a[0][1] * v[1] + a[0][2] * v[2] + a[0][3] * v[3],
a[1][0] * v[0] + a[1][1] * v[1] + a[1][2] * v[2] + a[1][3] * v[3],
a[2][0] * v[0] + a[2][1] * v[1] + a[2][2] * v[2] + a[2][3] * v[3],
a[3][0] * v[0] + a[3][1] * v[1] + a[3][2] * v[2] + a[3][3] * v[3]
);
}
/*
** Multiply a Matrix4x4 by a Vector4
*/
WWINLINE void Matrix4x4::Transform_Vector(const Matrix4x4 & A,const Vector3 & in,Vector3 * out)
{
Vector3 tmp;
Vector3 * v;
// check for aliased parameters
if (out == &in) {
tmp = in;
v = &tmp;
} else {
v = (Vector3 *)&in; // whats the right way to do this...
}
out->X = (A[0][0] * v->X + A[0][1] * v->Y + A[0][2] * v->Z + A[0][3]);
out->Y = (A[1][0] * v->X + A[1][1] * v->Y + A[1][2] * v->Z + A[1][3]);
out->Z = (A[2][0] * v->X + A[2][1] * v->Y + A[2][2] * v->Z + A[2][3]);
}
WWINLINE void Matrix4x4::Transform_Vector(const Matrix4x4 & A,const Vector3 & in,Vector4 * out)
{
out->X = (A[0][0] * in.X + A[0][1] * in.Y + A[0][2] * in.Z + A[0][3]);
out->Y = (A[1][0] * in.X + A[1][1] * in.Y + A[1][2] * in.Z + A[1][3]);
out->Z = (A[2][0] * in.X + A[2][1] * in.Y + A[2][2] * in.Z + A[2][3]);
out->W = 1.0f;
}
WWINLINE void Matrix4x4::Transform_Vector(const Matrix4x4 & A,const Vector4 & in,Vector4 * out)
{
Vector4 tmp;
Vector4 * v;
// check for aliased parameters
if (out == &in) {
tmp = in;
v = &tmp;
} else {
v = (Vector4 *)&in; // whats the right way to do this...
}
out->X = (A[0][0] * v->X + A[0][1] * v->Y + A[0][2] * v->Z + A[0][3] * v->W);
out->Y = (A[1][0] * v->X + A[1][1] * v->Y + A[1][2] * v->Z + A[1][3] * v->W);
out->Z = (A[2][0] * v->X + A[2][1] * v->Y + A[2][2] * v->Z + A[2][3] * v->W);
out->W = (A[3][0] * v->X + A[3][1] * v->Y + A[3][2] * v->Z + A[3][3] * v->W);
}
#endif /*MATRIX4_H*/

View File

@@ -0,0 +1,243 @@
/*
** Command & Conquer Generals Zero Hour(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 : G *
* *
* $Archive:: /G/WWMath/normalcone.h $*
* *
* $Author:: Eric_c $*
* *
* $Modtime:: 11/01/99 2:44p $*
* *
* $Revision:: 8 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*
** The NormalCone is a class used to represent a cone of unit length. It can be used to
** loosely represent a collection of other normals allowing backface culling to be
** performed at a heirarchial level rather than at a triangle level.
**
** The term 'NormalCone' is a bit of a misnomer; it is really a circular portion of a sphere.
** -ehc
*/
#ifndef NORMALCONE_H
#define NORMALCONE_H
#include "vector3.h"
#include "matrix3.h"
class NormalCone : public Vector3
{
public:
NormalCone() {}
NormalCone(const Vector3 & normal, float angle = 1.0f)
: Vector3(normal),
Angle(angle)
{}
void Set(const Vector3 & normal, float angle = 1.0f)
{
Vector3::Set(normal);
Angle = angle;
}
void Set(const NormalCone & src)
{
Set(src, src.Angle);
}
// return true if this object has degenerated into a sphere.
inline bool Complete_Sphere()
{
return (Angle - WWMATH_EPSILON) <= -1.0f;
}
// find the two vectors on the edge of the cone residing on the same plane as the input vector.
// Note: these two Get_Coplanar functions return floats in an attempt to reduce float/int CPU state changes...
inline float Get_Coplanar_Normals(const Vector3 & Input, Vector3 & Output1, Vector3 & Output2) const;
// find the two vectors on the edge of the cone residing on the same plane as the input vector and their dot products with the input.
inline float Get_Coplanar_Normals_And_Dots(const Vector3 & Input, Vector3 & Output1, Vector3 & Output2, float & dot1, float & dot2) const;
// evaluate the input vector, expanding the angle of the cone and recalculating the
// new center vector as needed.
inline void Merge(const Vector3 & Input);
// merge the input normal cone's coplanar normals with this object.
inline void Merge(const NormalCone & Input);
// this function returns the dot product between the input vector and the nearest coplanar normal
// contained by the cone.
// If the input vector is also contained by the cone, the result is always 1.0f.
// Note that in the case of a complete sphere, the nearest coplanar normal will be pointing in
// the opposite direction of the input vector.
inline float Smallest_Dot_Product(const Vector3 & Input);
// this value is the dot product of the edge of the cone and the center of the cone.
// A value of 1.0f indicates that it is a degenerate cone which is basically a cone with radius zero.
// A value of zero indicates the cone is actually hemisphere.
// A value of -1.0f indicates that it is a complete sphere.
float Angle;
};
// find the two vectors on the edge of the cone residing on the same plane as the input vector.
inline float NormalCone::Get_Coplanar_Normals(const Vector3 & Input, Vector3 & Output1, Vector3 & Output2) const
{
// get the cross product of the existing normal and the new one
Vector3 cross;
Vector3::Cross_Product(Input, *this, & cross);
float length = cross.Length2();
if(length < WWMATH_EPSILON)
return 0.0f;
length = sqrt(length);
cross /= length;
// Make a matrix3 which uses it as an axis of rotation and
// rotate this about that axis twice, once +Angle, once -Angle.
float radians = (1.0f - Angle) * WWMATH_PI * 0.5f;
Matrix3 m1(cross, radians);
Matrix3 m2(cross, -radians);
Matrix3::Rotate_Vector(m1, *this, & Output1);
Matrix3::Rotate_Vector(m2, *this, & Output2);
return length;
}
inline float NormalCone::Get_Coplanar_Normals_And_Dots(const Vector3 & Input, Vector3 & Output1, Vector3 & Output2, float & Dot1, float & Dot2) const
{
float length = Get_Coplanar_Normals(Input, Output1, Output2);
if(length < WWMATH_EPSILON)
return 0.0f;
// get the dot products of the new normal with the two coplanar normals and the current average
Dot1 = Vector3::Dot_Product(Input, Output1);
Dot2 = Vector3::Dot_Product(Input, Output2);
return length;
}
// evaluate the input vector, expanding the angle of the cone and recalculating the
// new center vector as needed.
inline void NormalCone::Merge(const Vector3 & Input)
{
// early exit if this normal cone has already turned into a complete sphere.
if(Complete_Sphere())
return;
// get the dot of the new vector with the current center vector
float dot0 = Vector3::Dot_Product(Input, * this) + WWMATH_EPSILON;
// if the dot value is greater than the existing cone angle, then the new vector fits
// within the cone, so return.
if(dot0 >= Angle)
return;
// get the two normals found in the cone which are coplanar to the one passed to this function.
Vector3 normal1, normal2;
float dot1, dot2;
if(Get_Coplanar_Normals_And_Dots(Input, normal1, normal2, dot1, dot2) <= WWMATH_EPSILON)
return;
// test the case where the current average has a lower dot than either of the coplanar normals.
// If true, this means that the object now represents a complete sphere with normals facing every
// direction.
if((dot0 < dot1) && (dot0 < dot2))
{
Angle = -1;
return;
}
// the smaller of the dot values we have is going to indicate which of the two coplanar normals to use
// for averaging into the new center normal.
if(dot1 < dot2)
Set(Input + normal1, dot1);
else
Set(Input + normal2, dot2);
// if the angle is < 0, reverse the direction of the averaged normal since we have constructed
// something more like a sphere with a cone shape taken out of it (a negative cone).
if(Angle < WWMATH_EPSILON)
*this *= -1;
Normalize();
}
// merge the input normal cone's coplanar normals with this object.
inline void NormalCone::Merge(const NormalCone & Input)
{
Vector3 n1, n2;
if(Input.Get_Coplanar_Normals(*this, n1,n2) >= WWMATH_EPSILON)
{
Merge(n1);
Merge(n2);
}
}
// this function returns the dot product between the input vector and the nearest coplanar normal
// contained by the cone.
// If the input vector is also contained by the cone, the result is always 1.0f.
// Note that in the case of a complete sphere, the nearest coplanar normal will be pointing in
// the opposite direction of the input vector.
inline float NormalCone::Smallest_Dot_Product(const Vector3 & Input)
{
if(Complete_Sphere())
return -1.0f;
// get the dot of the new vector with the current center vector
float dot0 = Vector3::Dot_Product(Input, * this);
// if the negative dot value is greater than the existing cone angle, then the new vector is
// parallel to one of the vectors contained in the cone but in negative
// direction, so return -1.0f
if(-dot0 + WWMATH_EPSILON >= Angle)
return -1.0f;
// if the dot value is greater than the existing cone angle, then the new vector is
// parallel to one of the vectors contained in the cone, so return 1.0f
if(dot0 + WWMATH_EPSILON >= Angle)
return 1.0f;
// get the two normals found in the cone which are coplanar to the one passed to this function.
Vector3 normal1, normal2;
float dot1, dot2;
Get_Coplanar_Normals_And_Dots(Input, normal1, normal2, dot1, dot2);
// return the smaller of the two dot products
if(dot1 < dot2)
return dot1;
return dot2;
}
#endif

View File

@@ -0,0 +1,791 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/obbox.cpp $*
* *
* Org Author:: Greg_h *
* *
* Author : Kenny Mitchell *
* *
* $Modtime:: 06/26/02 4:04p $*
* *
* $Revision:: 24 $*
* *
* 06/26/02 KM Matrix name change to avoid MAX conflicts *
*---------------------------------------------------------------------------------------------*
* Functions: *
* OBBoxClass::OBBoxClass -- Constructor that computes the box for a set of point *
* OBBoxClass::Init_From_Box_Points -- Create an OBBox from 8 corners of a box *
* OBBoxClass::Init_Random -- initalize a random oriented box *
* Oriented_Boxes_Intersect_On_Axis -- test if two boxes intersect on given axis *
* Oriented_Boxes_Intersect -- test if two oriented boxes intersect *
* Oriented_Boxes_Collide_On_Axis -- test if two boxes collide on the given axis *
* Oriented_Boxes_Collide -- test if two oriented boxes collide *
* Oriented_Box_Intersects_Tri_On_Axis -- tests if the box and tri intersect on the axis *
* Oriented_Box_Intersects_Tri -- tests if the given box and tri intersect *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "obbox.h"
#include "matrix3.h"
#include "vector3.h"
#include "aabox.h"
#include "tri.h"
#include "plane.h"
#include "quat.h"
#include <assert.h>
//#include <stdlib.h>
/***********************************************************************************************
* OBBoxClass::OBBoxClass -- Constructor that computes the box for a set of points *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 2/4/98 GTH : Created. *
*=============================================================================================*/
OBBoxClass::OBBoxClass(const Vector3 * /*points*/, int /*n*/)
{
// TODO: IMPLEMENT THIS!!!
assert(0);
#if 0
int i;
// compute mean and covariances of points
float xsum = 0.0f, ysum = 0.0f, zsum = 0.0f;;
float xxsum = 0.0f, xysum = 0.0f, xzsum = 0.0f;
float yysum = 0.0f, yzsum = 0.0f, zzsum = 0.0f;
for (i = 0; i < n; i++)
{
xsum += points[i].X;
ysum += points[i].Y;
zsum += points[i].Z;
xxsum += points[i].X * points[i].X;
xysum += points[i].X * points[i].Y;
xzsum += points[i].X * points[i].Z;
yysum += points[i].Y * points[i].Y;
yzsum += points[i].Y * points[i].Z;
zzsum += points[i].Z * points[i].Z;
}
float xmean = xsum/n;
float ymean = ysum/n;
float zmean = zsum/n;
float xxcov = xxsum/n - xmean*xmean;
float xycov = xysum/n - xmean*ymean;
float xzcov = xzsum/n - xmean*zmean;
float yycov = yysum/n - ymean*ymean;
float yzcov = yzsum/n - ymean*zmean;
float zzcov = zzsum/n - zmean*zmean;
// compute eigenvectors for covariance matrix,
// these will be the axes.
mgcEigen eig(3);
eig.Matrix(0,0) = xxcov;
eig.Matrix(0,1) = xycov;
eig.Matrix(0,2) = xzcov;
eig.Matrix(1,0) = xycov;
eig.Matrix(1,1) = yycov;
eig.Matrix(1,2) = yzcov;
eig.Matrix(2,0) = xzcov;
eig.Matrix(2,1) = yzcov;
eig.Matrix(2,2) = zzcov;
eig.EigenStuff3();
Point3 U =
{
eig.Eigenvector(0,0),
eig.Eigenvector(1,0),
eig.Eigenvector(2,0)
};
Point3 V =
{
eig.Eigenvector(0,1),
eig.Eigenvector(1,1),
eig.Eigenvector(2,1)
};
Point3 W =
{
eig.Eigenvector(0,2),
eig.Eigenvector(1,2),
eig.Eigenvector(2,2)
};
// box center is the mean of the distribution
box.center.x = xmean;
box.center.y = ymean;
box.center.z = zmean;
// Box axes are the eigenvectors of the covariance matrix with
// adjusted lengths to enclose the points. If U, V, and W are the
// eigenvectors, C is the center of the box, and X is a point in
// the input list, then X = C + a*U + b*V + c*W. The box extent is
// determined by max|a|, max|b|, and max|c|. The box axes are then
// defined to be (max|a|)*U and (max|b|)*V. Note that since U and V
// are unit length and orthogonal, a = Dot(U,X-C), b = Dot(V,X-C),
// and c = Dot(W,X-C).
float amax = 0.0f, bmax = 0.0f, cmax = 0.0f;
for (i = 0; i < n; i++)
{
float dx = pt[i].x - box.center.x;
float dy = pt[i].y - box.center.y;
float dz = pt[i].z - box.center.z;
float absdot = float(WWMath::Fabs(U.x*dx+U.y*dy+U.z*dz));
if ( absdot > amax )
amax = absdot;
absdot = float(WWMath::Fabs(V.x*dx+V.y*dy+V.z*dz));
if ( absdot > bmax )
bmax = absdot;
absdot = float(WWMath::Fabs(W.x*dx+W.y*dy+W.z*dz));
if ( absdot > cmax )
cmax = absdot;
}
box.axis[0].x = amax*U.x;
box.axis[0].y = amax*U.y;
box.axis[0].z = amax*U.z;
box.axis[1].x = bmax*V.x;
box.axis[1].y = bmax*V.y;
box.axis[1].z = bmax*V.z;
box.axis[2].x = cmax*W.x;
box.axis[2].y = cmax*W.y;
box.axis[2].z = cmax*W.z;
#endif
}
/***********************************************************************************************
* OBBoxClass::Init_From_Box_Points -- Create an OBBox from 8 corners of a box *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 2/24/98 GTH : Created. *
*=============================================================================================*/
void OBBoxClass::Init_From_Box_Points(Vector3 * points,int num)
{
int i,j;
/*
** This function assumes that you pass in 8 points which are the
** corners of a rectangular solid. Bad things will happen if
** this assumption is not true!!!!
*/
assert(num == 8);
/*
** Just pick the first point as the preliminary center. Compute
** vectors from this point to each of the other points
*/
Vector3 dp[8];
for (i=1;i<num;i++) {
dp[i] = points[i] - points[0];
}
/*
** Find the shortest two candidate axes. Then the
** third axis will be the cross product of these two.
*/
for (i=1;i<num;i++) {
for (j=i+1;j<num;j++) {
if (dp[j].Length2() < dp[i].Length2()) {
Swap(dp[j],dp[i]);
}
}
}
Vector3 axis0,axis1,axis2;
#ifdef ALLOW_TEMPORARIES
axis0 = Normalize(dp[1]);
axis1 = Normalize(dp[2]);
#else
axis0 = dp[1]; axis0.Normalize();
axis1 = dp[2]; axis1.Normalize();
#endif
Vector3::Cross_Product(axis0,axis1,&axis2);
Basis = Matrix3x3(axis0,axis1,axis2);
/*
** Center is the average of all of the points
*/
Center.Set(0,0,0);
for (i=0; i<num; i++) {
Center += points[i];
}
Center.X /= num;
Center.Y /= num;
Center.Z /= num;
/*
** Compute extents along the computed axes. This is done
** by projecting each point onto the three axes and keeping
** the largest projection on each.
*/
Extent.Set(0,0,0);
for (i=0; i<num; i++) {
float dx = points[i].X - Center.X;
float dy = points[i].Y - Center.Y;
float dz = points[i].Z - Center.Z;
float xprj = float(WWMath::Fabs(axis0.X * dx + axis0.Y * dy + axis0.Z * dz));
if (xprj > Extent.X) Extent.X = xprj;
float yprj = float(WWMath::Fabs(axis1.X * dx + axis1.Y * dy + axis1.Z * dz));
if (yprj > Extent.Y) Extent.Y = yprj;
float zprj = float(WWMath::Fabs(axis2.X * dx + axis2.Y * dy + axis2.Z * dz));
if (zprj > Extent.Z) Extent.Z = zprj;
}
}
/***********************************************************************************************
* OBBoxClass::Init_Random -- initalize a random oriented box *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/21/98 GTH : Created. *
*=============================================================================================*/
void OBBoxClass::Init_Random(float min_extent,float max_extent)
{
Center.Set(0,0,0);
Extent.X = min_extent + WWMath::Random_Float() * (max_extent - min_extent);
Extent.Y = min_extent + WWMath::Random_Float() * (max_extent - min_extent);
Extent.Z = min_extent + WWMath::Random_Float() * (max_extent - min_extent);
Quaternion orient;
orient.X = WWMath::Random_Float();
orient.Y = WWMath::Random_Float();
orient.Z = WWMath::Random_Float();
orient.W = WWMath::Random_Float();
orient.Normalize();
Basis = Build_Matrix3(orient);
}
/***********************************************************************************************
* Oriented_Boxes_Intersect_On_Axis -- test if two boxes intersect on given axis *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/7/99 GTH : Created. *
*=============================================================================================*/
bool Oriented_Boxes_Intersect_On_Axis
(
const OBBoxClass & box0,
const OBBoxClass & box1,
const Vector3 & axis
)
{
float ra,rb,rsum;
if (axis.Length2() < WWMATH_EPSILON) return true;
ra = box0.Project_To_Axis(axis);
rb = box1.Project_To_Axis(axis);
rsum = WWMath::Fabs(ra) + WWMath::Fabs(rb);
// project the center distance onto the line:
Vector3 C = box1.Center - box0.Center;
float cdist = Vector3::Dot_Product(axis,C);
if ((cdist > rsum) || (cdist < -rsum)) {
return false;
}
return true;
}
/***********************************************************************************************
* Oriented_Boxes_Intersect -- test if two oriented boxes intersect *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/7/99 GTH : Created. *
*=============================================================================================*/
bool Oriented_Boxes_Intersect
(
const OBBoxClass & box0,
const OBBoxClass & box1
)
{
Vector3 axis;
Vector3 A[3],B[3];
// vectors for the axis directions of the two boxes in world space
A[0].Set(box0.Basis[0][0],box0.Basis[1][0],box0.Basis[2][0]);
A[1].Set(box0.Basis[0][1],box0.Basis[1][1],box0.Basis[2][1]);
A[2].Set(box0.Basis[0][2],box0.Basis[1][2],box0.Basis[2][2]);
B[0].Set(box1.Basis[0][0],box1.Basis[1][0],box1.Basis[2][0]);
B[1].Set(box1.Basis[0][1],box1.Basis[1][1],box1.Basis[2][1]);
B[2].Set(box1.Basis[0][2],box1.Basis[1][2],box1.Basis[2][2]);
/////////////////////////////////////////////////////////////////////////
// Projecting the two boxes onto Box0's X axis. If their intervals
// on this line do not intersect, the boxes are not intersecting.
// Each of the tests in this function work in a similar way.
/////////////////////////////////////////////////////////////////////////
if (!Oriented_Boxes_Intersect_On_Axis(box0,box1,A[0])) return false;
if (!Oriented_Boxes_Intersect_On_Axis(box0,box1,A[1])) return false;
if (!Oriented_Boxes_Intersect_On_Axis(box0,box1,A[2])) return false;
if (!Oriented_Boxes_Intersect_On_Axis(box0,box1,B[0])) return false;
if (!Oriented_Boxes_Intersect_On_Axis(box0,box1,B[1])) return false;
if (!Oriented_Boxes_Intersect_On_Axis(box0,box1,B[2])) return false;
/////////////////////////////////////////////////////////////////////////
// None of the aligned axes turned out to be separating axes. Now
// we check all combinations of cross products of the two boxes axes.
/////////////////////////////////////////////////////////////////////////
Vector3::Cross_Product(A[0],B[0],&axis);
if (!Oriented_Boxes_Intersect_On_Axis(box0,box1,axis)) return false;
Vector3::Cross_Product(A[0],B[1],&axis);
if (!Oriented_Boxes_Intersect_On_Axis(box0,box1,axis)) return false;
Vector3::Cross_Product(A[0],B[2],&axis);
if (!Oriented_Boxes_Intersect_On_Axis(box0,box1,axis)) return false;
Vector3::Cross_Product(A[1],B[0],&axis);
if (!Oriented_Boxes_Intersect_On_Axis(box0,box1,axis)) return false;
Vector3::Cross_Product(A[1],B[1],&axis);
if (!Oriented_Boxes_Intersect_On_Axis(box0,box1,axis)) return false;
Vector3::Cross_Product(A[1],B[2],&axis);
if (!Oriented_Boxes_Intersect_On_Axis(box0,box1,axis)) return false;
Vector3::Cross_Product(A[2],B[0],&axis);
if (!Oriented_Boxes_Intersect_On_Axis(box0,box1,axis)) return false;
Vector3::Cross_Product(A[2],B[1],&axis);
if (!Oriented_Boxes_Intersect_On_Axis(box0,box1,axis)) return false;
Vector3::Cross_Product(A[2],B[2],&axis);
if (!Oriented_Boxes_Intersect_On_Axis(box0,box1,axis)) return false;
// None of the above tests separated the two boxes, so they are intersecting
return true;
}
/***********************************************************************************************
* Oriented_Boxes_Collide_On_Axis -- test if two boxes collide on the given axis *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/7/99 GTH : Created. *
*=============================================================================================*/
bool Oriented_Boxes_Collide_On_Axis
(
const OBBoxClass & box0,
const Vector3 & v0,
const OBBoxClass & box1,
const Vector3 & v1,
const Vector3 & axis,
float dt
)
{
float ra,rb,rsum;
if (axis.Length2() < WWMATH_EPSILON) return true;
ra = box0.Project_To_Axis(axis);
rb = box1.Project_To_Axis(axis);
rsum = WWMath::Fabs(ra) + WWMath::Fabs(rb);
// project the center distance onto the line:
Vector3 C = box1.Center - box0.Center;
Vector3 V = v1 - v0;
float cdist = Vector3::Dot_Product(axis,C);
float vdist = cdist + dt * Vector3::Dot_Product(axis,V);
if ((cdist > rsum && vdist > rsum) || (cdist < -rsum && vdist < -rsum)) {
return false;
}
return true;
}
/***********************************************************************************************
* Oriented_Boxes_Collide -- test if two oriented boxes collide *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/7/99 GTH : Created. *
*=============================================================================================*/
bool Oriented_Boxes_Collide
(
const OBBoxClass & box0,
const Vector3 & v0,
const OBBoxClass & box1,
const Vector3 & v1,
float dt
)
{
bool intersect = true;
// variables for holding the separating axis and the projected distances
Vector3 SepAxis;
// vectors for the axis directions of the two boxes in world space
Vector3 A0(box0.Basis[0][0],box0.Basis[1][0],box0.Basis[2][0]);
Vector3 A1(box0.Basis[0][1],box0.Basis[1][1],box0.Basis[2][1]);
Vector3 A2(box0.Basis[0][2],box0.Basis[1][2],box0.Basis[2][2]);
Vector3 B0(box1.Basis[0][0],box1.Basis[1][0],box1.Basis[2][0]);
Vector3 B1(box1.Basis[0][1],box1.Basis[1][1],box1.Basis[2][1]);
Vector3 B2(box1.Basis[0][2],box1.Basis[1][2],box1.Basis[2][2]);
/////////////////////////////////////////////////////////////////////////
// L = A0
//
// Projecting the two boxes onto Box0's X axis. If their intervals
// on this line do not intersect, the boxes are not intersecting!
// Each of the tests in this function work in a similar way.
/////////////////////////////////////////////////////////////////////////
SepAxis = A0;
if (!Oriented_Boxes_Collide_On_Axis(box0,v0,box1,v1,SepAxis,dt)) {
intersect = false;
goto exit;
}
/////////////////////////////////////////////////////////////////////////
// L = A1
// Separating Axis is Box0's Y axis
/////////////////////////////////////////////////////////////////////////
SepAxis = A1;
if (!Oriented_Boxes_Collide_On_Axis(box0,v0,box1,v1,SepAxis,dt)) {
intersect = false;
goto exit;
}
/////////////////////////////////////////////////////////////////////////
// L = A2
// Separating Axis is Box0's Z axis
/////////////////////////////////////////////////////////////////////////
SepAxis = A2;
if (!Oriented_Boxes_Collide_On_Axis(box0,v0,box1,v1,SepAxis,dt)) {
intersect = false;
goto exit;
}
/////////////////////////////////////////////////////////////////////////
// L = B0
// Separating Axis is Box1's X axis
/////////////////////////////////////////////////////////////////////////
SepAxis = B0;
if (!Oriented_Boxes_Collide_On_Axis(box0,v0,box1,v1,SepAxis,dt)) {
intersect = false;
goto exit;
}
/////////////////////////////////////////////////////////////////////////
// L = B1
// Separating Axis is Box1's Y axis
/////////////////////////////////////////////////////////////////////////
SepAxis = B1;
if (!Oriented_Boxes_Collide_On_Axis(box0,v0,box1,v1,SepAxis,dt)) {
intersect = false;
goto exit;
}
/////////////////////////////////////////////////////////////////////////
// L = B2
// Separating Axis is Box1's Z axis
/////////////////////////////////////////////////////////////////////////
SepAxis = B2;
if (!Oriented_Boxes_Collide_On_Axis(box0,v0,box1,v1,SepAxis,dt)) {
intersect = false;
goto exit;
}
/////////////////////////////////////////////////////////////////////////
// None of the aligned axes turned out to be separating axes. Now
// we check all combinations of cross products of the two boxes axes.
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
// L = A0xB0
/////////////////////////////////////////////////////////////////////////
Vector3::Cross_Product(A0,B0,&SepAxis);
if (!Oriented_Boxes_Collide_On_Axis(box0,v0,box1,v1,SepAxis,dt)) {
intersect = false;
goto exit;
}
/////////////////////////////////////////////////////////////////////////
// L = A0xB1
/////////////////////////////////////////////////////////////////////////
Vector3::Cross_Product(A0,B1,&SepAxis);
if (!Oriented_Boxes_Collide_On_Axis(box0,v0,box1,v1,SepAxis,dt)) {
intersect = false;
goto exit;
}
/////////////////////////////////////////////////////////////////////////
// L = A0xB2
/////////////////////////////////////////////////////////////////////////
Vector3::Cross_Product(A0,B2,&SepAxis);
if (!Oriented_Boxes_Collide_On_Axis(box0,v0,box1,v1,SepAxis,dt)) {
intersect = false;
goto exit;
}
/////////////////////////////////////////////////////////////////////////
// L = A1xB0
/////////////////////////////////////////////////////////////////////////
Vector3::Cross_Product(A1,B0,&SepAxis);
if (!Oriented_Boxes_Collide_On_Axis(box0,v0,box1,v1,SepAxis,dt)) {
intersect = false;
goto exit;
}
/////////////////////////////////////////////////////////////////////////
// L = A1xB1
/////////////////////////////////////////////////////////////////////////
Vector3::Cross_Product(A1,B1,&SepAxis);
if (!Oriented_Boxes_Collide_On_Axis(box0,v0,box1,v1,SepAxis,dt)) {
intersect = false;
goto exit;
}
/////////////////////////////////////////////////////////////////////////
// L = A1xB2
/////////////////////////////////////////////////////////////////////////
Vector3::Cross_Product(A1,B2,&SepAxis);
if (!Oriented_Boxes_Collide_On_Axis(box0,v0,box1,v1,SepAxis,dt)) {
intersect = false;
goto exit;
}
/////////////////////////////////////////////////////////////////////////
// L = A2xB0
/////////////////////////////////////////////////////////////////////////
Vector3::Cross_Product(A2,B0,&SepAxis);
if (!Oriented_Boxes_Collide_On_Axis(box0,v0,box1,v1,SepAxis,dt)) {
intersect = false;
goto exit;
}
/////////////////////////////////////////////////////////////////////////
// L = A2xB1
/////////////////////////////////////////////////////////////////////////
Vector3::Cross_Product(A2,B1,&SepAxis);
if (!Oriented_Boxes_Collide_On_Axis(box0,v0,box1,v1,SepAxis,dt)) {
intersect = false;
goto exit;
}
/////////////////////////////////////////////////////////////////////////
// L = A2xB2
/////////////////////////////////////////////////////////////////////////
Vector3::Cross_Product(A2,B2,&SepAxis);
if (!Oriented_Boxes_Collide_On_Axis(box0,v0,box1,v1,SepAxis,dt)) {
intersect = false;
goto exit;
}
exit:
return intersect;
}
/***********************************************************************************************
* Oriented_Box_Intersects_Tri_On_Axis -- tests if the box and tri intersect on the axis *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/7/99 GTH : Created. *
*=============================================================================================*/
bool Oriented_Box_Intersects_Tri_On_Axis(const OBBoxClass & box,const TriClass & tri,Vector3 & axis)
{
float leb; // leading edge of box (farthest point from center)
float lep; // leading edge of poly (closest point to center)
float dist; // distance from box center to v0
float tmp;
if (axis.Length2() < WWMATH_EPSILON) return true;
Vector3 D = *(tri.V[0]) - box.Center;
Vector3 r1 = *(tri.V[1]) - *(tri.V[0]);
Vector3 r2 = *(tri.V[2]) - *(tri.V[0]);
// I want the axis to point from box.center to tri.v0
dist = Vector3::Dot_Product(D,axis);
if (dist < 0) {
dist = -dist;
axis = -axis;
}
// compute leading edge of the box
leb = box.Project_To_Axis(axis);
// compute the leading edge of the triangle
lep = 0;
tmp = Vector3::Dot_Product(r1,axis); if (tmp < lep) lep = tmp;
tmp = Vector3::Dot_Product(r2,axis); if (tmp < lep) lep = tmp;
lep += dist;
if (lep >= leb) {
return false;
} else {
return true;
}
}
/***********************************************************************************************
* Oriented_Box_Intersects_Tri -- tests if the given box and tri intersect *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/7/99 GTH : Created. *
*=============================================================================================*/
bool Oriented_Box_Intersects_Tri(const OBBoxClass & box,const TriClass & tri)
{
Vector3 axis;
// vectors for the axis directions of the two boxes in world space
Vector3 A[3];
Vector3 E[3];
Vector3 normal = *tri.N;
A[0].Set(box.Basis[0][0],box.Basis[1][0],box.Basis[2][0]);
A[1].Set(box.Basis[0][1],box.Basis[1][1],box.Basis[2][1]);
A[2].Set(box.Basis[0][2],box.Basis[1][2],box.Basis[2][2]);
E[0] = *(tri.V[1]) - *(tri.V[0]);
E[1] = *(tri.V[2]) - *(tri.V[1]);
E[2] = *(tri.V[0]) - *(tri.V[2]);
if (!Oriented_Box_Intersects_Tri_On_Axis(box,tri,normal)) return false;
if (!Oriented_Box_Intersects_Tri_On_Axis(box,tri,A[0])) return false;
if (!Oriented_Box_Intersects_Tri_On_Axis(box,tri,A[1])) return false;
if (!Oriented_Box_Intersects_Tri_On_Axis(box,tri,A[2])) return false;
Vector3::Cross_Product(A[0],E[0],&axis);
if (!Oriented_Box_Intersects_Tri_On_Axis(box,tri,axis)) return false;
Vector3::Cross_Product(A[0],E[1],&axis);
if (!Oriented_Box_Intersects_Tri_On_Axis(box,tri,axis)) return false;
Vector3::Cross_Product(A[0],E[2],&axis);
if (!Oriented_Box_Intersects_Tri_On_Axis(box,tri,axis)) return false;
Vector3::Cross_Product(A[1],E[0],&axis);
if (!Oriented_Box_Intersects_Tri_On_Axis(box,tri,axis)) return false;
Vector3::Cross_Product(A[1],E[1],&axis);
if (!Oriented_Box_Intersects_Tri_On_Axis(box,tri,axis)) return false;
Vector3::Cross_Product(A[1],E[2],&axis);
if (!Oriented_Box_Intersects_Tri_On_Axis(box,tri,axis)) return false;
Vector3::Cross_Product(A[2],E[0],&axis);
if (!Oriented_Box_Intersects_Tri_On_Axis(box,tri,axis)) return false;
Vector3::Cross_Product(A[2],E[1],&axis);
if (!Oriented_Box_Intersects_Tri_On_Axis(box,tri,axis)) return false;
Vector3::Cross_Product(A[2],E[2],&axis);
if (!Oriented_Box_Intersects_Tri_On_Axis(box,tri,axis)) return false;
return true;
}

View File

@@ -0,0 +1,271 @@
/*
** Command & Conquer Generals Zero Hour(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 : wwmath *
* *
* $Archive:: /Commando/Code/wwmath/obbox.h $*
* *
* Org Author:: Greg_h *
* *
* Author : Kenny Mitchell *
* *
* $Modtime:: 06/26/02 4:04p $*
* *
* $Revision:: 24 $*
* *
* 06/26/02 KM Matrix name change to avoid MAX conflicts *
*---------------------------------------------------------------------------------------------*
* Functions: *
* OBBoxClass::Transform -- transform an oriented box *
* OBBoxClass::Project_To_Axis -- compute projection onto the given axis *
* OBBoxClass::Compute_Point -- computes position of a parametricly defined point *
* OBBoxClass::Compute_Axis_Aligned_Extent -- computes extent of an AABox enclosing this box *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef OBBOX_H
#define OBBOX_H
#include "always.h"
#include "vector3.h"
#include "matrix3.h"
#include "matrix3d.h"
#include "wwmath.h"
#include "castres.h"
class TriClass;
class AABoxClass;
class PlaneClass;
/*
** OBBoxClass
**
** Oriented-Bounding-Box Class.
** This is a collision box in world space.
** Center - position of the center of the box
** Extents - size of the box
** Basis - rotation matrix defining the orientation of the box
**
** To find the world space coordinates of the "+x,+y,+z" corner of
** the bounding box you could use this equation:
** Vector3 corner = Center + Basis * Extent;
*/
class OBBoxClass
{
public:
OBBoxClass(void) { }
OBBoxClass(const OBBoxClass & that) :
Basis(that.Basis),
Center(that.Center),
Extent(that.Extent)
{ }
OBBoxClass(const Vector3 & center,const Vector3 & extent) :
Basis(1),
Center(center),
Extent(extent)
{ }
OBBoxClass(const Vector3 & center,const Vector3 & extent,const Matrix3x3 & basis) :
Basis(basis),
Center(center),
Extent(extent)
{ }
OBBoxClass(const Vector3 * points, int num_points);
bool operator== (const OBBoxClass &src);
bool operator!= (const OBBoxClass &src);
void Init_From_Box_Points(Vector3 * points,int num_points);
void Init_Random(float min_extent = 0.5f,float max_extent = 1.0f);
float Project_To_Axis(const Vector3 & axis) const;
float Volume(void) const { return 2.0*Extent.X * 2.0*Extent.Y * 2.0*Extent.Z; }
void Compute_Point(float params[3],Vector3 * set_point) const;
void Compute_Axis_Aligned_Extent(Vector3 * set_extent) const;
Matrix3x3 Basis;
Vector3 Center;
Vector3 Extent;
static void Transform(const Matrix3D & tm,const OBBoxClass & in,OBBoxClass * out);
};
// Test functions: slow, easy to understand version of box intersection code :)
bool Oriented_Boxes_Intersect(const OBBoxClass & box0,const OBBoxClass & box1);
bool Oriented_Boxes_Collide(const OBBoxClass & box0,const Vector3 & v0,const OBBoxClass & box1,const Vector3 & v1,float dt);
bool Oriented_Box_Intersects_Tri(const OBBoxClass & box,const TriClass & tri);
/***********************************************************************************************
* OBBoxClass::Project_To_Axis -- compute projection onto the given axis *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 2/24/98 GTH : Created. *
*=============================================================================================*/
inline float OBBoxClass::Project_To_Axis(const Vector3 & axis) const
{
float x = Extent[0] * Vector3::Dot_Product(axis,Vector3(Basis[0][0],Basis[1][0],Basis[2][0]));
float y = Extent[1] * Vector3::Dot_Product(axis,Vector3(Basis[0][1],Basis[1][1],Basis[2][1]));
float z = Extent[2] * Vector3::Dot_Product(axis,Vector3(Basis[0][2],Basis[1][2],Basis[2][2]));
// projection is the sum of the absolute values of the projections of the three extents
return (WWMath::Fabs(x) + WWMath::Fabs(y) + WWMath::Fabs(z));
}
/***********************************************************************************************
* OBBoxClass::Transform -- transform an oriented box *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 2/24/98 GTH : Created. *
*=============================================================================================*/
inline void OBBoxClass::Transform
(
const Matrix3D & tm,
const OBBoxClass & in,
OBBoxClass * out
)
{
WWASSERT(out);
WWASSERT(out!=&in);
out->Extent = in.Extent;
Matrix3D::Transform_Vector(tm,in.Center,&(out->Center));
Matrix3x3::Multiply(tm,in.Basis,&(out->Basis));
}
/***********************************************************************************************
* OBBoxClass::Compute_Point -- computes position of a parametricly defined point *
* *
* set_point = Center + params[0]*A0 + params[1]*A1 + params[2]*A2 *
* *
* INPUT: *
* params - parametric description of a point in the box. -1 < params[i] < 1 *
* set_point - pointer to a Vector3 to set. *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/2/99 GTH : Created. *
*=============================================================================================*/
inline void OBBoxClass::Compute_Point(float params[3],Vector3 * set_point) const
{
Vector3 point = Extent;
point.X *= params[0];
point.Y *= params[1];
point.Z *= params[2];
Matrix3x3::Rotate_Vector(Basis,point,set_point);
Vector3::Add(Center,*set_point,set_point);
}
/***********************************************************************************************
* OBBoxClass::Compute_Axis_Aligned_Extent -- computes extent of an AABox enclosing this box *
* *
* INPUT: *
* set_extent - pointer to a Vector3 to put the result into *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/15/99 gth : Created. *
*=============================================================================================*/
inline void OBBoxClass::Compute_Axis_Aligned_Extent(Vector3 * set_extent) const
{
WWASSERT(set_extent != NULL);
// x extent is the box projected onto the x axis
set_extent->X = WWMath::Fabs(Extent[0] * Basis[0][0]) +
WWMath::Fabs(Extent[1] * Basis[0][1]) +
WWMath::Fabs(Extent[2] * Basis[0][2]);
set_extent->Y = WWMath::Fabs(Extent[0] * Basis[1][0]) +
WWMath::Fabs(Extent[1] * Basis[1][1]) +
WWMath::Fabs(Extent[2] * Basis[1][2]);
set_extent->Z = WWMath::Fabs(Extent[0] * Basis[2][0]) +
WWMath::Fabs(Extent[1] * Basis[2][1]) +
WWMath::Fabs(Extent[2] * Basis[2][2]);
}
/***********************************************************************************************
* OBBoxClass::operator== -- Comparison operator *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 6/21/00 PDS : Created. *
*=============================================================================================*/
inline bool OBBoxClass::operator== (const OBBoxClass &src)
{
return (Center == src.Center) && (Extent == src.Extent) && (Basis == src.Basis);
}
/***********************************************************************************************
* OBBoxClass::operator!= -- Comparison operator *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 6/21/00 PDS : Created. *
*=============================================================================================*/
inline bool OBBoxClass::operator!= (const OBBoxClass &src)
{
return (Center != src.Center) || (Extent != src.Extent) && (Basis == src.Basis);
}
#endif

View File

@@ -0,0 +1,367 @@
/*
** Command & Conquer Generals Zero Hour(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/>.
*/
/* $Header: /Commando/Code/wwmath/ODE.CPP 8 7/02/99 10:32a Greg_h $ */
/***********************************************************************************************
*** Confidential - Westwood Studios ***
***********************************************************************************************
* *
* Project Name : Commando *
* *
* $Archive:: /Commando/Code/wwmath/ODE.CPP $*
* *
* Author:: Greg_h *
* *
* $Modtime:: 6/25/99 6:23p $*
* *
* $Revision:: 8 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* Euler_Integrate -- uses Eulers method to integrate a system of ODE's *
* Midpoint_Integrate -- midpoint method (Runge-Kutta 2) for integration *
* Runge_Kutta_Integrate -- Runge Kutta 4 method *
* Runge_Kutta5_Integrate -- 5th order Runge-Kutta *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "ode.h"
#include <assert.h>
static StateVectorClass Y0;
static StateVectorClass Y1;
static StateVectorClass _WorkVector0;
static StateVectorClass _WorkVector1;
static StateVectorClass _WorkVector2;
static StateVectorClass _WorkVector3;
static StateVectorClass _WorkVector4;
static StateVectorClass _WorkVector5;
static StateVectorClass _WorkVector6;
static StateVectorClass _WorkVector7;
/***********************************************************************************************
* Euler_Solve -- uses Eulers method to integrate a system of ODE's *
* *
* INPUT: *
* odesys - pointer to the ODE system to integrate *
* dt - size of the timestep *
* *
* OUTPUT: *
* state vector in odesys will be updated for the next timestep *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
* 6/25/99 GTH : Updated to the new integrator system *
*=============================================================================================*/
void IntegrationSystem::Euler_Integrate(ODESystemClass * sys, float dt)
{
WWASSERT(sys != NULL);
/*
** Get the current state
*/
Y0.Reset();
sys->Get_State(Y0);
Y1.Resize(Y0.Count());
/*
** make aliases to the work-vectors we need
*/
StateVectorClass & dydt = _WorkVector0;
dydt.Resize(Y0.Count());
/*
** Euler method, just evaluate the derivative, multiply
** by the time-step and add to the current state vector.
*/
sys->Compute_Derivatives(0,NULL,&dydt);
Y1.Resize(Y0.Count());
for (int i = 0; i < Y0.Count(); i++) {
Y1[i] = Y0[i] + dydt[i] * dt;
}
sys->Set_State(Y1);
}
/***********************************************************************************************
* Midpoint_Integrate -- midpoint method (Runge-Kutta 2) *
* *
* INPUT: *
* sys - pointer to the ODE system to integrate *
* dt - size of the timestep *
* *
* OUTPUT: *
* state vector in odesys will be updated for the next timestep *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
* 6/25/99 GTH : Updated to the new integrator system *
*=============================================================================================*/
void IntegrationSystem::Midpoint_Integrate(ODESystemClass * sys,float dt)
{
int i;
/*
** Get the current state
*/
Y0.Reset();
sys->Get_State(Y0);
Y1.Resize(Y0.Count());
/*
** make aliases to the work-vectors we need
*/
StateVectorClass & dydt = _WorkVector0;
StateVectorClass & ymid = _WorkVector1;
dydt.Resize(Y0.Count());
ymid.Resize(Y0.Count());
/*
** MidPoint method, first evaluate the derivitives of the
** state vector just like the Euler method.
*/
sys->Compute_Derivatives(0.0f,NULL,&dydt);
/*
** Compute the midpoint between the Euler solution and
** the input values.
*/
for (i=0; i<Y0.Count(); i++) {
ymid[i] = Y0[i] + dt * dydt[i] / 2.0f;
}
/*
** Re-compute derivatives at this point.
*/
sys->Compute_Derivatives(dt/2.0f,&ymid,&dydt);
/*
** Use these derivatives to compute the solution.
*/
for (i=0; i<Y0.Count(); i++) {
Y1[i] = Y0[i] + dt * dydt[i];
}
sys->Set_State(Y1);
}
/***********************************************************************************************
* Runge_Kutta_Integrate -- Runge Kutta 4 method *
* *
* INPUT: *
* odesys - pointer to the ODE system to integrate *
* dt - size of the timestep *
* *
* OUTPUT: *
* state vector in odesys will be updated for the next timestep *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
void IntegrationSystem::Runge_Kutta_Integrate(ODESystemClass * sys,float dt)
{
int i;
float dt2 = dt / 2.0f;
float dt6 = dt / 6.0f;
/*
** Get the current state
*/
Y0.Reset();
sys->Get_State(Y0);
Y1.Resize(Y0.Count());
/*
** make aliases to the work-vectors we need
*/
StateVectorClass & dydt = _WorkVector0;
StateVectorClass & dym = _WorkVector1;
StateVectorClass & dyt = _WorkVector2;
StateVectorClass & yt = _WorkVector3;
dydt.Resize(Y0.Count());
dym.Resize(Y0.Count());
dyt.Resize(Y0.Count());
yt.Resize(Y0.Count());
/*
** First Step
*/
sys->Compute_Derivatives(0.0f,NULL,&dydt);
for (i=0; i<Y0.Count(); i++) {
yt[i] = Y0[i] + dt2 * dydt[i];
}
/*
** Second Step
*/
sys->Compute_Derivatives(dt2, &yt, &dyt);
for (i=0; i<Y0.Count(); i++) {
yt[i] = Y0[i] + dt2 * dyt[i];
}
/*
** Third Step
*/
sys->Compute_Derivatives(dt2, &yt, &dym);
for (i=0; i<Y0.Count(); i++) {
yt[i] = Y0[i] + dt*dym[i];
dym[i] += dyt[i];
}
/*
** Fourth Step
*/
sys->Compute_Derivatives(dt, &yt, &dyt);
for (i=0; i<Y0.Count(); i++) {
Y1[i] = Y0[i] + dt6 * (dydt[i] + dyt[i] + 2.0f*dym[i]);
}
sys->Set_State(Y1);
}
/***********************************************************************************************
* Runge_Kutta5_Integrate -- 5th order Runge-Kutta *
* *
* INPUT: *
* odesys - pointer to the ODE system to integrate *
* dt - size of the timestep *
* *
* OUTPUT: *
* state vector in odesys will be updated for the next timestep *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
* 6/25/99 GTH : Converted to the new Integrator system *
*=============================================================================================*/
void IntegrationSystem::Runge_Kutta5_Integrate(ODESystemClass * odesys,float dt)
{
int i;
int veclen;
static const float a2 = 0.2f;
static const float a3 = 0.3f;
static const float a4 = 0.6f;
static const float a5 = 1.0f;
static const float a6 = 0.875f;
static const float b21 = 0.2f;
static const float b31 = 3.0f/40.0f;
static const float b32 = 9.0f/40.0f;
static const float b41 = 0.3f;
static const float b42 = -0.9f;
static const float b43 = 1.2f;
static const float b51 = -11.0f /54.0f;
static const float b52 = 2.5f;
static const float b53 = -70.0f/27.0f;
static const float b54 = 35.0f/27.0f;
static const float b61 = 1631.0f/55296.0f;
static const float b62 = 175.0f/512.0f;
static const float b63 = 575.0f/13824.0f;
static const float b64 = 44275.0f/110592.0f;
static const float b65 = 253.0f/4096.0f;
static const float c1 = 37.0f/378.0f;
static const float c3 = 250.0f/621.0f;
static const float c4 = 125.0f/594.0f;
static const float c6 = 512.0f/1771.0f;
static const float dc5 = -277.0f/14336.0f;
static const float dc1 = c1 - 2825.0f/27648.0f;
static const float dc3 = c3 - 18575.0f/48384.0f;
static const float dc4 = c4 - 13525.0f/55296.0f;
static const float dc6 = c6 - 0.25f;
/*
** Get the current state
*/
Y0.Reset();
odesys->Get_State(Y0);
veclen = Y0.Count();
Y1.Resize(veclen);
/*
** make aliases to the work-vectors we need
*/
StateVectorClass & dydt = _WorkVector0;
StateVectorClass & ak2 = _WorkVector1;
StateVectorClass & ak3 = _WorkVector2;
StateVectorClass & ak4 = _WorkVector3;
StateVectorClass & ak5 = _WorkVector4;
StateVectorClass & ak6 = _WorkVector5;
StateVectorClass & ytmp = _WorkVector6;
StateVectorClass & yerr = _WorkVector7;
dydt.Resize(veclen);
ak2.Resize(veclen);
ak3.Resize(veclen);
ak4.Resize(veclen);
ak5.Resize(veclen);
ak6.Resize(veclen);
ytmp.Resize(veclen);
yerr.Resize(veclen);
// First step
odesys->Compute_Derivatives(0.0f,NULL,&dydt);
for (i=0;i<veclen;i++) {
ytmp[i] = Y0[i] + b21*dt*dydt[i];
}
// Second step
odesys->Compute_Derivatives(a2*dt, &ytmp, &ak2);
for (i=0; i<veclen; i++) {
ytmp[i] = Y0[i] + dt*(b31*dydt[i] + b32*ak2[i]);
}
// Third step
odesys->Compute_Derivatives(a3*dt, &ytmp, &ak3);
for (i=0; i<veclen; i++) {
ytmp[i] = Y0[i] + dt*(b41*dydt[i] + b42*ak2[i] + b43*ak3[i]);
}
// Fourth step
odesys->Compute_Derivatives(a4*dt, &ytmp, &ak4);
for (i=0; i<veclen; i++) {
ytmp[i] = Y0[i] + dt*(b51*dydt[i] + b52*ak2[i] + b53*ak3[i] + b54*ak4[i]);
}
// Fifth step
odesys->Compute_Derivatives(a5*dt, &ytmp, &ak5);
for (i=0; i<veclen; i++) {
ytmp[i] = Y0[i] + dt*(b61*dydt[i] + b62*ak2[i] + b63*ak3[i] + b64*ak4[i] + b65*ak5[i]);
}
// Sixth step
odesys->Compute_Derivatives(a6*dt, &ytmp, &ak6);
for (i=0; i<veclen; i++) {
Y1[i] = Y0[i] + dt*(c1*dydt[i] + c3*ak3[i] + c4*ak4[i] + c6*ak6[i]);
}
// Error approximation!
// (maybe I should use this someday? nah not going to use this integrator anyway...)
for (i=0; i<veclen; i++) {
yerr[i] = dt*(dc1*dydt[i] + dc3*ak3[i] + dc4*ak4[i] + dc5*ak5[i] + dc6*ak6[i]);
}
odesys->Set_State(Y1);
}

View File

@@ -0,0 +1,134 @@
/*
** Command & Conquer Generals Zero Hour(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/>.
*/
/* $Header: /G/wwmath/ode.h 9 9/21/99 5:54p Neal_k $ */
/***********************************************************************************************
*** Confidential - Westwood Studios ***
***********************************************************************************************
* *
* Project Name : Commando *
* *
* $Archive:: /G/wwmath/ode.h $*
* *
* Author:: Greg_h *
* *
* $Modtime:: 9/21/99 5:54p $*
* *
* $Revision:: 9 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef ODE_H
#define ODE_H
#include "always.h"
#include "vector.h"
#include "wwdebug.h"
/*
** StateVectorClass
** The state vector for a system of ordinary differential equations will be
** stored in this form. It is a dynamically resizeable array so that we don't
** have to hard-code a maximum size. If needed, in the final product, we could
** do a slight optimization which makes this a normal fixed size array that
** we've determined is "big enough".
*/
class StateVectorClass : public DynamicVectorClass<float>
{
public:
void Reset(void) { ActiveCount = 0; }
void Resize(int size) { if (size > VectorMax) { DynamicVectorClass<float>::Resize(size); } }
};
/*
** ODESystemClass
** If a system of Ordinary Differential Equations (ODE's) are put behind an interface
** of this type, they can be integrated using the Integrators defined in this module.
*/
class ODESystemClass
{
public:
/*
** Get_Current_State
** This function should fill the given state vector with the
** current state of this object. Each state variable should be
** inserted into the vector using its 'Add' interface.
*/
virtual void Get_State(StateVectorClass & set_state) = 0;
/*
** Set_Current_State
** This function should read its state from this state vector starting from the
** given index. The return value should be the index that the next object should
** read from (i.e. increment the index past your state)
*/
virtual int Set_State(const StateVectorClass & new_state,int start_index = 0) = 0;
/*
** Compute_Derivatives
** The various ODE solvers will use this interface to ask the ODESystemClass to
** compute the derivatives of their state. In some cases, the integrator will
** pass in a new state vector (test_state) to be used when computing the derivatives.
** NULL will be passed if they want the derivatives for the initial state.
** This function works similarly to the Set_State function in that it passes you
** the index to start reading from and you pass it back the index to continue from.
*/
virtual int Compute_Derivatives(float t,StateVectorClass * test_state,StateVectorClass * dydt,int start_index = 0) = 0;
};
/*
** IntegrationSystem
**
** The Euler_Solve is the simplest but most inaccurate. It requires only
** a single computation of the derivatives per timestep.
**
** The Midpoint_Solve function will evaluate the derivatives at two points
**
** The Runge_Kutta_Solve requires four evaluations of the derivatives.
** This is the fourth order Runge-Kutta method...
**
** Runge_Kutta5_Solve is an implementation of fifth order Runge-
** Kutta. It requires six evaluations of the derivatives.
*/
class IntegrationSystem
{
public:
static void Euler_Integrate(ODESystemClass * sys,float dt);
static void Midpoint_Integrate(ODESystemClass * sys,float dt);
static void Runge_Kutta_Integrate(ODESystemClass * sys,float dt);
static void Runge_Kutta5_Integrate(ODESystemClass * odesys,float dt);
};
#endif

View File

@@ -0,0 +1,250 @@
/*
** Command & Conquer Generals Zero Hour(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/>.
*/
/* $Header: /Commando/Code/wwmath/plane.h 16 5/05/01 5:48p Jani_p $ */
/***********************************************************************************************
*** Confidential - Westwood Studios ***
***********************************************************************************************
* *
* Project Name : Voxel Technology *
* *
* File Name : PLANE.H *
* *
* Programmer : Greg Hjelstrom *
* *
* Start Date : 03/17/97 *
* *
* Last Update : March 17, 1997 [GH] *
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef PLANE_H
#define PLANE_H
#include "always.h"
#include "vector3.h"
#include "sphere.h"
/*
** PlaneClass
**
** 3D-planes. This class uses the Normal+Distance description of a plane.
** The relationship for all points (p) on the plane is given by:
**
** N.X * p.X + N.Y * p.Y + N.Z * p.Z = D
**
** BEWARE, if you are used to the Ax + By + Cz + D = 0 description, the
** sign of the D value is inverted.
*/
class PlaneClass
{
public:
enum { FRONT = 0, BACK, ON };
Vector3 N; // Normal of the plane
float D; // Distance along the normal from the origin
PlaneClass(void) : N(0.0f,0.0f,1.0f), D(0.0f) { }
/*
** Plane initialization:
** a,b,c,d - explicitly set the four coefficients (note the sign of d!)
** normal,dist - explicitly set the normal and distance
** normal,point - compute plane with normal, containing point
** p1,p2,p3 - compute plane containing three points
*/
PlaneClass(float nx,float ny,float nz,float dist);
PlaneClass(const Vector3 & normal,float dist);
PlaneClass(const Vector3 & normal,const Vector3 & point);
PlaneClass(const Vector3 & point1,const Vector3 & point2,const Vector3 & point3);
inline void Set(float a,float b,float c,float d);
inline void Set(const Vector3 & normal,float dist);
inline void Set(const Vector3 & normal,const Vector3 & point);
inline void Set(const Vector3 & point1,const Vector3 & point2,const Vector3 & point3);
bool Compute_Intersection(const Vector3 & p0,const Vector3 & p1,float * set_t) const;
bool In_Front(const Vector3 & point) const;
bool In_Front(const SphereClass & sphere) const;
bool In_Front_Or_Intersecting(const SphereClass & sphere) const;
static void Intersect_Planes(const PlaneClass & a, const PlaneClass & b, Vector3 *line_dir, Vector3 *line_point);
};
inline PlaneClass::PlaneClass(float nx,float ny,float nz,float dist)
{
Set(nx,ny,nz,dist);
}
inline PlaneClass::PlaneClass(const Vector3 & normal,float dist)
{
Set(normal,dist);
}
inline PlaneClass::PlaneClass(const Vector3 & normal,const Vector3 & point)
{
Set(normal,point);
}
inline PlaneClass::PlaneClass(const Vector3 & point1, const Vector3 & point2, const Vector3 & point3)
{
Set(point1,point2,point3);
}
inline void PlaneClass::Set(float a,float b,float c,float d)
{
N.X = a;
N.Y = b;
N.Z = c;
D = d;
}
inline void PlaneClass::Set(const Vector3 & normal,float dist)
{
N = normal;
D = dist;
}
inline void PlaneClass::Set(const Vector3 & normal,const Vector3 & point)
{
N = normal;
D = Vector3::Dot_Product(normal , point);
}
inline void PlaneClass::Set(const Vector3 & point1, const Vector3 & point2, const Vector3 & point3)
{
#ifdef ALLOW_TEMPORARIES
N = Vector3::Cross_Product((point2 - point1), (point3 - point1));
#else
Vector3::Cross_Product((point2 - point1), (point3 - point1), &N);
#endif
if (N != Vector3(0.0f, 0.0f, 0.0f)) {
// Points are not colinear. Normalize N and calculate D.
N.Normalize();
D = Vector3::Dot_Product(N, point1);
} else {
// They are colinear - return default plane (constructors can't fail).
N = Vector3(0.0f, 0.0f, 1.0f);
D = 0.0f;
}
}
inline bool PlaneClass::Compute_Intersection(const Vector3 & p0,const Vector3 & p1,float * set_t) const
{
float num,den;
den = Vector3::Dot_Product(N,p1-p0);
/*
** If the denominator is zero, the ray is parallel to the plane
*/
if (den == 0.0f) {
return false;
}
num = -(Vector3::Dot_Product(N,p0) - D);
*set_t = num/den;
/*
** If t is not between 0 and 1, the line containing the segment intersects
** the plane but the segment does not
*/
if ((*set_t < 0.0f) || (*set_t > 1.0f)) {
return false;
}
return true;
}
inline bool PlaneClass::In_Front(const Vector3 & point) const
{
float dist = Vector3::Dot_Product(point,N);
return (dist > D);
}
// This function returns true if the sphere is in front of the plane.
inline bool PlaneClass::In_Front(const SphereClass & sphere) const
{
float dist = Vector3::Dot_Product(sphere.Center,N);
return ((dist - D) >= sphere.Radius);
}
// This function will return 1 if any part of the sphere is in front of the plane.
// (i.e. if the sphere is entirely in front of the plane or if it intersects the plane).
inline bool PlaneClass::In_Front_Or_Intersecting(const SphereClass & sphere) const
{
float dist = Vector3::Dot_Product(sphere.Center , N);
return ((D - dist) < sphere.Radius);
}
inline void PlaneClass::Intersect_Planes(const PlaneClass & a, const PlaneClass & b, Vector3 *line_dir, Vector3 *line_point)
{
// Method used is from "plane-to-plane intersection", Graphics Gems III, pp. 233-235.
// Find line of intersection. First find direction vector of line:
Vector3::Cross_Product(a.N, b.N, line_dir);
// Now find point on line. How we do it depends on what the largest coordinate of the
// direction vector is.
Vector3 abs_dir = *line_dir;
abs_dir.Update_Max(-abs_dir);
if (abs_dir.X > abs_dir.Y) {
if (abs_dir.X > abs_dir.Z) {
// X largest
float ool = 1.0f / line_dir->X;
line_point->Y = (b.N.Z * a.D - a.N.Z * b.D) * ool;
line_point->Z = (a.N.Y * b.D - b.N.Y * a.D) * ool;
line_point->X = 0.0f;
} else {
// Z largest
float ool = 1.0f / line_dir->Z;
line_point->X = (b.N.Y * a.D - a.N.Y * b.D) * ool;
line_point->Y = (a.N.X * b.D - b.N.X * a.D) * ool;
line_point->Z = 0.0f;
}
} else {
if (abs_dir.Y > abs_dir.Z) {
// Y largest
float ool = 1.0f / line_dir->Y;
line_point->Z = (b.N.X * a.D - a.N.X * b.D) * ool;
line_point->X = (a.N.Z * b.D - b.N.Z * a.D) * ool;
line_point->Y = 0.0f;
} else {
// Z largest
float ool = 1.0f / line_dir->Z;
line_point->X = (b.N.Y * a.D - a.N.Y * b.D) * ool;
line_point->Y = (a.N.X * b.D - b.N.X * a.D) * ool;
line_point->Z = 0.0f;
}
}
// Normalize direction vector (we do it here because we needed the non-normalized version to
// find the point).
line_dir->Normalize();
}
#endif /*PLANE_H*/

View File

@@ -0,0 +1,118 @@
/*
** Command & Conquer Generals Zero Hour(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 : G *
* *
* $Archive:: /G/ww3d/POT.CPP $*
* *
* $Author:: Naty_h $*
* *
* $Modtime:: 12/23/98 7:58a $*
* *
* $Revision:: 3 $*
* *
*-------------------------------------------------------------------------*
* Functions: *
* Find_POT -- finds closest inclusive power of 2 to a value *
* Find_POT_Log2 -- finds log2 of closest inclusive power of 2 to a value*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "pot.h"
/**************************************************************************
* Find_POT -- finds closest inclusive power of 2 to a value *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 10/20/1997 PWG : Created. *
*========================================================================*/
int Find_POT(int val)
{
// clear out the recorded position and the recorded count
int recpos = 0;
int reccnt = 0;
// walk through the value shifting off bits and record the
// position of the highest bit, and whether we have found
// more than one bit.
for (int lp = 0; val; lp++) {
if (val & 1) {
recpos = lp;
reccnt++;
}
val >>= 1;
}
// if we have not found more than one bit then the number
// was the power of two so return it.
if (reccnt < 2) {
return( 1 << recpos);
}
// if we found more than one bit, then the number needs to
// be rounded up to the next highest power of 2.
return( 1 << (recpos + 1));
}
/**************************************************************************
* Find_POT_Log2 -- finds log2 of closest inclusive power of 2 to a value *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 12/23/1998 NH : Created. *
*========================================================================*/
unsigned int Find_POT_Log2(unsigned int val)
{
// clear out the recorded position and the recorded count
int recpos = 0;
int reccnt = 0;
// walk through the value shifting off bits and record the
// position of the highest bit, and whether we have found
// more than one bit.
for (int lp = 0; val; lp++) {
if (val & 1) {
recpos = lp;
reccnt++;
}
val >>= 1;
}
// if we have not found more than one bit then the number
// was the power of two so return it.
if (reccnt < 2) {
return recpos;
}
// if we found more than one bit, then the number needs to
// be rounded up to the next highest power of 2.
return recpos + 1;
}

View File

@@ -0,0 +1,45 @@
/*
** Command & Conquer Generals Zero Hour(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 : G *
* *
* $Archive:: /G/ww3d/POT.H $*
* *
* $Author:: Naty_h $*
* *
* $Modtime:: 12/23/98 7:51a $*
* *
* $Revision:: 4 $*
* *
*-------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef __POT_H__
#define __POT_H__
int Find_POT(int val);
unsigned int Find_POT_Log2(unsigned int val);
#endif

View File

@@ -0,0 +1,895 @@
/*
** Command & Conquer Generals Zero Hour(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/>.
*/
/* $Header: /Commando/Code/wwmath/quat.cpp 38 8/28/01 10:26a Greg_h $ */
/***********************************************************************************************
*** Confidential - Westwood Studios ***
***********************************************************************************************
* *
* Project Name : Voxel Technology *
* *
* File Name : QUAT.CPP *
* *
* Programmer : Greg Hjelstrom *
* *
* Start Date : 02/24/97 *
* *
* Last Update : February 28, 1997 [GH] *
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* Quaternion::Quaternion -- constructor *
* Quaternion::Set -- Set the quaternion *
* Quaternion::operator= -- Assignment operator *
* Quaternion::Make_Closest -- Use nearest representation to the given quaternion. *
* Trackball -- Computes a "trackball" quaternion given 2D mouse coordinates *
* Axis_To_Quat -- Creates a quaternion given an axis and angle of rotation *
* Slerp -- Spherical Linear interpolation! *
* Build_Quaternion -- Creates a quaternion from a Matrix *
* Build_Matrix -- Creates a Matrix from a Quaternion *
* Normalize -- normalizes a quaternion *
* Quaternion::Quaternion -- constructor *
* Slerp_Setup -- Get ready to call "Cached_Slerp" *
* Cached_Slerp -- Quaternion slerping, optimized with cached values *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "quat.h"
#include "matrix3d.h"
#include "matrix4.h"
#include "wwmath.h"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#define SLERP_EPSILON 0.001
static int _nxt[3] = { 1 , 2 , 0 };
// ------------------------------------------------------------
// local functions
// ------------------------------------------------------------
static float project_to_sphere(float,float,float);
/***********************************************************************************************
* Quaternion::Quaternion -- constructor *
* *
* constructs a quaternion from the given axis and angle of rotation (in RADIANS of course) *
* *
* INPUT: *
* axis - axis of the rotation *
* angle - rotation angle *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 12/10/97 GTH : Created. *
*=============================================================================================*/
Quaternion::Quaternion(const Vector3 & axis,float angle)
{
float s = WWMath::Sin(angle/2);
float c = WWMath::Cos(angle/2);
X = s * axis.X;
Y = s * axis.Y;
Z = s * axis.Z;
W = c;
}
/***********************************************************************************************
* Quaternion::Normalize -- Normalize to a unit quaternion *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 02/24/1997 GH : Created. *
*=============================================================================================*/
void Quaternion::Normalize()
{
float len2=X * X + Y * Y + Z * Z + W * W;
if (0.0f == len2) {
return;
} else {
float inv_mag = WWMath::Inv_Sqrt(len2);
X *= inv_mag;
Y *= inv_mag;
Z *= inv_mag;
W *= inv_mag;
}
}
/***********************************************************************************************
* Q::Make_Closest -- Use nearest representation to the given quaternion. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 02/28/1997 GH : Created. *
*=============================================================================================*/
Quaternion & Quaternion::Make_Closest(const Quaternion & qto)
{
float cos_t = qto.X * X + qto.Y * Y + qto.Z * Z + qto.W * W;
// if we are on opposite hemisphere from qto, negate ourselves
if (cos_t < 0.0) {
X = -X;
Y = -Y;
Z = -Z;
W = -W;
}
return *this;
}
/***********************************************************************************************
* Trackball -- Computes a "trackball" quaternion given 2D mouse coordinates *
* *
* INPUT: *
* x0,y0 - x1,y1 - "normalized" mouse coordinates for the mouse movement *
* sphsize - size of the trackball sphere *
* *
* OUTPUT: *
* a quaternion representing the rotation of a trackball *
* *
* WARNINGS: *
* *
* HISTORY: *
* 02/28/1997 GH : Created. *
*=============================================================================================*/
Quaternion Trackball(float x0, float y0, float x1, float y1, float sphsize)
{
Vector3 a;
Vector3 p1;
Vector3 p2;
Vector3 d;
float phi,t;
if ((x0 == x1) && (y0 == y1)) {
return Quaternion(0.0f, 0.0f, 0.0f, 1.0f); // Zero rotation
}
// Compute z coordinates for projection of p1 and p2 to
// deformed sphere
p1[0] = x0;
p1[1] = y0;
p1[2] = project_to_sphere(sphsize, x0, y0);
p2[0] = x1;
p2[1] = y1;
p2[2] = project_to_sphere(sphsize, x1, y1);
// Find their cross product
Vector3::Cross_Product(p2,p1,&a);
// Compute how much to rotate
d = p1 - p2;
t = d.Length() / (2.0f * sphsize);
// Avoid problems with out of control values
if (t > 1.0f) t = 1.0f;
if (t < -1.0f) t = -1.0f;
phi = 2.0f * WWMath::Asin(t);
return Axis_To_Quat(a, phi);
}
/***********************************************************************************************
* Axis_To_Quat -- Creates a quaternion given an axis and angle of rotation *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 02/28/1997 GH : Created. *
*=============================================================================================*/
Quaternion Axis_To_Quat(const Vector3 &a, float phi)
{
Quaternion q;
Vector3 tmp = a;
tmp.Normalize();
q[0] = tmp[0];
q[1] = tmp[1];
q[2] = tmp[2];
q.Scale(WWMath::Sin(phi / 2.0f));
q[3] = WWMath::Cos(phi / 2.0f);
return q;
}
/***********************************************************************************************
* Slerp -- Spherical Linear interpolation! *
* *
* INPUT: *
* p - start quaternion *
* q - end quaternion *
* alpha - interpolating parameter *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 02/28/1997 GH : Created. *
*=============================================================================================*/
#if 0
#pragma warning (disable : 4725)
#define ARC_TABLE_SIZE_MASK 1023
#define SIN_TABLE_SIZE_MASK 1023
void __cdecl Fast_Slerp(Quaternion& res, const Quaternion & p,const Quaternion & q,float alpha)
{
float float_epsilon2=WWMATH_EPSILON * WWMATH_EPSILON;
float HalfOfArcTableSize=float(ARC_TABLE_SIZE/2);
float HalfOfSinTableSize=float(SIN_TABLE_SIZE/2);
const unsigned ARC_TABLE_SIZE_PER_2=ARC_TABLE_SIZE/2;
float beta; // complementary interploation parameter
float theta; // angle between p and q
__asm {
mov esi, p
mov edi, q
fld1 // we'll need 1.0 and 0.0 later
// ----------------------------------------------------------------------------
// cos theta = dot product of p and q
// cos_t = p.X * q.X + p.Y * q.Y + p.Z * q.Z + p.W * q.W;
// if q is on opposite hemisphere from A, use -B instead
// if (cos_t < 0.0) {
// cos_t = -cos_t;
// qflip = true;
// }
// else {
// qflip = false;
// }
// ----------------------------------------------------------------------------
fld dword ptr [esi] // p.X
fmul dword ptr [edi] // p.X*q.X
fld dword ptr [esi+08h] // p.Y
fmul dword ptr [edi+08h] // p.Y*q.Y
fld dword ptr [esi+04h] // p.Z
fmul dword ptr [edi+04h] // p.Z*q.Z
fld dword ptr [edi+0ch] // p.W
fmul dword ptr [esi+0ch] // p.W*q.W
faddp st(2), st(0) // y+=w
faddp st(2), st(0) // x+=z
faddp st(1),st(0) // x+z + y+w
fst beta
fabs
mov ebx,beta
and ebx,0x80000000
// ----------------------------------------------------------------------------
// if q is very close to p, just linearly interpolate
// between the two.
// if (1.0 - cos_t < WWMATH_EPSILON * WWMATH_EPSILON) {
// beta = 1.0 - alpha;
// }
// ----------------------------------------------------------------------------
fld st(0) // duplicate st(0), which contains cos_t
fsubr st(0),st(2) // st(2) contains 1.0
fcomp float_epsilon2
fnstsw ax
test ah, 01h
je normal_slerp
fld alpha
fsubr st(0),st(1) // st(1) contains 1.0
fstp beta
jmp done_slerp
normal_slerp:
// ----------------------------------------------------------------------------
// normal slerp!
// else {
// theta = WWMath::Acos(cos_t);
// sin_t = WWMath::Sin(theta);
// oo_sin_t = 1.0 / sin_t;
// beta = WWMath::Sin(theta - alpha*theta) * oo_sin_t;
// alpha = WWMath::Sin(alpha*theta) * oo_sin_t;
// }
// if (qflip) {
// alpha = -alpha;
// }
// ----------------------------------------------------------------------------
fld HalfOfSinTableSize
fld HalfOfArcTableSize
fmul st(0),st(2) // cos_t * (ARC_TABLE_SIZE/2)
fistp theta // convert to integer
mov eax,theta
add eax,ARC_TABLE_SIZE_PER_2
jns no_neg
xor eax,eax
jmp contin
no_neg:
cmp eax,ARC_TABLE_SIZE
jl contin // Note: Use Setcc/Movcc here!!!
mov eax,ARC_TABLE_SIZE_MASK
contin:
fld dword ptr[_FastAcosTable+eax*4]
fst theta
fmul st(0),st(1) // theta * (sin_table_size/2)
fadd st(0),st(1) // theta * (sin_table_size/2) + (sin_table_size/2)
fistp beta // conver to integer
mov ecx,SIN_TABLE_SIZE_MASK
mov eax,beta
and eax,ecx // & SIN_TABLE_SIZE_MASK
fld dword ptr[_FastInvSinTable+eax*4] // 1.0f/sin(theta)
fld theta
fmul alpha // theta*alpha
fld st(0) // duplicate stack head as we need theta*alpha later
fsubr theta // theta-theta*alpha
fmul st(0),st(3) // (theta-theta*alpha)*HalfOfSinTableSize
fadd st(0),st(3) // (theta-theta*alpha)*HalfOfSinTableSize+HalfOfSinTableSize
fistp beta // convert to integer
mov eax,beta
and eax,ecx // & SIN_TABLE_SIZE_MASK
fld dword ptr[_FastSinTable+eax*4] // sin(theta-theta*alpha)
fmul st(0),st(2) // sin(theta-theta*alpha) * oo_sin_t
fstp beta
fmul st(0),st(2) // (theta*alpha)*HalfOfSinTableSize
fadd st(0),st(2) // (theta*alpha)*HalfOfSinTableSize+HalfOfSinTableSize
fistp theta // convert to integer
mov eax,theta
and eax,ecx // & SIN_TABLE_SIZE_MASK
fld dword ptr[_FastSinTable+eax*4] // sin(theta*alpha)
fmul st(0),st(1) // oo_sin_t
fstp alpha
fstp st(0) // pop oo_sin_t
fstp st(0) // pop HalfOfSinTableSize
done_slerp:
test ebx, ebx
je no_negative
fld alpha
fchs
fstp alpha
no_negative:
// ----------------------------------------------------------------------------
// res.X = beta*p.X + alpha*q.X;
// res.Y = beta*p.Y + alpha*q.Y;
// res.Z = beta*p.Z + alpha*q.Z;
// res.W = beta*p.W + alpha*q.W;
// ----------------------------------------------------------------------------
fstp st(0) // pop cos_t
fstp st(0) // pop 1.0
fld alpha
fld dword ptr [edi+4] // q.Y
fmul st(0),st(1) // alpha*q.Y
fld dword ptr [edi+8] // q.Z
fmul st(0),st(2) // alpha*q.Z
fld dword ptr [edi+12] // q.W
fmul st(0),st(3) // alpha*q.W
fld dword ptr [edi] // q.X
fmulp st(4),st // alpha*q.X
fld beta
fld dword ptr [esi+4] // p.Y
fmul st(0),st(1) // beta*p.Y
fld dword ptr [esi+8] // p.Z
fmul st(0),st(2) // beta*p.Z
fld dword ptr [esi] // p.X
fmul st(0),st(3) // beta*p.X
fxch st(3) // move beta to top of stack
fmul dword ptr [esi+12] // beta*p.W
faddp st(4),st // w
faddp st(4),st // z
faddp st(4),st // y
faddp st(4),st // x
mov ecx, res
fstp [ecx+12] // w
fstp [ecx+8] // z
fstp [ecx+4] // y
fstp [ecx] // x
}
}
#else
void __cdecl Fast_Slerp(Quaternion& res, const Quaternion & p,const Quaternion & q,float alpha)
{
float beta; // complementary interploation parameter
float theta; // angle between p and q
float cos_t; // sine, cosine of theta
float oo_sin_t;
int qflip; // use flip of q?
// cos theta = dot product of p and q
cos_t = p.X * q.X + p.Y * q.Y + p.Z * q.Z + p.W * q.W;
// if q is on opposite hemisphere from A, use -B instead
if (cos_t < 0.0f) {
cos_t = -cos_t;
qflip = true;
} else {
qflip = false;
}
if (1.0f - cos_t < WWMATH_EPSILON * WWMATH_EPSILON) {
// if q is very close to p, just linearly interpolate
// between the two.
beta = 1.0f - alpha;
} else {
theta = WWMath::Fast_Acos(cos_t);
float sin_t = WWMath::Fast_Sin(theta);
oo_sin_t = 1.0f / sin_t;
beta = WWMath::Fast_Sin(theta - alpha*theta) * oo_sin_t;
alpha = WWMath::Fast_Sin(alpha*theta) * oo_sin_t;
}
if (qflip) {
alpha = -alpha;
}
res.X = beta*p.X + alpha*q.X;
res.Y = beta*p.Y + alpha*q.Y;
res.Z = beta*p.Z + alpha*q.Z;
res.W = beta*p.W + alpha*q.W;
}
#endif // MSC_VER
void Slerp(Quaternion& res, const Quaternion & p,const Quaternion & q,float alpha)
{
float beta; // complementary interploation parameter
float theta; // angle between p and q
//float sin_t
float cos_t; // sine, cosine of theta
float oo_sin_t;
int qflip; // use flip of q?
// cos theta = dot product of p and q
cos_t = p.X * q.X + p.Y * q.Y + p.Z * q.Z + p.W * q.W;
// if q is on opposite hemisphere from A, use -B instead
if (cos_t < 0.0f) {
cos_t = -cos_t;
qflip = true;
} else {
qflip = false;
}
if (1.0f - cos_t < WWMATH_EPSILON * WWMATH_EPSILON) {
// if q is very close to p, just linearly interpolate
// between the two.
beta = 1.0f - alpha;
} else {
// normal slerp!
theta = WWMath::Acos(cos_t);
float sin_t = WWMath::Sin(theta);
oo_sin_t = 1.0f / sin_t;
beta = WWMath::Sin(theta - alpha*theta) * oo_sin_t;
alpha = WWMath::Sin(alpha*theta) * oo_sin_t;
}
if (qflip) {
alpha = -alpha;
}
res.X = beta*p.X + alpha*q.X;
res.Y = beta*p.Y + alpha*q.Y;
res.Z = beta*p.Z + alpha*q.Z;
res.W = beta*p.W + alpha*q.W;
}
/***********************************************************************************************
* Slerp_Setup -- Get ready to call "Cached_Slerp" *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 2/27/98 GTH : Created. *
*=============================================================================================*/
void Slerp_Setup(const Quaternion & p,const Quaternion & q,SlerpInfoStruct * slerpinfo)
{
float cos_t;
assert(slerpinfo != NULL);
// cos theta = dot product of p and q
cos_t = p.X * q.X + p.Y * q.Y + p.Z * q.Z + p.W * q.W;
// if q is on opposite hemisphere from A, use -B instead
if (cos_t < 0.0f) {
cos_t = -cos_t;
slerpinfo->Flip = true;
} else {
slerpinfo->Flip = false;
}
if (1.0f - cos_t < SLERP_EPSILON) {
slerpinfo->Linear = true;
slerpinfo->Theta = 0.0f;
slerpinfo->SinT = 0.0f;
} else {
slerpinfo->Linear = false;
slerpinfo->Theta = WWMath::Acos(cos_t);
slerpinfo->SinT = WWMath::Sin(slerpinfo->Theta);
}
}
/***********************************************************************************************
* Cached_Slerp -- Quaternion slerping, optimized with cached values *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 2/27/98 GTH : Created. *
*=============================================================================================*/
Quaternion Cached_Slerp(const Quaternion & p,const Quaternion & q,float alpha,SlerpInfoStruct * slerpinfo)
{
float beta; // complementary interploation parameter
float oo_sin_t;
if (slerpinfo->Linear) {
// if q is very close to p, just linearly interpolate
// between the two.
beta = 1.0f - alpha;
} else {
// normal slerp!
oo_sin_t = 1.0f / slerpinfo->Theta;
beta = WWMath::Sin(slerpinfo->Theta - alpha*slerpinfo->Theta) * oo_sin_t;
alpha = WWMath::Sin(alpha*slerpinfo->Theta) * oo_sin_t;
}
if (slerpinfo->Flip) {
alpha = -alpha;
}
Quaternion res;
res.X = beta*p.X + alpha*q.X;
res.Y = beta*p.Y + alpha*q.Y;
res.Z = beta*p.Z + alpha*q.Z;
res.W = beta*p.W + alpha*q.W;
return res;
}
void Cached_Slerp(const Quaternion & p,const Quaternion & q,float alpha,SlerpInfoStruct * slerpinfo,Quaternion * set_q)
{
float beta; // complementary interploation parameter
float oo_sin_t;
if (slerpinfo->Linear) {
// if q is very close to p, just linearly interpolate
// between the two.
beta = 1.0f - alpha;
} else {
// normal slerp!
oo_sin_t = 1.0f / slerpinfo->Theta;
beta = WWMath::Sin(slerpinfo->Theta - alpha*slerpinfo->Theta) * oo_sin_t;
alpha = WWMath::Sin(alpha*slerpinfo->Theta) * oo_sin_t;
}
if (slerpinfo->Flip) {
alpha = -alpha;
}
set_q->X = beta*p.X + alpha*q.X;
set_q->Y = beta*p.Y + alpha*q.Y;
set_q->Z = beta*p.Z + alpha*q.Z;
set_q->W = beta*p.W + alpha*q.W;
}
/***********************************************************************************************
* Build_Quaternion -- Creates a quaternion from a Matrix *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* Matrix MUST NOT have scaling! *
* *
* HISTORY: *
* 02/28/1997 GH : Created. *
*=============================================================================================*/
Quaternion Build_Quaternion(const Matrix3D & mat)
{
float tr,s;
int i,j,k;
Quaternion q;
// sum the diagonal of the rotation matrix
tr = mat[0][0] + mat[1][1] + mat[2][2];
if (tr > 0.0f) {
s = sqrt(tr + 1.0);
q[3] = s * 0.5;
s = 0.5 / s;
q[0] = (mat[2][1] - mat[1][2]) * s;
q[1] = (mat[0][2] - mat[2][0]) * s;
q[2] = (mat[1][0] - mat[0][1]) * s;
} else {
i=0;
if (mat[1][1] > mat[0][0]) i = 1;
if (mat[2][2] > mat[i][i]) i = 2;
j = _nxt[i];
k = _nxt[j];
s = sqrt((mat[i][i] - (mat[j][j] + mat[k][k])) + 1.0);
q[i] = s * 0.5;
if (s != 0.0) {
s = 0.5 / s;
}
q[3] = ( mat[k][j] - mat[j][k] ) * s;
q[j] = ( mat[j][i] + mat[i][j] ) * s;
q[k] = ( mat[k][i] + mat[i][k] ) * s;
}
return q;
}
Quaternion Build_Quaternion(const Matrix3x3 & mat)
{
float tr,s;
int i,j,k;
Quaternion q;
// sum the diagonal of the rotation matrix
tr = mat[0][0] + mat[1][1] + mat[2][2];
if (tr > 0.0) {
s = sqrt(tr + 1.0);
q[3] = s * 0.5;
s = 0.5 / s;
q[0] = (mat[2][1] - mat[1][2]) * s;
q[1] = (mat[0][2] - mat[2][0]) * s;
q[2] = (mat[1][0] - mat[0][1]) * s;
} else {
i = 0;
if (mat[1][1] > mat[0][0]) i = 1;
if (mat[2][2] > mat[i][i]) i = 2;
j = _nxt[i];
k = _nxt[j];
s = sqrt( (mat[i][i] - (mat[j][j]+mat[k][k])) + 1.0);
q[i] = s * 0.5;
if (s != 0.0) {
s = 0.5/s;
}
q[3] = ( mat[k][j] - mat[j][k] ) * s;
q[j] = ( mat[j][i] + mat[i][j] ) * s;
q[k] = ( mat[k][i] + mat[i][k] ) * s;
}
return q;
}
Quaternion Build_Quaternion(const Matrix4x4 & mat)
{
float tr,s;
int i,j,k;
Quaternion q;
// sum the diagonal of the rotation matrix
tr = mat[0][0] + mat[1][1] + mat[2][2];
if (tr > 0.0) {
s = sqrt(tr + 1.0);
q[3] = s * 0.5;
s = 0.5 / s;
q[0] = (mat[2][1] - mat[1][2]) * s;
q[1] = (mat[0][2] - mat[2][0]) * s;
q[2] = (mat[1][0] - mat[0][1]) * s;
} else {
i = 0;
if (mat[1][1] > mat[0][0]) i = 1;
if (mat[2][2] > mat[i][i]) i = 2;
j = _nxt[i];
k = _nxt[j];
s = sqrt( (mat[i][i] - (mat[j][j]+mat[k][k])) + 1.0);
q[i] = s * 0.5;
if (s != 0.0) {
s = 0.5/s;
}
q[3] = ( mat[k][j] - mat[j][k] ) * s;
q[j] = ( mat[j][i] + mat[i][j] ) * s;
q[k] = ( mat[k][i] + mat[i][k] ) * s;
}
return q;
}
/***********************************************************************************************
* Build_Matrix -- Creates a Matrix from a Quaternion *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 02/28/1997 GH : Created. *
*=============================================================================================*/
Matrix3x3 Build_Matrix3(const Quaternion & q)
{
Matrix3x3 m;
m[0][0] = (float)(1.0 - 2.0 * (q[1] * q[1] + q[2] * q[2]));
m[0][1] = (float)(2.0 * (q[0] * q[1] - q[2] * q[3]));
m[0][2] = (float)(2.0 * (q[2] * q[0] + q[1] * q[3]));
m[1][0] = (float)(2.0 * (q[0] * q[1] + q[2] * q[3]));
m[1][1] = (float)(1.0 - 2.0f * (q[2] * q[2] + q[0] * q[0]));
m[1][2] = (float)(2.0 * (q[1] * q[2] - q[0] * q[3]));
m[2][0] = (float)(2.0 * (q[2] * q[0] - q[1] * q[3]));
m[2][1] = (float)(2.0 * (q[1] * q[2] + q[0] * q[3]));
m[2][2] =(float)(1.0 - 2.0 * (q[1] * q[1] + q[0] * q[0]));
return m;
}
Matrix4x4 Build_Matrix4(const Quaternion & q)
{
Matrix4x4 m;
// initialize the rotation sub-matrix
m[0][0] = (float)(1.0 - 2.0 * (q[1] * q[1] + q[2] * q[2]));
m[0][1] = (float)(2.0 * (q[0] * q[1] - q[2] * q[3]));
m[0][2] = (float)(2.0 * (q[2] * q[0] + q[1] * q[3]));
m[1][0] = (float)(2.0 * (q[0] * q[1] + q[2] * q[3]));
m[1][1] = (float)(1.0 - 2.0f * (q[2] * q[2] + q[0] * q[0]));
m[1][2] = (float)(2.0 * (q[1] * q[2] - q[0] * q[3]));
m[2][0] = (float)(2.0 * (q[2] * q[0] - q[1] * q[3]));
m[2][1] = (float)(2.0 * (q[1] * q[2] + q[0] * q[3]));
m[2][2] = (float)(1.0 - 2.0 * (q[1] * q[1] + q[0] * q[0]));
// no translation
m[0][3] = m[1][3] = m[2][3] = 0.0f;
// last row
m[3][0] = m[3][1] = m[3][2] = 0.0f;
m[3][3] = 1.0f;
return m;
}
void Quaternion::Rotate_X(float theta)
{
// TODO: optimize this
*this = (*this) * Quaternion(Vector3(1,0,0),theta);
}
void Quaternion::Rotate_Y(float theta)
{
// TODO: optimize this
*this = (*this) * Quaternion(Vector3(0,1,0),theta);
}
void Quaternion::Rotate_Z(float theta)
{
// TODO: optimize this
*this = (*this) * Quaternion(Vector3(0,0,1),theta);
}
float project_to_sphere(float r, float x, float y)
{
const float SQRT2 = 1.41421356f;
float t, z;
float d = WWMath::Sqrt(x * x + y * y);
if (d < r * (SQRT2/(2.0f))) // inside sphere
z = WWMath::Sqrt(r * r - d * d);
else { // on hyperbola
t = r / SQRT2;
z = t * t / d;
}
return z;
}
void Quaternion::Randomize(void)
{
X = ((float) (rand() & 0xFFFF)) / 65536.0f;
Y = ((float) (rand() & 0xFFFF)) / 65536.0f;
Z = ((float) (rand() & 0xFFFF)) / 65536.0f;
W = ((float) (rand() & 0xFFFF)) / 65536.0f;
Normalize();
}

View File

@@ -0,0 +1,317 @@
/*
** Command & Conquer Generals Zero Hour(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/>.
*/
/* $Header: /Commando/Code/wwmath/quat.h 29 5/11/01 7:11p Jani_p $ */
/***************************************************************************
*** Confidential - Westwood Studios ***
***************************************************************************
* *
* Project Name : Voxel Technology *
* *
* File Name : QUAT.H *
* *
* Programmer : Greg Hjelstrom *
* *
* Start Date : 02/24/97 *
* *
* Last Update : February 24, 1997 [GH] *
* *
*-------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef QUAT_H
#define QUAT_H
#include "always.h"
#include "wwmath.h"
#include "matrix3.h"
#include "vector3.h"
#include "matrix3d.h"
class Quaternion
{
private:
public:
// X,Y,Z are the imaginary parts of the quaterion
// W is the real part
float X;
float Y;
float Z;
float W;
public:
WWINLINE Quaternion(void) {};
WWINLINE explicit Quaternion(bool init) { if (init) { X = 0.0f; Y = 0.0f; Z = 0.0f; W = 1.0f; } }
WWINLINE explicit Quaternion(float a, float b, float c, float d) { X=a; Y=b; Z=c; W=d; }
WWINLINE explicit Quaternion(const Vector3 & axis,float angle);
WWINLINE Quaternion & operator=(const Quaternion & source);
WWINLINE void Set(float a = 0.0, float b = 0.0, float c = 0.0, float d = 1.0) { X = a; Y = b; Z = c; W = d; }
WWINLINE void Make_Identity(void) { Set(); };
WWINLINE void Scale(float s) { X = (float)(s*X); Y = (float)(s*Y); Z = (float)(s*Z); W = (float)(s*W); }
// Array access
WWINLINE float & operator [](int i) { return (&X)[i]; }
WWINLINE const float & operator [](int i) const { return (&X)[i]; }
// Unary operators.
// Remember that q and -q represent the same 3D rotation.
WWINLINE Quaternion operator-() const { return(Quaternion(-X,-Y,-Z,-W)); }
WWINLINE Quaternion operator+() const { return *this; }
// Every 3D rotation can be expressed by two different quaternions, This
// function makes the current quaternion convert itself to the representation
// which is closer on the 4D unit-hypersphere to the given quaternion.
Quaternion & Make_Closest(const Quaternion & qto);
// Square of the magnitude of the quaternion
WWINLINE float Length2(void) const { return (X*X + Y*Y + Z*Z + W*W); }
// Magnitude of the quaternion
WWINLINE float Length(void) const { return WWMath::Sqrt(Length2()); }
// Make the quaternion unit length
void Normalize(void);
// post-concatenate rotations about the coordinate axes
void Rotate_X(float theta);
void Rotate_Y(float theta);
void Rotate_Z(float theta);
// initialize this quaternion randomly (creates a random *unit* quaternion)
void Randomize(void);
// transform (rotate) a vector with this quaternion
WWINLINE Vector3 Rotate_Vector(const Vector3 & v) const;
WWINLINE void Rotate_Vector(const Vector3 & v,Vector3 * set_result) const;
// verify that none of the members of this quaternion are invalid floats
bool Is_Valid(void) const;
};
// Inverse of the quaternion (1/q)
WWINLINE Quaternion Inverse(const Quaternion & a)
{
return Quaternion(-a[0],-a[1],-a[2],a[3]);
}
// Conjugate of the quaternion
WWINLINE Quaternion Conjugate(const Quaternion & a)
{
return Quaternion(-a[0],-a[1],-a[2],a[3]);
}
// Add two quaternions
WWINLINE Quaternion operator + (const Quaternion & a,const Quaternion & b)
{
return Quaternion(a[0] + b[0], a[1] + b[1], a[2] + b[2], a[3] + b[3]);
}
// Subract two quaternions
WWINLINE Quaternion operator - (const Quaternion & a,const Quaternion & b)
{
return Quaternion(a[0] - b[0], a[1] - b[1], a[2] - b[2], a[3] - b[3]);
}
// Multiply a quaternion by a scalar:
WWINLINE Quaternion operator * (float scl, const Quaternion & a)
{
return Quaternion(scl*a[0], scl*a[1], scl*a[2], scl*a[3]);
}
// Multiply a quaternion by a scalar
WWINLINE Quaternion operator * (const Quaternion & a, float scl)
{
return scl*a;
}
// Multiply two quaternions
WWINLINE Quaternion operator * (const Quaternion & a,const Quaternion & b)
{
return Quaternion
(
a.W*b.X + b.W*a.X + (a.Y*b.Z - b.Y*a.Z),
a.W*b.Y + b.W*a.Y - (a.X*b.Z - b.X*a.Z),
a.W*b.Z + b.W*a.Z + (a.X*b.Y - b.X*a.Y),
a.W * b.W - (a.X * b.X + a.Y * b.Y + a.Z * b.Z)
);
}
// Divide two quaternions
WWINLINE Quaternion operator / (const Quaternion & a,const Quaternion & b)
{
return a * Inverse(b);
}
// Normalized version of the quaternion
WWINLINE Quaternion Normalize(const Quaternion & a)
{
float mag = a.Length();
if (0.0f == mag) {
return a;
} else {
float oomag = 1.0f / mag;
return Quaternion(a[0] * oomag, a[1] * oomag, a[2] * oomag, a[3] * oomag);
}
}
// This function computes a quaternion based on an axis
// (defined by the given Vector a) and an angle about
// which to rotate. The angle is expressed in radians.
Quaternion Axis_To_Quat(const Vector3 &a, float angle);
// Pass the x and y coordinates of the last and current position
// of the mouse, scaled so they are from -1.0 to 1.0
// The quaternion is the computed as the rotation of a trackball
// between the two points projected onto a sphere. This can
// be used to implement an intuitive viewing control system.
Quaternion Trackball(float x0, float y0, float x1, float y1, float sphsize);
// Spherical Linear interpolation of quaternions
//Quaternion Slerp(const Quaternion & a,const Quaternion & b,float t);
void __cdecl Slerp(Quaternion& result, const Quaternion & a,const Quaternion & b,float t);
// Fast slerp is innaccurate but multiple times faster
void __cdecl Fast_Slerp(Quaternion& result, const Quaternion & a,const Quaternion & b,float t);
// Convert a rotation matrix into a quaternion
Quaternion Build_Quaternion(const Matrix3x3 & matrix);
Quaternion Build_Quaternion(const Matrix3D & matrix);
Quaternion Build_Quaternion(const Matrix4x4 & matrix);
// Convert a quaternion into a rotation matrix
Matrix3x3 Build_Matrix3(const Quaternion & quat);
Matrix3D &Build_Matrix3D(const Quaternion & q, Matrix3D &out);
WWINLINE Matrix3D &Build_Matrix3D(const Quaternion & q, Matrix3D &out)
{
out[0][0] = (float)(1.0 - 2.0 * (q[1] * q[1] + q[2] * q[2]));
out[0][1] = (float)(2.0 * (q[0] * q[1] - q[2] * q[3]));
out[0][2] = (float)(2.0 * (q[2] * q[0] + q[1] * q[3]));
out[1][0] = (float)(2.0 * (q[0] * q[1] + q[2] * q[3]));
out[1][1] = (float)(1.0 - 2.0f * (q[2] * q[2] + q[0] * q[0]));
out[1][2] = (float)(2.0 * (q[1] * q[2] - q[0] * q[3]));
out[2][0] = (float)(2.0 * (q[2] * q[0] - q[1] * q[3]));
out[2][1] = (float)(2.0 * (q[1] * q[2] + q[0] * q[3]));
out[2][2] =(float)(1.0 - 2.0 * (q[1] * q[1] + q[0] * q[0]));
// no translation
out[0][3] = out[1][3] = out[2][3] = 0.0f;
return out;
}
Matrix4x4 Build_Matrix4(const Quaternion & quat);
// Some values can be cached if you are performing multiple slerps
// between the same two quaternions...
struct SlerpInfoStruct
{
float SinT;
float Theta;
bool Flip;
bool Linear;
};
// Cached slerp implementation
void Slerp_Setup(const Quaternion & p,const Quaternion & q,SlerpInfoStruct * slerpinfo);
void Cached_Slerp(const Quaternion & p,const Quaternion & q,float alpha,SlerpInfoStruct * slerpinfo,Quaternion * set_q);
Quaternion Cached_Slerp(const Quaternion & p,const Quaternion & q,float alpha,SlerpInfoStruct * slerpinfo);
WWINLINE Vector3 Quaternion::Rotate_Vector(const Vector3 & v) const
{
float x = W*v.X + (Y*v.Z - v.Y*Z);
float y = W*v.Y - (X*v.Z - v.X*Z);
float z = W*v.Z + (X*v.Y - v.X*Y);
float w = -(X*v.X + Y*v.Y + Z*v.Z);
return Vector3
(
w*(-X) + W*x + (y*(-Z) - (-Y)*z),
w*(-Y) + W*y - (x*(-Z) - (-X)*z),
w*(-Z) + W*z + (x*(-Y) - (-X)*y)
);
}
WWINLINE void Quaternion::Rotate_Vector(const Vector3 & v,Vector3 * result) const
{
assert(result != NULL);
float x = W*v.X + (Y*v.Z - v.Y*Z);
float y = W*v.Y - (X*v.Z - v.X*Z);
float z = W*v.Z + (X*v.Y - v.X*Y);
float w = -(X*v.X + Y*v.Y + Z*v.Z);
result->X = w*(-X) + W*x + (y*(-Z) - (-Y)*z);
result->Y = w*(-Y) + W*y - (x*(-Z) - (-X)*z);
result->Z = w*(-Z) + W*z + (x*(-Y) - (-X)*y);
}
WWINLINE bool Quaternion::Is_Valid(void) const
{
return ( WWMath::Is_Valid_Float(X) &&
WWMath::Is_Valid_Float(Y) &&
WWMath::Is_Valid_Float(Z) &&
WWMath::Is_Valid_Float(W) );
}
WWINLINE bool Equal_Within_Epsilon(const Quaternion &a, const Quaternion &b, float epsilon)
{
return( (WWMath::Fabs(a.X - b.X) < epsilon) &&
(WWMath::Fabs(a.Y - b.Y) < epsilon) &&
(WWMath::Fabs(a.Z - b.Z) < epsilon) &&
(WWMath::Fabs(a.W - b.W) < epsilon) );
}
/***********************************************************************************************
* Quaternion::operator= -- Assignment operator *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 02/24/1997 GH : Created. *
*=============================================================================================*/
WWINLINE Quaternion & Quaternion::operator = (const Quaternion & source)
{
X = source[0];
Y = source[1];
Z = source[2];
W = source[3];
return *this;
}
#endif /* QUAT_H */

View File

@@ -0,0 +1,108 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/rect.h $*
* *
* Author:: Byon Garrabrant *
* *
* $Modtime:: 4/16/01 10:01a $*
* *
* $Revision:: 9 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef RECT_H
#define RECT_H
#include "vector2.h"
class RectClass
{
public:
float Left;
float Top;
float Right;
float Bottom;
// Constructors
RectClass( void ) {};
RectClass( const RectClass & r ) { Left = r.Left; Top = r.Top; Right = r.Right; Bottom = r.Bottom; }
RectClass( float left, float top, float right, float bottom ) { Left = left; Top = top; Right = right; Bottom = bottom; }
RectClass( const Vector2 & top_left, const Vector2 & bottom_right ) { Left = top_left.X; Top = top_left.Y; Right = bottom_right.X; Bottom = bottom_right.Y; }
// Assignment
RectClass & operator = (const RectClass & r) { Left = r.Left; Top = r.Top; Right = r.Right; Bottom = r.Bottom; return *this; }
void Set(float left, float top, float right, float bottom) { Left = left; Top = top; Right = right; Bottom = bottom; }
void Set( const Vector2 & top_left, const Vector2 & bottom_right ) { Left = top_left.X; Top = top_left.Y; Right = bottom_right.X; Bottom = bottom_right.Y; }
void Set(const RectClass & r) { Left = r.Left; Top = r.Top; Right = r.Right; Bottom = r.Bottom; }
// Access
float Width(void) const { return Right - Left; }
float Height(void) const { return Bottom - Top; }
Vector2 Center( void ) const { return Vector2( (Left + Right)/2, (Top + Bottom)/2 ); }
Vector2 Extent( void ) const { return Vector2( (Right - Left)/2, (Bottom - Top)/2 ); }
Vector2 Upper_Left( void ) const { return Vector2( Left, Top ); }
Vector2 Lower_Right( void ) const { return Vector2( Right, Bottom ); }
Vector2 Upper_Right( void ) const { return Vector2( Right, Top ); }
Vector2 Lower_Left( void ) const { return Vector2( Left, Bottom ); }
// Scaling
RectClass & operator *= (float k) { return Scale( k ); }
RectClass & operator /= (float k) { return Scale( 1/k ); }
RectClass & Scale_Relative_Center( float k ) { Vector2 center = Center(); *this-=center; Left*=k; Top*=k; Right*=k; Bottom*=k; *this+=center; return *this; }
RectClass & Scale( float k ) { Left*=k; Top*=k; Right*=k; Bottom*=k; return *this; }
RectClass & Scale( const Vector2 &k ) { Left*=k.X; Top*=k.Y; Right*=k.X; Bottom*=k.Y; return *this; }
RectClass & Inverse_Scale( const Vector2 &k ) { Left/=k.X; Top/=k.Y; Right/=k.X; Bottom/=k.Y; return *this; }
// Offset
RectClass & operator += ( const Vector2 & o ) { Left+=o.X; Top+=o.Y; Right+=o.X; Bottom+=o.Y; return *this; }
RectClass & operator -= ( const Vector2 & o ) { Left-=o.X; Top-=o.Y; Right-=o.X; Bottom-=o.Y; return *this; }
// Inflate
void Inflate( const Vector2 & o ) { Left-=o.X; Top-=o.Y; Right+=o.X; Bottom+=o.Y; }
// Union
RectClass & operator += ( const RectClass & r ) { Left=MIN(Left,r.Left); Top=MIN(Top,r.Top); Right=MAX(Right,r.Right); Bottom=MAX(Bottom,r.Bottom); return *this; }
// Equality
bool operator == ( const RectClass &rval ) const { return (rval.Left == Left) && (rval.Right == Right) && (rval.Top == Top) && (rval.Bottom == Bottom); }
bool operator != ( const RectClass &rval ) const { return (rval.Left != Left) || (rval.Right != Right) || (rval.Top != Top) || (rval.Bottom != Bottom); }
// Containment
bool Contains ( const Vector2 &pos ) const { return (pos.X >= Left) && (pos.X <= Right) && (pos.Y >= Top) && (pos.Y <= Bottom); }
// Misc
void Snap_To_Units( const Vector2 & u ) { Left = (int)(Left / u.X + 0.5f) * u.X; Right = (int)(Right / u.X + 0.5f) * u.X; Top = (int)(Top / u.Y + 0.5f) * u.Y; Bottom = (int)(Bottom / u.Y + 0.5f) * u.Y; }
};
#endif

View File

@@ -0,0 +1,552 @@
/*
** Command & Conquer Generals Zero Hour(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 : wwmath *
* *
* $Archive:: /Commando/Code/WWMath/sphere.h $*
* *
* Author:: Greg_h *
* *
* $Modtime:: 3/29/01 10:37a $*
* *
* $Revision:: 18 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* SphereClass::SphereClass -- constructor *
* SphereClass::Init -- assign a new center and radius to this sphere *
* SphereClass::Re_Center -- move the center, update radius to enclose old sphere *
* SphereClass::Add_Sphere -- expands 'this' sphere to enclose the given sphere *
* SphereClass::Transform -- transforms this sphere *
* SphereClass::Volume -- returns the volume of this sphere *
* SphereClass::operator+= -- 'Add' a sphere to this one *
* SphereClass::operator *= -- transform this sphere by the given radius *
* Spheres_Intersect -- test whether two spheres intersect *
* Add_Spheres -- Add two spheres together, creating sphere which encloses both *
* operator + -- Add two spheres together, creating a sphere which encloses both *
* Transform Sphere -- transform a sphere *
* Transform_Sphere -- transform a sphere *
* operator * -- Transform a sphere *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef SPHERE_H
#define SPHERE_H
#include "always.h"
#include "vector3.h"
#include "matrix3d.h"
/////////////////////////////////////////////////////////////////////
// SphereClass
//
// Center - center of the sphere.
// Radius - radius of the sphere
//
/////////////////////////////////////////////////////////////////////
class SphereClass
{
public:
inline SphereClass(void) { };
inline SphereClass(const Vector3 & center,float radius) { Init(center,radius); }
inline SphereClass(const Matrix3D& mtx,const Vector3 & center,float radius) { Init(mtx,center,radius); }
inline SphereClass(const Vector3 & center,const SphereClass & s0);
inline SphereClass(const Vector3 *Position, const int VertCount);
inline void Init(const Vector3 & pos,float radius);
inline void Init(const Matrix3D& mtx,const Vector3 & pos,float radius);
inline void Re_Center(const Vector3 & center);
inline void Add_Sphere(const SphereClass & s);
inline void Transform(const Matrix3D & tm);
inline float Volume(void) const;
inline SphereClass & operator += (const SphereClass & s);
inline SphereClass & operator *= (const Matrix3D & m);
Vector3 Center;
float Radius;
};
/***********************************************************************************************
* SphereClass::SphereClass -- constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/12/98 GTH : Created. *
*=============================================================================================*/
inline SphereClass::SphereClass(const Vector3 & center,const SphereClass & s0)
{
float dist = (s0.Center - center).Length();
Center = center;
Radius = s0.Radius + dist;
}
inline SphereClass::SphereClass(const Vector3 *Position,const int VertCount)
{
int i;
double dx,dy,dz;
// bounding sphere
// Using the algorithm described in Graphics Gems I page 301.
// This algorithm supposedly generates a bounding sphere within
// 5% of the optimal one but is much faster and simpler to implement.
Vector3 xmin(Position[0].X,Position[0].Y,Position[0].Z);
Vector3 xmax(Position[0].X,Position[0].Y,Position[0].Z);
Vector3 ymin(Position[0].X,Position[0].Y,Position[0].Z);
Vector3 ymax(Position[0].X,Position[0].Y,Position[0].Z);
Vector3 zmin(Position[0].X,Position[0].Y,Position[0].Z);
Vector3 zmax(Position[0].X,Position[0].Y,Position[0].Z);
// FIRST PASS:
// finding the 6 minima and maxima points
for (i=1; i<VertCount; i++) {
if (Position[i].X < xmin.X) {
xmin.X = Position[i].X; xmin.Y = Position[i].Y; xmin.Z = Position[i].Z;
}
if (Position[i].X > xmax.X) {
xmax.X = Position[i].X; xmax.Y = Position[i].Y; xmax.Z = Position[i].Z;
}
if (Position[i].Y < ymin.Y) {
ymin.X = Position[i].X; ymin.Y = Position[i].Y; ymin.Z = Position[i].Z;
}
if (Position[i].Y > ymax.Y) {
ymax.X = Position[i].X; ymax.Y = Position[i].Y; ymax.Z = Position[i].Z;
}
if (Position[i].Z < zmin.Z) {
zmin.X = Position[i].X; zmin.Y = Position[i].Y; zmin.Z = Position[i].Z;
}
if (Position[i].Z > zmax.Z) {
zmax.X = Position[i].X; zmax.Y = Position[i].Y; zmax.Z = Position[i].Z;
}
}
// xspan = distance between the 2 points xmin and xmax squared.
// same goes for yspan and zspan.
dx = xmax.X - xmin.X;
dy = xmax.Y - xmin.Y;
dz = xmax.Z - xmin.Z;
double xspan = dx*dx + dy*dy + dz*dz;
dx = ymax.X - ymin.X;
dy = ymax.Y - ymin.Y;
dz = ymax.Z - ymin.Z;
double yspan = dx*dx + dy*dy + dz*dz;
dx = zmax.X - zmin.X;
dy = zmax.Y - zmin.Y;
dz = zmax.Z - zmin.Z;
double zspan = dx*dx + dy*dy + dz*dz;
// Set points dia1 and dia2 to the maximally separated pair
// This will be the diameter of the initial sphere
Vector3 dia1 = xmin;
Vector3 dia2 = xmax;
double maxspan = xspan;
if (yspan > maxspan) {
maxspan = yspan;
dia1 = ymin;
dia2 = ymax;
}
if (zspan > maxspan) {
maxspan = zspan;
dia1 = zmin;
dia2 = zmax;
}
// Compute initial center and radius and radius squared
Vector3 center;
center.X = (dia1.X + dia2.X) / 2.0f;
center.Y = (dia1.Y + dia2.Y) / 2.0f;
center.Z = (dia1.Z + dia2.Z) / 2.0f;
dx = dia2.X - center.X;
dy = dia2.Y - center.Y;
dz = dia2.Z - center.Z;
double radsqr = dx*dx + dy*dy + dz*dz;
double radius = sqrt(radsqr);
// SECOND PASS:
// Increment current sphere if any points fall outside of it.
for (i=0; i<VertCount; i++) {
dx = Position[i].X - center.X;
dy = Position[i].Y - center.Y;
dz = Position[i].Z - center.Z;
double testrad2 = dx*dx + dy*dy + dz*dz;
if (testrad2 > radsqr) {
// this point was outside the old sphere, compute a new
// center point and radius which contains this point
double testrad = sqrt(testrad2);
// adjust center and radius
radius = (radius + testrad) / 2.0;
radsqr = radius * radius;
double oldtonew = testrad - radius;
center.X = (radius * center.X + oldtonew * Position[i].X) / testrad;
center.Y = (radius * center.Y + oldtonew * Position[i].Y) / testrad;
center.Z = (radius * center.Z + oldtonew * Position[i].Z) / testrad;
}
}
Center = center;
Radius = radius;
}
/***********************************************************************************************
* SphereClass::Init -- assign a new center and radius to this sphere *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/12/98 GTH : Created. *
*=============================================================================================*/
inline void SphereClass::Init(const Vector3 & pos,float radius)
{
Center = pos;
Radius = radius;
}
/***********************************************************************************************
* SphereClass::Init -- assign a new center and radius to this sphere *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/12/98 GTH : Created. *
*=============================================================================================*/
inline void SphereClass::Init(const Matrix3D& mtx, const Vector3 & pos,float radius)
{
#ifdef ALLOW_TEMPORARIES
Center = mtx * pos;
#else
mtx.mulVector3(pos, Center);
#endif
Radius = radius;
}
/***********************************************************************************************
* SphereClass::Re_Center -- move the center, update radius to enclose old sphere *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/12/98 GTH : Created. *
*=============================================================================================*/
inline void SphereClass::Re_Center(const Vector3 & center)
{
float dist = (Center - center).Length();
Center = center;
Radius += dist;
}
/***********************************************************************************************
* SphereClass::Add_Sphere -- expands 'this' sphere to enclose the given sphere *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/12/98 GTH : Created. *
*=============================================================================================*/
inline void SphereClass::Add_Sphere(const SphereClass & s)
{
if (s.Radius == 0.0f) return;
float dist = (s.Center - Center).Length();
if (dist == 0.0f) {
Radius = (Radius > s.Radius) ? Radius : s.Radius;
return;
}
float rnew = (dist + Radius + s.Radius) / 2.0f;
// If rnew is smaller than either of the two sphere radii (it can't be
// smaller than both of them), this means that the smaller sphere is
// completely inside the larger, and the result of adding the two is
// simply the larger sphere. If rnew isn't less than either of them, it is
// the new radius - calculate the new center.
if (rnew < Radius) {
// The existing sphere is the result - do nothing.
} else {
if (rnew < s.Radius) {
// The new sphere is the result:
Init(s.Center, s.Radius);
} else {
// Neither sphere is completely inside the other, so rnew is the new
// radius - calculate the new center
float lerp = (rnew - Radius) / dist;
Vector3 center = (s.Center - Center) * lerp + Center;
Init(center, rnew);
}
}
}
/***********************************************************************************************
* SphereClass::Transform -- transforms this sphere *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/12/99 GTH : Created. *
*=============================================================================================*/
inline void SphereClass::Transform(const Matrix3D & tm)
{
// warning, assumes Orthogonal matrix
#ifdef ALLOW_TEMPORARIES
Center = tm * Center;
#else
tm.mulVector3(Center, Center);
#endif
}
/***********************************************************************************************
* SphereClass::Volume -- returns the volume of this sphere *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/22/99 GTH : Created. *
*=============================================================================================*/
inline float SphereClass::Volume(void) const
{
return (4.0 / 3.0) * WWMATH_PI * (Radius * Radius * Radius);
}
/***********************************************************************************************
* SphereClass::operator+= -- 'Add' a sphere to this one *
* *
* Expands 'this' sphere to also enclose the given sphere *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/12/98 GTH : Created. *
*=============================================================================================*/
inline SphereClass & SphereClass::operator += (const SphereClass & s)
{
Add_Sphere(s);
return *this;
}
/***********************************************************************************************
* SphereClass::operator *= -- transform this sphere by the given radius *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/12/98 GTH : Created. *
*=============================================================================================*/
inline SphereClass & SphereClass::operator *= (const Matrix3D & m)
{
Init(m, Center, Radius);
return *this;
}
/***********************************************************************************************
* Spheres_Intersect -- test whether two spheres intersect *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/12/98 GTH : Created. *
*=============================================================================================*/
inline bool Spheres_Intersect(const SphereClass & s0,const SphereClass & s1)
{
Vector3 delta = s0.Center - s1.Center;
float dist2 = Vector3::Dot_Product(delta, delta);
if (dist2 < (s0.Radius + s1.Radius) * (s0.Radius + s1.Radius)) {
return true;
} else {
return false;
}
}
/***********************************************************************************************
* Add_Spheres -- Add two spheres together, creating sphere which encloses both *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/12/98 GTH : Created. *
*=============================================================================================*/
inline SphereClass Add_Spheres(const SphereClass & s0, const SphereClass & s1)
{
if (s0.Radius == 0.0f) {
return s1;
} else {
SphereClass result(s0);
result.Add_Sphere(s1);
return result;
}
}
/***********************************************************************************************
* operator + -- Add two spheres together, creating a sphere which encloses both *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/12/98 GTH : Created. *
*=============================================================================================*/
inline SphereClass operator + (const SphereClass & s0,const SphereClass & s1)
{
return Add_Spheres(s0,s1);
}
/***********************************************************************************************
* Transform Sphere -- transform a sphere *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/12/98 GTH : Created. *
*=============================================================================================*/
inline SphereClass Transform_Sphere(const Matrix3D & m, const SphereClass & s)
{
// Warning, assumes Orthogonal matrix
return SphereClass(m,s.Center,s.Radius);
}
/***********************************************************************************************
* Transform_Sphere -- transform a sphere *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/12/98 GTH : Created. *
*=============================================================================================*/
inline void Transform_Sphere(const Matrix3D & m, const SphereClass & s,SphereClass & res)
{
// warning, assumes Orthogonal matrix
#ifdef ALLOW_TEMPORARIES
res.Center = m*s.Center;
#else
m.mulVector3(s.Center, res.Center);
#endif
res.Radius = s.Radius;
}
/***********************************************************************************************
* operator * -- Transform a sphere *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/12/98 GTH : Created. *
*=============================================================================================*/
inline SphereClass operator * (const Matrix3D & m, const SphereClass & s)
{
return Transform_Sphere(m,s);
}
#endif

View File

@@ -0,0 +1,248 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /VSS_Sync/wwmath/tcbspline.cpp $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 6/13/01 2:18p $*
* *
* $Revision:: 6 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "tcbspline.h"
#include "wwdebug.h"
#include "persistfactory.h"
#include "wwmathids.h"
#include "wwhack.h"
/*
** Force-Link this module because the linker can't detect that we actually need it...
*/
DECLARE_FORCE_LINK(tcbspline);
/*
** Save-Load stuff
*/
SimplePersistFactoryClass<TCBSpline3DClass,WWMATH_CHUNKID_TCBSPLINE3D> _TCBSpline3DFactory;
enum
{
// ID's used by TCBSpline3D
TCB3D_CHUNK_HERMITE3D = 0x02071009,
TCB3D_CHUNK_PARAMS,
};
/*
** TCBSpline3DClass Implemenation
*/
int TCBSpline3DClass::Add_Key(const Vector3 & point,float t)
{
int index;
index = HermiteSpline3DClass::Add_Key(point,t);
TCBClass params;
params.Tension = 0.0f;
params.Continuity = 0.0f;
params.Bias = 0.0f;
Params.Insert(index,params);
return index;
}
void TCBSpline3DClass::Remove_Key(int i)
{
HermiteSpline3DClass::Remove_Key(i);
Params.Delete(i);
}
void TCBSpline3DClass::Clear_Keys(void)
{
HermiteSpline3DClass::Clear_Keys();
Params.Clear();
}
void TCBSpline3DClass::Set_TCB_Params(int i,float tension,float continuity,float bias)
{
WWASSERT(i >= 0);
WWASSERT(i < Params.Count());
Params[i].Tension = tension;
Params[i].Continuity = continuity;
Params[i].Bias = bias;
TangentsDirty = true;
}
void TCBSpline3DClass::Get_TCB_Params(int i,float *tension,float *continuity,float *bias)
{
if (tension) *tension = Params[i].Tension;
if (continuity) *continuity = Params[i].Continuity;
if (bias) *bias = Params[i].Bias;
}
void TCBSpline3DClass::Update_Tangents(void)
{
if (Keys.Count() < 2) {
for (int i=0; i<Keys.Count(); i++) {
Tangents[0].InTangent.Set(0,0,0);
Tangents[0].OutTangent.Set(0,0,0);
}
}
// First and Last Key:
// Only need to compute the OutTangent for key[0] and the InTangent for key[end]
int end = Keys.Count() - 1;
Tangents[0].InTangent.Set(0,0,0);
Tangents[end].OutTangent.Set(0,0,0);
if (IsLooping) {
// This really only works if the start and end points have the same position...
// Also just using the TCB params from p0 for both
float k0 = 0.5f * ((1-Params[0].Tension) * (1-Params[0].Continuity) * (1-Params[0].Bias));
float k1 = 0.5f * ((1-Params[0].Tension) * (1+Params[0].Continuity) * (1+Params[0].Bias));
float k2 = 0.5f * ((1-Params[0].Tension) * (1+Params[0].Continuity) * (1-Params[0].Bias));
float k3 = 0.5f * ((1-Params[0].Tension) * (1-Params[0].Continuity) * (1+Params[0].Bias));
Vector3 dp_in;
Vector3 dp_out;
Vector3::Subtract(Keys[0].Point,Keys[end-1].Point,&dp_in);
Vector3::Subtract(Keys[1].Point,Keys[0].Point,&dp_out);
Vector3::Add(k0*dp_in, k1*dp_out, &Tangents[end].InTangent);
Vector3::Add(k2*dp_out, k3*dp_in, &Tangents[0].OutTangent);
} else {
float k2 = 0.25f * ((1-Params[0].Tension) * (1+Params[0].Continuity) * (1-Params[0].Bias));
float k3 = 0.25f * ((1-Params[0].Tension) * (1-Params[0].Continuity) * (1+Params[0].Bias));
Vector3 dp_in;
Vector3 dp_out;
Vector3::Subtract(Keys[1].Point,Keys[0].Point,&dp_out);
dp_in = dp_out;
Vector3::Add(k2*dp_out, k3*dp_in, &Tangents[0].OutTangent);
float k0 = 0.25f * ((1-Params[0].Tension) * (1-Params[0].Continuity) * (1-Params[0].Bias));
float k1 = 0.25f * ((1-Params[0].Tension) * (1+Params[0].Continuity) * (1+Params[0].Bias));
Vector3::Subtract(Keys[end].Point,Keys[end-1].Point,&dp_in);
dp_out = dp_in;
Vector3::Add(k0*dp_out, k1*dp_in, &Tangents[end].InTangent);
}
float total_time = (Keys[1].Time - Keys[0].Time) + (Keys[end].Time - Keys[end-1].Time);
float in_factor = 2.0f * (Keys[end].Time - Keys[end-1].Time) / total_time;
float out_factor = 2.0f * (Keys[1].Time - Keys[0].Time) / total_time;
Tangents[end].InTangent *= in_factor;
Tangents[0].OutTangent *= out_factor;
// Now compute the tangents of all of the normal keys...
for (int pi=1;pi<Keys.Count() - 1; pi++) {
float k0 = 0.5f * ((1-Params[pi].Tension) * (1-Params[pi].Continuity) * (1-Params[pi].Bias));
float k1 = 0.5f * ((1-Params[pi].Tension) * (1+Params[pi].Continuity) * (1+Params[pi].Bias));
float k2 = 0.5f * ((1-Params[pi].Tension) * (1+Params[pi].Continuity) * (1-Params[pi].Bias));
float k3 = 0.5f * ((1-Params[pi].Tension) * (1-Params[pi].Continuity) * (1+Params[pi].Bias));
Vector3 dp_in;
Vector3 dp_out;
Vector3::Subtract(Keys[pi].Point,Keys[pi-1].Point,&dp_in);
Vector3::Subtract(Keys[pi+1].Point,Keys[pi].Point,&dp_out);
Vector3::Add(k0*dp_out, k1*dp_in, &Tangents[pi].InTangent);
Vector3::Add(k2*dp_out, k3*dp_in, &Tangents[pi].OutTangent);
float total_time = (Keys[pi+1].Time - Keys[pi-1].Time);
float in_factor = 2.0f * (Keys[pi].Time - Keys[pi-1].Time) / total_time;
float out_factor = 2.0f * (Keys[pi+1].Time - Keys[pi].Time) / total_time;
Tangents[pi].InTangent *= in_factor; // compensating for un-even keys
Tangents[pi].OutTangent *= out_factor;
}
TangentsDirty = false;
}
const PersistFactoryClass & TCBSpline3DClass::Get_Factory(void) const
{
return _TCBSpline3DFactory;
}
bool TCBSpline3DClass::Save(ChunkSaveClass &csave)
{
csave.Begin_Chunk(TCB3D_CHUNK_HERMITE3D);
HermiteSpline3DClass::Save(csave);
csave.End_Chunk();
csave.Begin_Chunk(TCB3D_CHUNK_PARAMS);
for (int i=0; i<Params.Count(); i++) {
csave.Write(&(Params[i].Tension),sizeof(Params[i].Tension));
csave.Write(&(Params[i].Continuity),sizeof(Params[i].Continuity));
csave.Write(&(Params[i].Bias),sizeof(Params[i].Bias));
}
csave.End_Chunk();
return true;
}
bool TCBSpline3DClass::Load(ChunkLoadClass &cload)
{
int i;
TCBClass param;
// reset the keys
Params.Delete_All();
// read in the chunks
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID())
{
case TCB3D_CHUNK_HERMITE3D:
HermiteSpline3DClass::Load(cload);
break;
case TCB3D_CHUNK_PARAMS:
for (i=0; i<Keys.Count(); i++) {
cload.Read(&(param.Tension),sizeof(param.Tension));
cload.Read(&(param.Continuity),sizeof(param.Continuity));
cload.Read(&(param.Bias),sizeof(param.Bias));
Params.Add(param);
}
break;
default:
WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",__FILE__,__LINE__));
break;
}
cload.Close_Chunk();
}
return true;
}

View File

@@ -0,0 +1,86 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /VSS_Sync/wwmath/tcbspline.h $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 6/13/01 2:18p $*
* *
* $Revision:: 4 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef TCBSPLINE_H
#define TCBSPLINE_H
#include "hermitespline.h"
/*
** TCBSpline3DClass
** Tension-Continuity-Bias splines. Otherwise known as Kochanek-Bartels cubic splines
*/
class TCBSpline3DClass : public HermiteSpline3DClass
{
public:
virtual int Add_Key(const Vector3 & point,float t);
virtual void Remove_Key(int i);
virtual void Clear_Keys(void);
virtual void Set_TCB_Params(int i,float tension,float continuity,float bias);
virtual void Get_TCB_Params(int i,float *tension,float *continuity,float *bias);
void Update_Tangents(void);
// save-load support
virtual const PersistFactoryClass & Get_Factory(void) const;
virtual bool Save(ChunkSaveClass &csave);
virtual bool Load(ChunkLoadClass &cload);
protected:
class TCBClass
{
public:
float Tension;
float Continuity;
float Bias;
bool operator == (const TCBClass & that) { return ((Tension == that.Tension) && (Continuity == that.Continuity) && (Bias == that.Bias)); }
bool operator != (const TCBClass & that) { return !TCBClass::operator == (that); }
};
DynamicVectorClass<TCBClass> Params;
};
#endif

View File

@@ -0,0 +1,363 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/WWMath/tri.cpp $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 5/03/01 3:41p $*
* *
* $Revision:: 8 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* TriClass::Find_Dominant_Plane -- returns indices of the axes of the dominant plane *
* TriClass::Contains_Point -- performs 2D point-in-triangle test. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "tri.h"
#include "vector2.h"
struct FDPRec
{
int axis1;
int axis2;
int axis3;
};
static inline void find_dominant_plane_fast(const TriClass & tri, FDPRec& info)
{
static const FDPRec dominance[3] =
{
{ 1, 2, 0 }, // Dominant is the X axis
{ 0, 2, 1 }, // Dominant is the Y axis
{ 0, 1, 2 } // Dominant is the Z axis
};
/*
** Find the largest component of the normal
*/
float x = WWMath::Fabs(tri.N->X);
float y = WWMath::Fabs(tri.N->Y);
float z = WWMath::Fabs(tri.N->Z);
float val = x;
int ni = 0;
if (y > val)
{
ni = 1;
val = y;
}
if (z > val)
{
ni = 2;
}
info = dominance[ni];
}
static inline void find_dominant_plane(const TriClass & tri, int * axis1,int * axis2,int * axis3)
{
/*
** Find the largest component of the normal
*/
int ni = 0;
float x = WWMath::Fabs(tri.N->X);
float y = WWMath::Fabs(tri.N->Y);
float z = WWMath::Fabs(tri.N->Z);
float val = x;
if (y > val) {
ni = 1;
val = y;
}
if (z > val) {
ni = 2;
}
/*
** return the indices of the two axes perpendicular
*/
switch (ni)
{
case 0:
// Dominant is the X axis
*axis1 = 1;
*axis2 = 2;
*axis3 = 0;
break;
case 1:
// Dominant is the Y axis
*axis1 = 0;
*axis2 = 2;
*axis3 = 1;
break;
case 2:
// Dominant is the Z axis
*axis1 = 0;
*axis2 = 1;
*axis3 = 2;
break;
}
}
/***********************************************************************************************
* TriClass::Find_Dominant_Plane -- returns indices of the axes of the dominant plane *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/24/99 GTH : Created. *
*=============================================================================================*/
void TriClass::Find_Dominant_Plane(int * axis1,int * axis2) const
{
/*
** Find the largest component of the normal
*/
int ni = 0;
float x = WWMath::Fabs(N->X);
float y = WWMath::Fabs(N->Y);
float z = WWMath::Fabs(N->Z);
float val = x;
if (y > val) {
ni = 1;
val = y;
}
if (z > val) {
ni = 2;
}
/*
** return the indices of the two axes perpendicular
*/
switch (ni)
{
case 0:
// Dominant is the X axis
*axis1 = 1;
*axis2 = 2;
break;
case 1:
// Dominant is the Y axis
*axis1 = 0;
*axis2 = 2;
break;
case 2:
// Dominant is the Z axis
*axis1 = 0;
*axis2 = 1;
break;
}
}
/***********************************************************************************************
* TriClass::Contains_Point -- performs 2D point-in-triangle test. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* Assumes that the point is in the plane of the triangle... use this after you've intersected *
* a ray with the plane of the triangle. *
* *
* HISTORY: *
* 3/24/99 GTH : Created. *
*=============================================================================================*/
bool TriClass::Contains_Point(const Vector3 & ipoint) const
{
#if 0
/*
** Perform the test in 2d on the plane which the normal
** is most perpendicular to. (copied from E.Cosky's intersection code)
*/
int axis1 = 0;
int axis2 = 0;
Find_Dominant_Plane(&axis1,&axis2);
#if 1
unsigned char flags; // dummy variable passed into function and not used here
return Point_In_Triangle_2D(*V[0], *V[1], *V[2], ipoint, axis1, axis2, flags);
#else
float u0 = ipoint[axis1] - (*V[0])[axis1];
float v0 = ipoint[axis2] - (*V[0])[axis2];
/*
** determine the 2d vectors on the dominant plane from the first vertex to the other two
*/
float u1 = (*V[1])[axis1] - (*V[0])[axis1];
float v1 = (*V[1])[axis2] - (*V[0])[axis2];
float u2 = (*V[2])[axis1] - (*V[0])[axis1];
float v2 = (*V[2])[axis2] - (*V[0])[axis2];
float alpha, beta;
bool intersect = false;
// calculate alpha and beta as normalized (0..1) percentages across the 2d projected triangle
// and do bounds checking (sum <= 1) to determine whether or not the triangle intersection occurs.
if (u1 == 0) {
beta = u0 / u2;
if ((beta >= 0) && (beta <= 1)) {
alpha = (v0 - beta * v2) / v1;
intersect = ((alpha >= 0) && ((alpha + beta) <= 1 + WWMATH_EPSILON));
}
} else {
beta = (v0 * u1 - u0 * v1) / (v2 * u1 - u2 * v1);
if ((beta >= 0) && (beta <= 1)) {
alpha = (u0 - beta * u2) / u1;
intersect = ((alpha >= 0) && ((alpha + beta) <= 1 + WWMATH_EPSILON));
}
}
return intersect;
#endif
#endif
/*
** New cross-product based point-containment
*/
#if 0
int vi;
int axis3 = 0;
for (vi=0; vi<3; vi++) {
if ((axis1 != vi) && (axis2 != vi)) axis3 = vi;
}
Vector3 test_point = ipoint;
test_point[axis3] = 0.0f;
Vector3 points[3];
for (vi=0; vi<3; vi++) {
points[vi] = *(V[vi]);
points[vi][axis3] = 0.0f;
}
bool side[3];
Vector3 edge;
Vector3 cross;
Vector3 dp;
for (vi=0; vi<3; vi++) {
edge = points[(vi+1)%3] - points[vi];
dp = test_point - points[vi];
Vector3::Cross_Product(dp,edge,&cross);
side[vi] = (cross[axis3] > 0.0f);
}
bool my_intersect = ((side[0] == side[1]) && (side[1] == side[2]));
return my_intersect;
#endif
/*
** "Optimized" version
*/
#if 1
#if 0
// srj opto version
/// @todo srj -- profile me to see if this is actually faster
FDPRec info;
find_dominant_plane_fast(*this, info);
/*
** Compute the 2D cross product of edge0 with a vector to the point
*/
float edge_x, edge_y, dp_x, dp_y, cross;
bool side0, side1, side2;
edge_x = (*V[1])[info.axis1] - (*V[0])[info.axis1];
edge_y = (*V[1])[info.axis2] - (*V[0])[info.axis2];
dp_x = ipoint[info.axis1] - (*V[0])[info.axis1];
dp_y = ipoint[info.axis2] - (*V[0])[info.axis2];
cross = edge_x * dp_y - edge_y * dp_x;
side0 = (cross >= 0.0f);
edge_x = (*V[2])[info.axis1] - (*V[1])[info.axis1];
edge_y = (*V[2])[info.axis2] - (*V[1])[info.axis2];
dp_x = ipoint[info.axis1] - (*V[1])[info.axis1];
dp_y = ipoint[info.axis2] - (*V[1])[info.axis2];
cross = edge_x * dp_y - edge_y * dp_x;
side1 = (cross >= 0.0f);
edge_x = (*V[0])[info.axis1] - (*V[2])[info.axis1];
edge_y = (*V[0])[info.axis2] - (*V[2])[info.axis2];
dp_x = ipoint[info.axis1] - (*V[2])[info.axis1];
dp_y = ipoint[info.axis2] - (*V[2])[info.axis2];
cross = edge_x * dp_y - edge_y * dp_x;
side2 = (cross >= 0.0f);
bool my_intersect = ((side0 == side1) && (side1 == side2));
return my_intersect;
#else
int vi;
int axis1 = 0;
int axis2 = 0;
int axis3 = 0;
find_dominant_plane(*this,&axis1,&axis2,&axis3);
bool side[3];
/*
** Compute the 2D cross product of edge0 with a vector to the point
*/
Vector2 edge;
Vector2 dp;
for (vi=0; vi<3; vi++) {
int va=vi;
int vb=(vi+1)%3;
edge.Set((*V[vb])[axis1] - (*V[va])[axis1] , (*V[vb])[axis2] - (*V[va])[axis2]);
dp.Set(ipoint[axis1] - (*V[va])[axis1] , ipoint[axis2] - (*V[va])[axis2]);
float cross = edge.X * dp.Y - edge.Y * dp.X;
side[vi] = (cross >= 0.0f);
}
bool my_intersect = ((side[0] == side[1]) && (side[1] == side[2]));
return my_intersect;
#endif
#endif
}

View File

@@ -0,0 +1,306 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/tri.h $*
* *
* Author:: Greg_h *
* *
* $Modtime:: 5/04/01 8:35p $*
* *
* $Revision:: 14 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef TRI_H
#define TRI_H
#include "always.h"
#include "vector4.h"
#include "vector3.h"
#include "vector2.h"
#include <assert.h>
/**
** TriClass
** When the math library needs to deal with triangles, this will be the form used.
** The initial reason for this class is for Commando's collision detection code. Initially
** I wrote custom code inside the render object for the terrain to perform collision
** detection. Moving the low-level geometrical collision code into the math library makes it
** more re-useable and independent from changes in the rendering code.
*/
class TriClass
{
public:
const Vector3 * N;
const Vector3 * V[3];
void Compute_Normal()
{
assert(N);
assert(V[0]);
assert(V[1]);
assert(V[2]);
Vector3::Cross_Product(*(V[1])-*(V[0]),*(V[2])-*(V[0]),(Vector3 *)N);
((Vector3 *)N)->Normalize();
}
bool Contains_Point(const Vector3 & ipoint) const;
void Find_Dominant_Plane(int * axis1,int * axis2) const;
};
/*
** Utility functions:
** Functions which have to do with triangles but not neccessarily with TriClass - usually used
** by TriClass but also available for use elsewhere.
*/
// Enums for raycast flags (currently used only for semi-infinite axis-aligned rays)
enum
{
TRI_RAYCAST_FLAG_NONE = 0x00,
TRI_RAYCAST_FLAG_HIT_EDGE = 0x01,
TRI_RAYCAST_FLAG_START_IN_TRI = 0x02
};
// This function does a pure 2D point-in-triangle test. We pass in 3D points both for the
// triangle and test points, but we also pass in the two axes to use (the third axis is ignored,
// so we are actually testing the projection of the four points onto one of the axis planes). The
// triangle points are not assumed to be in any particular winding order (that is checked for
// internally). It is used internally by TriClass::Contains_Point(), and may be used elsewhere.
// If the ray intersects the camera at an edge we also count it as an intersection. We set a bit
// in 'flags' to true in this case (some users of this function need this extra information and
// it is very cheap to compute). We do not modify 'flags' if no edges are hit, so it must be
// initialized outside this function if its value is to be used.
inline bool Point_In_Triangle_2D(const Vector3 &tri_point0, const Vector3 &tri_point1,
const Vector3 &tri_point2, const Vector3 &test_point, int axis_1, int axis_2,
unsigned char &flags)
{
// The function is based on checking signs of determinants, or in a more visually intuitive
// sense, checking on which side of a line a point lies. For example, if the points run in
// counter-clockwise order, the interior of the triangle is the intersection of the three
// half-planes to the left of the directed infinite lines along P0 to P1, P1 to P2, P2 to P0.
// Therefore the test point is in the triangle iff it is to the left of all three lines (if
// the points are in clockwise order, we check if it is to the right of the lines).
Vector2 p0p1(tri_point1[axis_1] - tri_point0[axis_1], tri_point1[axis_2] - tri_point0[axis_2]);
Vector2 p1p2(tri_point2[axis_1] - tri_point1[axis_1], tri_point2[axis_2] - tri_point1[axis_2]);
Vector2 p2p0(tri_point0[axis_1] - tri_point2[axis_1], tri_point0[axis_2] - tri_point2[axis_2]);
// Check which side P2 is relative to P0P1. The sign of this test must equal the sign of all
// three tests between the lines and the test point for the test point to be inside the
// triangle. (this test will also tell us if the three points are colinear - if the triangle
// is degenerate).
Vector2 p0p2(tri_point2[axis_1] - tri_point0[axis_1], tri_point2[axis_2] - tri_point0[axis_2]);
float p0p1p2 = Vector2::Perp_Dot_Product(p0p1, p0p2);
if (p0p1p2 != 0.0f) {
// The triangle is not degenerate - test three sides
float side_factor = p0p1p2 > 0.0f ? 1.0f : -1.0f;
float factors[3];
// Now perform tests
Vector2 p0pT(test_point[axis_1] - tri_point0[axis_1], test_point[axis_2] - tri_point0[axis_2]);
factors[0] = Vector2::Perp_Dot_Product(p0p1, p0pT);
if (factors[0] * side_factor < 0.0f) {
return false;
}
Vector2 p1pT(test_point[axis_1] - tri_point1[axis_1], test_point[axis_2] - tri_point1[axis_2]);
factors[1] = Vector2::Perp_Dot_Product(p1p2, p1pT);
if (factors[1] * side_factor < 0.0f) {
return false;
}
Vector2 p2pT(test_point[axis_1] - tri_point2[axis_1], test_point[axis_2] - tri_point2[axis_2]);
factors[2] = Vector2::Perp_Dot_Product(p2p0, p2pT);
if (factors[2] * side_factor < 0.0f) {
return false;
}
if ((factors[0] == 0.0f) || (factors[1] == 0.0f) || (factors[2] == 0.0f)) flags |= TRI_RAYCAST_FLAG_HIT_EDGE;
return true;
} else {
// The triangle is degenerate. This should be a rare case, so it does not matter much if it
// is a little slower than the non-colinear case.
// Find the two outer points along the triangle's line ('start' and 'end' points)
float p0p1dist2 = p0p1.Length2();
float p1p2dist2 = p1p2.Length2();
float p2p0dist2 = p1p2.Length2();
float max_dist2;
Vector2 pSpE, pSpT; // 'end' point, test point - both in 'start' points' frame
if (p0p1dist2 > p1p2dist2) {
if (p0p1dist2 > p2p0dist2) {
// points 0 and 1 are the 'outer' points. 0 is 'start' and 1 is 'end'.
pSpE = p0p1;
pSpT.Set(test_point[axis_1] - tri_point0[axis_1], test_point[axis_2] - tri_point0[axis_2]);
max_dist2 = p0p1dist2;
} else {
// points 0 and 2 are the 'outer' points. 2 is 'start' and 0 is 'end'.
pSpE = p2p0;
pSpT.Set(test_point[axis_1] - tri_point2[axis_1], test_point[axis_2] - tri_point2[axis_2]);
max_dist2 = p2p0dist2;
}
} else {
if (p1p2dist2 > p2p0dist2) {
// points 1 and 2 are the 'outer' points. 1 is 'start' and 2 is 'end'.
pSpE = p1p2;
pSpT.Set(test_point[axis_1] - tri_point1[axis_1], test_point[axis_2] - tri_point1[axis_2]);
max_dist2 = p1p2dist2;
} else {
// points 0 and 2 are the 'outer' points. 2 is 'start' and 0 is 'end'.
pSpE = p2p0;
pSpT.Set(test_point[axis_1] - tri_point2[axis_1], test_point[axis_2] - tri_point2[axis_2]);
max_dist2 = p2p0dist2;
}
}
if (max_dist2 != 0.0f) {
// Triangle is line segment, check if test point is colinear with it
if (Vector2::Perp_Dot_Product(pSpE, pSpT)) {
// Not colinear
return false;
} else {
// Colinear - is test point's distance from start and end <= segment length?
Vector2 pEpT = pSpT - pSpE;
if (pSpT.Length2() <= max_dist2 && pEpT.Length2() <= max_dist2) {
flags |= TRI_RAYCAST_FLAG_HIT_EDGE;
return true;
} else {
return false;
}
}
} else {
// All triangle points coincide, check if test point coincides with them
if (pSpT.Length2() == 0.0f) {
flags |= TRI_RAYCAST_FLAG_HIT_EDGE;
return true;
} else {
return false;
}
}
}
}
// This function tests a semi-infinite axis-aligned ray vs. a triangle.
// The inputs are blah blah blah
inline bool Cast_Semi_Infinite_Axis_Aligned_Ray_To_Triangle(const Vector3 &tri_point0,
const Vector3 &tri_point1, const Vector3 &tri_point2, const Vector4 &tri_plane,
const Vector3 &ray_start, int axis_r, int axis_1, int axis_2, int direction,
unsigned char & flags)
{
bool retval = false;
// First check infinite ray vs. triangle (2D check)
unsigned char flags_2d = TRI_RAYCAST_FLAG_NONE;
if (Point_In_Triangle_2D(tri_point0, tri_point1, tri_point2, ray_start, axis_1, axis_2, flags_2d)) {
// NOTE: SR plane equations, unlike WWMath's PlaneClass, use the Ax+By+Cz+D = 0
// representation. It can also be viewed as C0x+C1y+C2z+C3 = dist where dist is the
// signed distance from the plane (and therefore the plane is defined as those points
// where dist = 0). Now that we know that the projection along the ray is inside the
// triangle, determining whether the ray hits the triangle is a matter of determining
// whether the start point is on the triangle plane's "anti-rayward side" (the side
// which the ray points away from).
// To determine this, we will use C[axis_r] (the plane coefficient of the ray axis).
// This coefficient is positive if the positive direction of the ray axis points into
// the planes' positive halfspace and negative if it points into the planes' negative
// halfspace (it is zero if the ray axis is parallel to the triangle plane). If we
// multiply this by 'sign' which is defined to be -1.0 if 'direction' equals 0 and
// 1.0 if 'direction' equals 1, we will get a number which is positive or negative
// depending on which halfspace the ray itself (as opposed to the rays axis) points
// towards. If we further multiply this by dist(start point) - the result of plugging
// the start point into the plane equation - we will get a number which is positive
// if the start point is on the 'rayward side' (ray does not intersect the triangle)
// and is negative if the start point is on the 'anti-rayward side' (ray does
// intersect triangle). In either of these two cases we are done.
// (see below for what happens when the result is zero - more checks need to be made).
static const float sign[2] = {-1.0f, 1.0f };
float result = tri_plane[axis_r] * sign[direction] * (tri_plane.X * ray_start.X +
tri_plane.Y * ray_start.Y + tri_plane.Z * ray_start.Z + tri_plane.W);
if (result < 0.0f) {
// Intersection!
flags |= (flags_2d & TRI_RAYCAST_FLAG_HIT_EDGE);
retval = true;
} else {
if (result == 0.0f) {
// If the result is 0, this means either the ray is parallel to the triangle
// plane or the start point is embedded in the triangle plane. Note that since
// the start point passed the 2D check, then if the ray is parallel the start
// point must also be embedded in the triangle plane. This leaves us with two
// cases:
// A) The ray is not parallel to the plane - in this case the start point is
// embedded in the triangle. We report an intersection, bitwise OR the edge
// result from the 2D check into the 'edge hit' flag and set the 'start in tri'
// flag.
// B) The ray is parallel to the plane. In this case the result of the 2D test
// tells us that the infinite line intersects the triangle (actually is
// embedded in the triangle along part of its length), but we do not know
// whether the semi-infinite ray also does so. We simplify things by not
// counting such an 'intersection'. There are four reasons behind this:
// 1. It differs from a 'normal' intersection (which has one intersecting
// point) - there are infinitely many intersecting points.
// 2. Moving the plane by an infinitesimally small amount to either side will
// cause the ray to no longer touch the plane.
// 3. This will not affect results for the known uses of this function.
// 4. By doing so we avoid having to code up a bunch of complex tests.
// Therefore in case B) we just report no intersection. We still need to find
// out whether the point is embedded in the triangle (for setting the flag) so
// we do another simple 2D test on the dominant plane.
if (tri_plane[axis_r]) {
// Case A)
flags |= (flags_2d & TRI_RAYCAST_FLAG_HIT_EDGE);
flags |= TRI_RAYCAST_FLAG_START_IN_TRI;
retval = true;
} else {
// Case B) - test if point in tri (we know it is in plane, so we can use
// TriClass function)
TriClass tri;
tri.V[0] = &tri_point0;
tri.V[1] = &tri_point1;
tri.V[2] = &tri_point2;
tri.N = (Vector3*)&tri_plane;
if (tri.Contains_Point(ray_start)) {
flags |= TRI_RAYCAST_FLAG_START_IN_TRI;
}
}
} // if (result == 0.0f)
} // else (result < 0.0f)
} // if Point_In_Triangle_2D()
return retval;
}
#endif

View File

@@ -0,0 +1,173 @@
/*
** Command & Conquer Generals Zero Hour(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 : G *
* *
* $Archive:: /Commando/Code/wwmath/v3_rnd.cpp $*
* *
* $Author:: Greg_h $*
* *
* $Modtime:: 7/09/99 9:49a $*
* *
* $Revision:: 4 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "v3_rnd.h"
#include "vector2.h"
const float Vector3Randomizer::OOIntMax = 1.0f / (float)INT_MAX;
const float Vector3Randomizer::OOUIntMax = 1.0f / (float)UINT_MAX;
Random3Class Vector3Randomizer::Randomizer;
Vector3SolidBoxRandomizer::Vector3SolidBoxRandomizer(const Vector3 & extents)
{
Extents.X = MAX(extents.X, 0.0f);
Extents.Y = MAX(extents.Y, 0.0f);
Extents.Z = MAX(extents.Z, 0.0f);
}
void Vector3SolidBoxRandomizer::Get_Vector(Vector3 &vector)
{
vector.X = Get_Random_Float_Minus1_To_1() * Extents.X;
vector.Y = Get_Random_Float_Minus1_To_1() * Extents.Y;
vector.Z = Get_Random_Float_Minus1_To_1() * Extents.Z;
}
float Vector3SolidBoxRandomizer::Get_Maximum_Extent(void)
{
float max = MAX(Extents.X, Extents.Y);
max = MAX(max, Extents.Z);
return max;
}
void Vector3SolidBoxRandomizer::Scale(float scale)
{
scale = MAX(scale, 0.0f);
Extents.X *= scale;
Extents.Y *= scale;
Extents.Z *= scale;
}
Vector3SolidSphereRandomizer::Vector3SolidSphereRandomizer(float radius)
{
Radius = MAX(radius, 0.0f);
}
void Vector3SolidSphereRandomizer::Get_Vector(Vector3 &vector)
{
// Generate vectors in a cube and discard the ones not in a sphere
float rad_squared = Radius * Radius;
for (;;) {
vector.X = Get_Random_Float_Minus1_To_1() * Radius;
vector.Y = Get_Random_Float_Minus1_To_1() * Radius;
vector.Z = Get_Random_Float_Minus1_To_1() * Radius;
if (vector.Length2() <= rad_squared) break;
}
}
float Vector3SolidSphereRandomizer::Get_Maximum_Extent(void)
{
return Radius;
}
void Vector3SolidSphereRandomizer::Scale(float scale)
{
scale = MAX(scale, 0.0f);
Radius *= scale;
}
Vector3HollowSphereRandomizer::Vector3HollowSphereRandomizer(float radius)
{
Radius = MAX(radius, 0.0f);
}
void Vector3HollowSphereRandomizer::Get_Vector(Vector3 &vector)
{
// Generate vectors in a 2x2x2 origin-centered cube, discard the ones not in a unit-radius
// sphere and scale the result to Radius.
float v_l2;
for (;;) {
vector.X = Get_Random_Float_Minus1_To_1();
vector.Y = Get_Random_Float_Minus1_To_1();
vector.Z = Get_Random_Float_Minus1_To_1();
v_l2 = vector.Length2();
if (v_l2 <= 1.0f && v_l2 > 0.0f) break;
}
float scale = Radius * WWMath::Inv_Sqrt(v_l2);
vector.X *= scale;
vector.Y *= scale;
vector.Z *= scale;
}
float Vector3HollowSphereRandomizer::Get_Maximum_Extent(void)
{
return Radius;
}
void Vector3HollowSphereRandomizer::Scale(float scale)
{
scale = MAX(scale, 0.0f);
Radius *= scale;
}
Vector3SolidCylinderRandomizer::Vector3SolidCylinderRandomizer(float extent, float radius)
{
Extent = MAX(extent, 0.0f);
Radius = MAX(radius, 0.0f);
}
void Vector3SolidCylinderRandomizer::Get_Vector(Vector3 &vector)
{
vector.X = Get_Random_Float_Minus1_To_1() * Extent;
// Generate 2D vectors in a square and discard the ones not in a circle
Vector2 vec2;
float rad_squared = Radius * Radius;
for (;;) {
vec2.X = Get_Random_Float_Minus1_To_1() * Radius;
vec2.Y = Get_Random_Float_Minus1_To_1() * Radius;
if (vec2.Length2() <= rad_squared) break;
}
vector.Y = vec2.X;
vector.Z = vec2.Y;
}
float Vector3SolidCylinderRandomizer::Get_Maximum_Extent(void)
{
return MAX(Extent, Radius);
}
void Vector3SolidCylinderRandomizer::Scale(float scale)
{
scale = MAX(scale, 0.0f);
Extent *= scale;
Radius *= scale;
}

View File

@@ -0,0 +1,235 @@
/*
** Command & Conquer Generals Zero Hour(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 : G *
* *
* $Archive:: /Commando/Code/wwmath/v3_rnd.h $*
* *
* $Author:: Greg_h $*
* *
* $Modtime:: 7/09/99 9:49a $*
* *
* $Revision:: 4 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef V3_RND_H
#define V3_RND_H
#include "always.h"
#include "vector3.h"
#include "random.h"
#include <limits.h>
/*
** Vector3Randomizer is an abstract class for generating random Vector3s.
** Examples: generating vectors in a sphere, cylinder, etc.
** This file contains several concrete derived classes; others may be defined
** in future.
** A possible future extension to this class would be to add a method to
** efficiently get an array of random points.
*/
class Vector3Randomizer {
public:
enum
{
CLASSID_UNKNOWN = 0xFFFFFFFF,
CLASSID_SOLIDBOX = 0,
CLASSID_SOLIDSPHERE,
CLASSID_HOLLOWSPHERE,
CLASSID_SOLIDCYLINDER,
CLASSID_MAXKNOWN,
CLASSID_LAST = 0x0000FFFF
};
virtual ~Vector3Randomizer(void) { }
// RTTI identifiction
virtual unsigned int Class_ID (void) const = 0;
// Return a random vector
virtual void Get_Vector(Vector3 &vector) = 0;
// Get the maximum component possible for generated vectors
virtual float Get_Maximum_Extent(void) = 0;
// Scale all vectors produced in future
virtual void Scale(float scale) = 0;
// Clone the randomizer
virtual Vector3Randomizer * Clone(void) const = 0;
protected:
// Derived classes should have protected copy CTors so users use the Clone() function
// Utility functions
float Get_Random_Float_Minus1_To_1() { return Randomizer * OOIntMax; }
float Get_Random_Float_0_To_1() { return ((unsigned int)Randomizer) * OOUIntMax; }
static const float OOIntMax;
static const float OOUIntMax;
static Random3Class Randomizer;
private:
// Derived classes should have a private dummy assignment operator to block usage
};
/*
** Vector3SolidBoxRandomizer is a randomizer for generating points uniformly distributed inside a
** box which is centered on the origin.
*/
class Vector3SolidBoxRandomizer : public Vector3Randomizer {
public:
Vector3SolidBoxRandomizer(const Vector3 & extents);
virtual unsigned int Class_ID (void) const { return CLASSID_SOLIDBOX; }
virtual const Vector3 & Get_Extents (void) const { return Extents; }
virtual void Get_Vector(Vector3 &vector);
virtual float Get_Maximum_Extent(void);
virtual void Scale(float scale);
virtual Vector3Randomizer * Clone(void) const { return W3DNEW Vector3SolidBoxRandomizer(*this); }
protected:
// Derived classes should have protected copy CTors so users use the Clone() function
Vector3SolidBoxRandomizer(const Vector3SolidBoxRandomizer &src) : Extents(src.Extents) { }
private:
// Derived classes should have a private dummy assignment operator to block usage
Vector3SolidBoxRandomizer & Vector3SolidBoxRandomizer::operator = (const Vector3SolidBoxRandomizer &that) { that; return *this; }
Vector3 Extents;
};
/*
** Vector3SolidSphereRandomizer is a randomizer for generating points uniformly distributed inside
** a sphere which is centered on the origin.
*/
class Vector3SolidSphereRandomizer : public Vector3Randomizer {
public:
Vector3SolidSphereRandomizer(float radius);
virtual unsigned int Class_ID (void) const { return CLASSID_SOLIDSPHERE; }
virtual float Get_Radius (void) const { return Radius; }
virtual void Get_Vector(Vector3 &vector);
virtual float Get_Maximum_Extent(void);
virtual void Scale(float scale);
virtual Vector3Randomizer * Clone(void) const { return W3DNEW Vector3SolidSphereRandomizer(*this); }
protected:
// Derived classes should have protected copy CTors so users use the Clone() function
Vector3SolidSphereRandomizer(const Vector3SolidSphereRandomizer &src) : Radius(src.Radius) { }
private:
// Derived classes should have a private dummy assignment operator to block usage
Vector3SolidSphereRandomizer & Vector3SolidSphereRandomizer::operator = (const Vector3SolidSphereRandomizer &that) { that; return *this; }
float Radius;
};
/*
** Vector3HollowSphereRandomizer is a randomizer for generating points uniformly distributed on the
** surface of a sphere which is centered on the origin.
*/
class Vector3HollowSphereRandomizer : public Vector3Randomizer {
public:
Vector3HollowSphereRandomizer(float radius);
virtual unsigned int Class_ID (void) const { return CLASSID_HOLLOWSPHERE; }
virtual float Get_Radius (void) const { return Radius; }
virtual void Get_Vector(Vector3 &vector);
virtual float Get_Maximum_Extent(void);
virtual void Scale(float scale);
virtual Vector3Randomizer * Clone(void) const { return W3DNEW Vector3HollowSphereRandomizer(*this); }
protected:
// Derived classes should have protected copy CTors so users use the Clone() function
Vector3HollowSphereRandomizer(const Vector3HollowSphereRandomizer &src) : Radius(src.Radius) { }
private:
// Derived classes should have a private dummy assignment operator to block usage
Vector3HollowSphereRandomizer & Vector3HollowSphereRandomizer::operator = (const Vector3HollowSphereRandomizer &that) { that; return *this; }
float Radius;
};
/*
** Vector3SolidCylinderRandomizer is a randomizer for generating points uniformly distributed
** inside a cylinder which is centered on the origin (set extent to 0 for a disk).
*/
class Vector3SolidCylinderRandomizer : public Vector3Randomizer {
public:
Vector3SolidCylinderRandomizer(float extent, float radius);
virtual unsigned int Class_ID (void) const { return CLASSID_SOLIDCYLINDER; }
virtual float Get_Radius (void) const { return Radius; }
virtual float Get_Height (void) const { return Extent; }
virtual void Get_Vector(Vector3 &vector);
virtual float Get_Maximum_Extent(void);
virtual void Scale(float scale);
virtual Vector3Randomizer * Clone(void) const { return W3DNEW Vector3SolidCylinderRandomizer(*this); }
protected:
// Derived classes should have protected copy CTors so users use the Clone() function
Vector3SolidCylinderRandomizer(const Vector3SolidCylinderRandomizer &src) : Extent(src.Extent), Radius(src.Radius) { }
private:
// Derived classes should have a private dummy assignment operator to block usage
Vector3SolidCylinderRandomizer & Vector3SolidCylinderRandomizer::operator = (const Vector3SolidCylinderRandomizer &that) { that; return *this; }
float Extent;
float Radius;
};
#endif

View File

@@ -0,0 +1,671 @@
/*
** Command & Conquer Generals Zero Hour(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/>.
*/
/* $Header: /Commando/Code/wwmath/vector2.h 24 7/06/01 9:43a Byon_g $ */
/***********************************************************************************************
*** Confidential - Westwood Studios ***
***********************************************************************************************
* *
* Project Name : Westwood 3D *
* *
* File Name : VECTOR2.H *
* *
* Programmer : Greg Hjelstrom *
* *
* Start Date : 02/24/97 *
* *
* Last Update : February 24, 1997 [GH] *
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* Scalar Division Operator -- Divide a vector by a scalar *
* Scalar Multiply Operator -- Multiply a vector by a scalar *
* Vector Addition Operator -- Add two vectors *
* Vector Subtraction Operator -- Subract two vectors *
* Vector Inner Product Operator -- Compute the inner or dot product *
* Vector Equality Operator -- Detemine if two vectors are identical *
* Equal_Within_Epsilon -- Determine if two vectors are identical within *
* Vector Inequality Operator -- Detemine if two vectors are identical *
* Swap -- swap two Vector2's *
* Vector2::Is_Valid -- Verifies that all components are valid floats *
* Vector2::Update_Min -- sets each component of the vector to the min of this and a. *
* Vector2::Update_Max -- sets each component of the vector to the max of this and a. *
* Vector2::Scale -- multiply components of a vector by independant scaling factors. *
* Vector2::Lerp -- linearly interpolates two Vector2's *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef VECTOR2_H
#define VECTOR2_H
#include "always.h"
#include "wwmath.h"
#include <math.h>
/*
** 2-Dimensional Vectors
*/
class Vector2
{
public:
union {
float X;
float U;
};
union {
float Y;
float V;
};
// Constructors
WWINLINE Vector2(void) {};
WWINLINE Vector2(const Vector2 & v) { X = v.X; Y = v.Y; }
WWINLINE Vector2(float x, float y) { X = x; Y = y; }
WWINLINE Vector2(const float vector[2]) { X = vector[0]; Y = vector[1]; }
// Assignment
WWINLINE Vector2 & operator = (const Vector2 & v) { X = v[0]; Y = v[1]; return *this; }
WWINLINE void Set(float x, float y) { X = x; Y = y; }
WWINLINE void Set(const Vector2 & v) { X = v.X; Y = v.Y; }
// Array access
WWINLINE float & operator [](int i) { return (&X)[i]; }
WWINLINE const float & operator [](int i) const { return (&X)[i]; }
// normalize, compute length
WWINLINE void Normalize(void);
WWINLINE float Length(void) const;
WWINLINE float Length2(void) const;
// unary operators
WWINLINE Vector2 operator-() const { return Vector2(-X,-Y); }
WWINLINE Vector2 operator+() const { return *this; }
WWINLINE Vector2 & operator += (const Vector2 & v) { X += v.X; Y += v.Y; return *this; }
WWINLINE Vector2 & operator -= (const Vector2 & v) { X -= v.X; Y -= v.Y; return *this; }
WWINLINE Vector2 & operator *= (float k) { X = (float)(X*k); Y=(float)(Y*k); return *this; }
WWINLINE Vector2 & operator /= (float k) { k=1.0f/k; X*=k; Y*=k; return *this; }
// scalar multiplication, division
WWINLINE friend Vector2 operator * (const Vector2 &a,float k);
WWINLINE friend Vector2 operator * (float k,const Vector2 &a);
WWINLINE friend Vector2 operator / (const Vector2 &a,float k);
// vector addition,subtraction
WWINLINE friend Vector2 operator + (const Vector2 &a,const Vector2 &b);
WWINLINE friend Vector2 operator - (const Vector2 &a,const Vector2 &b);
// dot product / inner product
WWINLINE friend float operator * (const Vector2 &a,const Vector2 &b);
static WWINLINE float Dot_Product(const Vector2 &a,const Vector2 &b);
// dot product between a and perpendicular vector to b
static float Perp_Dot_Product(const Vector2 &a,const Vector2 &b);
// Equality operators
friend bool operator == (const Vector2 &a,const Vector2 &b);
friend bool operator != (const Vector2 &a,const Vector2 &b);
WWINLINE friend bool Equal_Within_Epsilon(const Vector2 &a,const Vector2 &b,float epsilon);
// Rotation
WWINLINE void Rotate(float theta);
WWINLINE void Rotate(float s, float c);
WWINLINE bool Rotate_Towards_Vector(Vector2 &target, float max_theta, bool & positive_turn);
WWINLINE bool Rotate_Towards_Vector(Vector2 &target, float max_s, float max_c, bool & positive_turn);
// verify that none of the members of this vector are invalid floats
WWINLINE bool Is_Valid(void) const;
// make this vector the min or max of itself and the passed vector
WWINLINE void Update_Min (const Vector2 & a);
WWINLINE void Update_Max (const Vector2 & a);
WWINLINE void Scale (float a, float b);
WWINLINE void Scale (const Vector2 & a);
static WWINLINE float Distance(const Vector2 &p1, const Vector2 &p2);
static WWINLINE float Quick_Distance(const Vector2 &p1, const Vector2 &p2);
// interpolate between two Vector2's
static void Lerp(const Vector2 & a,const Vector2 & b,float t,Vector2 * set_result);
};
/**************************************************************************
* Scalar Multiply Operator -- Multiply a vector by a scalar *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 02/24/1997 GH : Created. *
*========================================================================*/
WWINLINE Vector2 operator * (const Vector2 &a,float k)
{
float a0k(a[0] * k);
float a1k(a[1] * k);
return Vector2(a0k,a1k);
}
WWINLINE Vector2 operator * (float k, const Vector2 &a)
{
float a0k(a[0] * k);
float a1k(a[1] * k);
return Vector2(a0k,a1k);
}
/**************************************************************************
* Scalar Division Operator -- Divide a vector by a scalar *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*========================================================================*/
WWINLINE Vector2 operator / (const Vector2 &a,float k)
{
float ook=1.0f/k;
float a0ook(a[0] * ook);
float a1ook(a[1] * ook);
return Vector2(a0ook,a1ook);
}
/**************************************************************************
* Vector Addition Operator -- Add two vectors *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 02/24/1997 GH : Created. *
*========================================================================*/
WWINLINE Vector2 operator + (const Vector2 &a,const Vector2 &b)
{
return Vector2(
a.X + b.X,
a.Y + b.Y
);
}
/**************************************************************************
* Vector Subtraction Operator -- Subract two vectors *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 02/24/1997 GH : Created. *
*========================================================================*/
WWINLINE Vector2 operator - (const Vector2 &a,const Vector2 &b)
{
return Vector2(
a.X - b.X,
a.Y - b.Y
);
}
/**************************************************************************
* Vector Inner Product -- Compute the inner or dot product of two vector *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*========================================================================*/
WWINLINE float operator * (const Vector2 &a,const Vector2 &b)
{
return a.X*b.X + a.Y*b.Y;
}
WWINLINE float Vector2::Dot_Product(const Vector2 &a,const Vector2 &b)
{
return a*b;
}
WWINLINE float Vector2::Perp_Dot_Product(const Vector2 &a,const Vector2 &b)
{
return a.X * -b.Y + a.Y * b.X;
}
/**************************************************************************
* Vector Equality Operator -- Detemine if two vectors are identical *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*========================================================================*/
WWINLINE bool operator == (const Vector2 &a,const Vector2 &b)
{
bool a0b0(a[0] == b[0]);
bool a1b1(a[1] == b[1]);
return ( a0b0 & a1b1);
}
/**************************************************************************
* Vector Inequality Operator -- Detemine if two vectors are identical *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*========================================================================*/
WWINLINE bool operator != (const Vector2 &a,const Vector2 &b)
{
bool a0b0(a[0] != b[0]);
bool a1b1(a[1] != b[1]);
return ( a0b0 | a1b1);
}
/**************************************************************************
* Equal_Within_Epsilon -- Determine if two vectors are identical within e*
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*========================================================================*/
WWINLINE bool Equal_Within_Epsilon(const Vector2 &a,const Vector2 &b,float epsilon)
{
return( (WWMath::Fabs(a.X - b.X) < epsilon) && (WWMath::Fabs(a.Y - b.Y) < epsilon) );
}
/**************************************************************************
* Vector2::Normalize -- Normalizes the vector. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*========================================================================*/
WWINLINE void Vector2::Normalize()
{
float len2 = Length2();
if (len2 != 0.0f) {
float oolen = WWMath::Inv_Sqrt(len2);
X *= oolen;
Y *= oolen;
}
}
WWINLINE Vector2 Normalize(const Vector2 & vec)
{
float len2 = vec.Length2();
if (len2 != 0.0f) {
float oolen = WWMath::Inv_Sqrt(len2);
return vec / oolen;
}
return Vector2(0.0f,0.0f);
}
/**************************************************************************
* Vector2::Length -- Returns the length of the vector *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*========================================================================*/
WWINLINE float Vector2::Length() const
{
return (float)WWMath::Sqrt(Length2());
}
/**************************************************************************
* Vector2::Length -- Returns the square of the length of the vector *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*========================================================================*/
WWINLINE float Vector2::Length2() const
{
return (X*X + Y*Y);
}
/**************************************************************************
* Vector2::Rotate -- Rotate vector *
* *
* INPUT: *
* float theta - angle to rotate *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*========================================================================*/
WWINLINE void Vector2::Rotate(float theta)
{
Rotate(WWMath::Sin(theta), WWMath::Cos(theta));
}
/**************************************************************************
* Vector2::Rotate -- Rotate vector *
* *
* INPUT: *
* s - sine of the angle *
* c - cosine of the angle *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*========================================================================*/
WWINLINE void Vector2::Rotate(float s, float c)
{
float new_x = X * c + Y * -s;
float new_y = X * s + Y * c;
X = new_x;
Y = new_y;
}
/**************************************************************************
* Vector2::Rotate -- Rotate towards given vector (stop on reaching it) *
* *
* INPUT: *
* float theta - angle to rotate *
* *
* OUTPUT: *
* bool - true if we have reached the desired vector *
* *
* WARNINGS: *
* This function assumes both vectors are normalized! *
* *
* HISTORY: *
*========================================================================*/
WWINLINE bool Vector2::Rotate_Towards_Vector(Vector2 &target, float max_theta, bool & positive_turn)
{
return Rotate_Towards_Vector(target, WWMath::Sin(max_theta), WWMath::Cos(max_theta), positive_turn);
}
/**************************************************************************
* Vector2::Rotate -- Rotate towards given vector (stop on reaching it) *
* *
* INPUT: *
* s - sine of the angle *
* c - cosine of the angle *
* *
* OUTPUT: *
* bool - true if we have reached the desired vector *
* *
* WARNINGS: *
* This function assumes both vectors are normalized! *
* *
* HISTORY: *
*========================================================================*/
WWINLINE bool Vector2::Rotate_Towards_Vector(Vector2 &target, float max_s, float max_c, bool & positive_turn)
{
bool return_value = false;
positive_turn = Vector2::Perp_Dot_Product(target, *this) > 0.0f;
if (Vector2::Dot_Product(*this, target) >= max_c) {
Set(target);
return_value = true;
} else {
// Determine turn direction and rotate accordingly.
if (positive_turn) {
Rotate(max_s, max_c);
} else {
Rotate(-max_s, max_c);
}
}
return return_value;
}
/***********************************************************************************************
* Swap -- swap two Vector2's *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
WWINLINE void Swap(Vector2 & a,Vector2 & b)
{
Vector2 tmp(a);
a = b;
b = tmp;
}
/***********************************************************************************************
* Vector2::Is_Valid -- Verifies that all components are valid floats *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 10/18/99 gth : Created. *
*=============================================================================================*/
WWINLINE bool Vector2::Is_Valid(void) const
{
return (WWMath::Is_Valid_Float(X) && WWMath::Is_Valid_Float(Y));
}
/***********************************************************************************************
* Vector2::Update_Min -- Set each component of the vector to the min of this and a. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/12/00 IML : Created. *
*=============================================================================================*/
WWINLINE void Vector2::Update_Min (const Vector2 & a)
{
if (a.X < X) X = a.X;
if (a.Y < Y) Y = a.Y;
}
/***********************************************************************************************
* Vector2::Update_Max -- Set each component of the vector to the max of this and a. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/12/00 IML : Created. *
*=============================================================================================*/
WWINLINE void Vector2::Update_Max (const Vector2 & a)
{
if (a.X > X) X = a.X;
if (a.Y > Y) Y = a.Y;
}
/***********************************************************************************************
* Vector2::Scale -- multiply components of a vector by independant scaling factors. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/19/2000 IML : Created. *
*=============================================================================================*/
WWINLINE void Vector2::Scale (float a, float b)
{
X *= a;
Y *= b;
}
/***********************************************************************************************
* Vector2::Scale -- multiply components of a vector by independant scaling factors. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/19/2000 IML : Created. *
*=============================================================================================*/
WWINLINE void Vector2::Scale (const Vector2 & a)
{
X *= a.X;
Y *= a.Y;
}
/***********************************************************************************************
* Quick_Distance -- Fast but inaccurate 2D distance calculation. *
* *
* *
* *
* *
* HISTORY: *
* 11/29/1999MLL: Created. *
*=============================================================================================*/
WWINLINE float Quick_Distance(float x1, float y1, float x2, float y2)
{
// From Graphic Gems I.
float x_diff = x1 - x2;
float y_diff = y1 - y2;
WWMath::Fabs(x_diff);
WWMath::Fabs(y_diff);
if (x_diff > y_diff)
{
return ((y_diff / 2) + x_diff);
}
else
{
return ((x_diff / 2) + y_diff);
}
}
WWINLINE float Vector2::Quick_Distance(const Vector2 &a, const Vector2 &b)
{
return ::Quick_Distance(a.X, a.Y, b.X, b.Y);
}
/***********************************************************************************************
* Distance -- Accurate distance 2D calculation. *
* *
* *
* *
* *
* HISTORY: *
* 11/29/1999MLL: Created. *
*=============================================================================================*/
WWINLINE float Vector2::Distance(const Vector2 &a, const Vector2 &b)
{
Vector2 temp;
temp = a - b;
return (temp.Length());
}
WWINLINE float Distance(float x1, float y1, float x2, float y2)
{
float x_diff = x1 - x2;
float y_diff = y1 - y2;
return (WWMath::Sqrt((x_diff * x_diff) + (y_diff * y_diff)));
}
/***********************************************************************************************
* Vector2::Lerp -- linearly interpolates two Vector2's *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/14/2000 gth : Created. *
*=============================================================================================*/
WWINLINE void Vector2::Lerp(const Vector2 & a,const Vector2 & b,float t,Vector2 * set_result)
{
assert(set_result != NULL);
set_result->X = (a.X + (b.X - a.X)*t);
set_result->Y = (a.Y + (b.Y - a.Y)*t);
}
#endif /* VECTOR2_H */

View File

@@ -0,0 +1,113 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/vector2i.h $*
* *
* Author:: Eric Cosky *
* *
* $Modtime:: 5/10/01 11:37p $*
* *
* $Revision:: 4 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef VECTOR2I_H
#define VECTOR2I_H
#include "always.h"
class Vector2i
{
public:
int I;
int J;
WWINLINE Vector2i(void);
WWINLINE Vector2i(int i,int j);
WWINLINE void Set(int i, int j);
WWINLINE void Swap(Vector2i & other);
WWINLINE bool operator== (const Vector2i & v) const;
WWINLINE bool operator!= (const Vector2i& v) const;
WWINLINE const int& operator[] (int n) const;
WWINLINE int& operator[] (int n);
};
WWINLINE Vector2i::Vector2i(void)
{
}
WWINLINE Vector2i::Vector2i(int i,int j)
{
I = i; J = j;
}
WWINLINE bool Vector2i::operator == (const Vector2i & v) const
{
return (I == v.I && J == v.J );
}
WWINLINE bool Vector2i::operator != (const Vector2i& v) const
{
return !(I == v.I && J == v.J);
}
WWINLINE const int& Vector2i::operator[] (int n) const
{
return ((int*)this)[n];
}
WWINLINE int& Vector2i::operator[] (int n)
{
return ((int*)this)[n];
}
WWINLINE void Vector2i::Set(int i, int j) { I = i; J = j; }
WWINLINE void Vector2i::Swap(Vector2i & other)
{
// this could use MMX..
I ^= other.I;
other.I ^= I;
I ^= other.I;
J ^= other.J;
other.J ^= J;
J ^= other.J;
}
#endif

View File

@@ -0,0 +1,916 @@
/*
** Command & Conquer Generals Zero Hour(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/>.
*/
/* $Header: /Commando/Code/wwmath/vector3.h 40 5/11/01 7:11p Jani_p $ */
/***********************************************************************************************
*** Confidential - Westwood Studios ***
***********************************************************************************************
* *
* Project Name : Westwood 3D *
* *
* File Name : VECTOR3.H *
* *
* Programmer : Greg Hjelstrom *
* *
* Start Date : 02/24/97 *
* *
* Last Update : February 24, 1997 [GH] *
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* Scalar Division Operator -- Divide a vector by a scalar *
* Scalar Multiply Operator -- Multiply a vector by a scalar *
* Vector Addition Operator -- Add two vectors *
* Vector Subtraction Operator -- Subract two vectors *
* Vector Inner Product Operator -- Compute the inner or dot product *
* Vector Equality Operator -- Determine if two vectors are identical *
* Vector Inequality Operator -- Determine if two vectors are identical *
* Equal_Within_Epsilon -- Determine if two vectors are identical within *
* Cross_Product -- compute the cross product of two vectors *
* Vector3::Normalize -- Normalizes the vector. *
* Vector3::Length -- Returns the length of the vector *
* Vector3::Length2 -- Returns the square of the length of the vector *
* Vector3::Quick_Length -- returns a quick approximation of the length *
* Swap -- swap two Vector3's *
* Lerp -- linearly interpolate two Vector3's by an interpolation factor. *
* Lerp -- linearly interpolate two Vector3's without return-by-value *
* Vector3::Add -- Add two vector3's without return-by-value *
* Vector3::Subtract -- Subtract two vector3's without return-by-value *
* Vector3::Update_Min -- sets each component of the vector to the min of this and a *
* Vector3::Update_Max -- Sets each component of the vector to the max of this and a *
* Vector3::Scale -- scale this vector by 3 independent scale factors *
* Vector3::Rotate_X -- rotates this vector around the X axis *
* Vector3::Rotate_X -- Rotates this vector around the x axis *
* Vector3::Rotate_Y -- Rotates this vector around the y axis *
* Vector3::Rotate_Y -- Rotates this vector around the Y axis *
* Vector3::Rotate_Z -- Rotates this vector around the Z axis *
* Vector3::Rotate_Z -- Rotates this vector around the Z axis *
* Vector3::Is_Valid -- Verifies that each component of this vector is a valid float *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef VECTOR3_H
#define VECTOR3_H
#include "always.h"
#include "wwmath.h"
#include <assert.h>
#ifdef _UNIX
#include "osdep.h"
#endif
/*
** Vector3 - 3-Dimensional Vectors
*/
class Vector3
{
public:
float X;
float Y;
float Z;
// Constructors
WWINLINE Vector3(void) {};
WWINLINE Vector3(const Vector3 & v) { X = v.X; Y = v.Y; Z = v.Z; }
WWINLINE Vector3(float x, float y, float z) { X = x; Y = y; Z = z; }
WWINLINE Vector3(const float vector[3]) { X = vector[0]; Y = vector[1]; Z = vector[2]; }
// Assignment
WWINLINE Vector3 & operator = (const Vector3 & v) { X = v.X; Y = v.Y; Z = v.Z; return *this; }
WWINLINE void Set(float x, float y, float z) { X = x; Y = y; Z = z; }
WWINLINE void Set(const Vector3 & that) { X = that.X; Y = that.Y; Z = that.Z; }
// Array access
WWINLINE float & operator [](int i) { return (&X)[i]; }
WWINLINE const float & operator [](int i) const { return (&X)[i]; }
// normalize, compute length
void Normalize(void);
WWINLINE float Length(void) const;
WWINLINE float Length2(void) const;
float Quick_Length(void) const;
void Scale(const Vector3 & scale);
// rotation, (warning, modifies this vector!)
WWINLINE void Rotate_X(float angle);
WWINLINE void Rotate_X(float s_angle,float c_angle);
WWINLINE void Rotate_Y(float angle);
WWINLINE void Rotate_Y(float s_angle,float c_angle);
WWINLINE void Rotate_Z(float angle);
WWINLINE void Rotate_Z(float s_angle,float c_angle);
// unary operators
WWINLINE Vector3 operator-() const { return(Vector3(-X,-Y,-Z)); }
WWINLINE Vector3 operator+() const { return *this; }
WWINLINE Vector3 & operator += (const Vector3 & v) { X += v.X; Y += v.Y; Z += v.Z; return *this; }
WWINLINE Vector3 & operator -= (const Vector3 & v) { X -= v.X; Y -= v.Y; Z -= v.Z; return *this; }
WWINLINE Vector3 & operator *= (float k) { X = X*k; Y=Y*k; Z=Z*k; return *this; }
WWINLINE Vector3 & operator /= (float k) { float ook=1.0f/k; X=X*ook; Y=Y*ook; Z=Z*ook; return *this; }
// scalar multiplication, division
WWINLINE friend Vector3 operator * (const Vector3 &a,float k);
WWINLINE friend Vector3 operator * (float k,const Vector3 &a);
WWINLINE friend Vector3 operator / (const Vector3 &a,float k);
// vector addition,subtraction
WWINLINE friend Vector3 operator + (const Vector3 &a,const Vector3 &b);
WWINLINE friend Vector3 operator - (const Vector3 &a,const Vector3 &b);
// Equality operators
friend bool operator == (const Vector3 &a,const Vector3 &b);
friend bool operator != (const Vector3 &a,const Vector3 &b);
WWINLINE friend bool Equal_Within_Epsilon(const Vector3 &a,const Vector3 &b,float epsilon);
// dot product / inner product
//WWINLINE friend float operator * (const Vector3 &a,const Vector3 &b);
static WWINLINE float Dot_Product(const Vector3 &a,const Vector3 &b);
// cross product / outer product
#ifdef ALLOW_TEMPORARIES
static WWINLINE Vector3 Cross_Product(const Vector3 &a,const Vector3 &b);
#endif
static WWINLINE void Cross_Product(const Vector3 &a,const Vector3 &b,Vector3 * result);
static WWINLINE void Normalized_Cross_Product(const Vector3 &a, const Vector3 &b, Vector3 * result);
static WWINLINE float Cross_Product_X(const Vector3 &a,const Vector3 &b);
static WWINLINE float Cross_Product_Y(const Vector3 &a,const Vector3 &b);
static WWINLINE float Cross_Product_Z(const Vector3 &a,const Vector3 &b);
// add and subtract without return by value
static WWINLINE void Add(const Vector3 & a,const Vector3 & b,Vector3 * c);
static WWINLINE void Subtract(const Vector3 & a,const Vector3 & b,Vector3 * c);
// Line intersection functions.
static WWINLINE float Find_X_At_Y(float y, const Vector3 &p1, const Vector3 &p2);
static WWINLINE float Find_X_At_Z(float z, const Vector3 &p1, const Vector3 &p2);
static WWINLINE float Find_Y_At_X(float x, const Vector3 &p1, const Vector3 &p2);
static WWINLINE float Find_Y_At_Z(float z, const Vector3 &p1, const Vector3 &p2);
static WWINLINE float Find_Z_At_X(float x, const Vector3 &p1, const Vector3 &p2);
static WWINLINE float Find_Z_At_Y(float z, const Vector3 &p1, const Vector3 &p2);
// make this vector the min or max of itself and the passed vector
WWINLINE void Update_Min(const Vector3 & a);
WWINLINE void Update_Max(const Vector3 & a);
WWINLINE void Cap_Absolute_To(const Vector3 & a);
// verify that none of the members of this vector are invalid floats
WWINLINE bool Is_Valid(void) const;
static WWINLINE float Quick_Distance(const Vector3 &p1, const Vector3 &p2);
static WWINLINE float Distance(const Vector3 &p1, const Vector3 &p2);
// Linearly interpolate two Vector3's
static void Lerp(const Vector3 & a, const Vector3 & b, float alpha,Vector3 * set_result);
// Color Conversion
WWINLINE unsigned long Convert_To_ABGR( void ) const;
WWINLINE unsigned long Convert_To_ARGB( void ) const;
};
/**************************************************************************
* Scalar Multiply Operator -- Multiply a vector by a scalar *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 02/24/1997 GH : Created. *
*========================================================================*/
WWINLINE Vector3 operator * (const Vector3 &a,float k)
{
return Vector3((a.X * k),(a.Y * k),(a.Z * k));
}
WWINLINE Vector3 operator * (float k, const Vector3 &a)
{
return Vector3((a.X * k),(a.Y * k),(a.Z * k));
}
/**************************************************************************
* Scalar Division Operator -- Divide a vector by a scalar *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*========================================================================*/
WWINLINE Vector3 operator / (const Vector3 &a,float k)
{
float ook = 1.0f/k;
return Vector3((a.X * ook),(a.Y * ook),(a.Z * ook));
}
/**************************************************************************
* Vector Addition Operator -- Add two vectors *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 02/24/1997 GH : Created. *
*========================================================================*/
WWINLINE Vector3 operator + (const Vector3 &a,const Vector3 &b)
{
return Vector3(
a.X+b.X,
a.Y+b.Y,
a.Z+b.Z
);
}
/**************************************************************************
* Vector Subtraction Operator -- Subract two vectors *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 02/24/1997 GH : Created. *
*========================================================================*/
WWINLINE Vector3 operator - (const Vector3 &a,const Vector3 &b)
{
return Vector3(
a.X-b.X,
a.Y-b.Y,
a.Z-b.Z
);
}
/**************************************************************************
* Vector Inner Product -- Compute the inner or dot product of two vector *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*========================================================================*/
//WWINLINE float operator * (const Vector3 &a,const Vector3 &b)
//{
// return a.X*b.X +
// a.Y*b.Y +
// a.Z*b.Z;
//}
WWINLINE float Vector3::Dot_Product(const Vector3 &a,const Vector3 &b)
{
return a.X*b.X +
a.Y*b.Y +
a.Z*b.Z;
}
/**************************************************************************
* Vector Equality Operator -- Determine if two vectors are identical *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*========================================================================*/
WWINLINE bool operator == (const Vector3 &a,const Vector3 &b)
{
return ( (a.X == b.X) && (a.Y == b.Y) && (a.Z == b.Z));
}
/**************************************************************************
* Vector Inequality Operator -- Determine if two vectors are identical *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*========================================================================*/
WWINLINE bool operator != (const Vector3 &a,const Vector3 &b)
{
return ( (a.X != b.X) || (a.Y != b.Y) || (a.Z != b.Z));
}
/**************************************************************************
* Equal_Within_Epsilon -- Determine if two vectors are identical within e*
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*========================================================================*/
WWINLINE bool Equal_Within_Epsilon(const Vector3 &a,const Vector3 &b,float epsilon)
{
return( (WWMath::Fabs(a.X - b.X) < epsilon) &&
(WWMath::Fabs(a.Y - b.Y) < epsilon) &&
(WWMath::Fabs(a.Z - b.Z) < epsilon) );
}
/**************************************************************************
* Cross_Product -- compute the cross product of two vectors *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*========================================================================*/
#ifdef ALLOW_TEMPORARIES
WWINLINE Vector3 Vector3::Cross_Product(const Vector3 &a,const Vector3 &b)
{
return Vector3(
(a.Y * b.Z - a.Z * b.Y),
(a.Z * b.X - a.X * b.Z),
(a.X * b.Y - a.Y * b.X)
);
}
#endif
WWINLINE void Vector3::Cross_Product(const Vector3 &a,const Vector3 &b,Vector3 * set_result)
{
assert(set_result != &a);
set_result->X = (a.Y * b.Z - a.Z * b.Y);
set_result->Y = (a.Z * b.X - a.X * b.Z);
set_result->Z = (a.X * b.Y - a.Y * b.X);
}
WWINLINE void Vector3::Normalized_Cross_Product(const Vector3 &a,const Vector3 &b,Vector3 * set_result)
{
assert(set_result != &a);
set_result->X = (a.Y * b.Z - a.Z * b.Y);
set_result->Y = (a.Z * b.X - a.X * b.Z);
set_result->Z = (a.X * b.Y - a.Y * b.X);
set_result->Normalize();
}
WWINLINE float Vector3::Cross_Product_X(const Vector3 &a,const Vector3 &b)
{
return a.Y * b.Z - a.Z * b.Y;
}
WWINLINE float Vector3::Cross_Product_Y(const Vector3 &a,const Vector3 &b)
{
return a.Z * b.X - a.X * b.Z;
}
WWINLINE float Vector3::Cross_Product_Z(const Vector3 &a,const Vector3 &b)
{
return a.X * b.Y - a.Y * b.X;
}
/**************************************************************************
* Vector3::Normalize -- Normalizes the vector. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*========================================================================*/
WWINLINE void Vector3::Normalize()
{
float len2 = Length2();
if (len2 != 0.0f)
{
float oolen = WWMath::Inv_Sqrt(len2);
X *= oolen;
Y *= oolen;
Z *= oolen;
}
}
#ifdef ALLOW_TEMPORARIES
WWINLINE Vector3 Normalize(const Vector3 & vec)
{
float len2 = vec.Length2();
if (len2 != 0.0f)
{
float oolen = WWMath::Inv_Sqrt(len2);
return vec * oolen;
}
return vec;
}
#endif
/**************************************************************************
* Vector3::Length -- Returns the length of the vector *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*========================================================================*/
WWINLINE float Vector3::Length() const
{
return WWMath::Sqrt(Length2());
}
/**************************************************************************
* Vector3::Length2 -- Returns the square of the length of the vector *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*========================================================================*/
WWINLINE float Vector3::Length2() const
{
return X*X + Y*Y + Z*Z;
}
/***********************************************************************************************
* Vector3::Quick_Length -- returns a quick approximation of the length *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 7/15/98 GTH : Created. *
*=============================================================================================*/
WWINLINE float Vector3::Quick_Length(void) const
{
// this method of approximating the length comes from Graphics Gems 1 and
// supposedly gives an error of +/- 8%
float max = WWMath::Fabs(X);
float mid = WWMath::Fabs(Y);
float min = WWMath::Fabs(Z);
float tmp;
if (max < mid) { tmp = max; max = mid; mid = tmp; }
if (max < min) { tmp = max; max = min; min = tmp; }
if (mid < min) { tmp = mid; mid = min; min = mid; }
return max + (11.0f / 32.0f)*mid + (1.0f / 4.0f)*min;
}
/***********************************************************************************************
* Swap -- swap two Vector3's *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
WWINLINE void Swap(Vector3 & a,Vector3 & b)
{
Vector3 tmp(a);
a = b;
b = tmp;
}
/***********************************************************************************************
* Lerp -- linearly interpolate two Vector3's without return-by-value *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 10/18/99 gth : Created. *
*=============================================================================================*/
WWINLINE void Vector3::Lerp(const Vector3 & a, const Vector3 & b, float alpha,Vector3 * set_result)
{
assert(set_result != NULL);
set_result->X = (a.X + (b.X - a.X)*alpha);
set_result->Y = (a.Y + (b.Y - a.Y)*alpha);
set_result->Z = (a.Z + (b.Z - a.Z)*alpha);
}
/***********************************************************************************************
* Vector3::Add -- Add two vector3's without return-by-value *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 10/18/99 gth : Created. *
*=============================================================================================*/
WWINLINE void Vector3::Add(const Vector3 &a,const Vector3 &b,Vector3 * set_result)
{
assert(set_result != NULL);
set_result->X = a.X + b.X;
set_result->Y = a.Y + b.Y;
set_result->Z = a.Z + b.Z;
}
/***********************************************************************************************
* Vector3::Subtract -- Subtract two vector3's without return-by-value *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 10/18/99 gth : Created. *
*=============================================================================================*/
WWINLINE void Vector3::Subtract(const Vector3 &a,const Vector3 &b,Vector3 * set_result)
{
assert(set_result != NULL);
set_result->X = a.X - b.X;
set_result->Y = a.Y - b.Y;
set_result->Z = a.Z - b.Z;
}
/***********************************************************************************************
* Vector3::Update_Min -- sets each component of the vector to the min of this and a *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 10/18/99 gth : Created. *
*=============================================================================================*/
WWINLINE void Vector3::Update_Min(const Vector3 & a)
{
if (a.X < X) X = a.X;
if (a.Y < Y) Y = a.Y;
if (a.Z < Z) Z = a.Z;
}
/***********************************************************************************************
* Vector3::Update_Max -- Sets each component of the vector to the max of this and a *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 10/18/99 gth : Created. *
*=============================================================================================*/
WWINLINE void Vector3::Update_Max(const Vector3 & a)
{
if (a.X > X) X = a.X;
if (a.Y > Y) Y = a.Y;
if (a.Z > Z) Z = a.Z;
}
/***********************************************************************************************
* Vector3::Cap_To_Absolute_Of -- Sets each component of the vector to no larger than the -ve or +ve of*
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/29/99 wst : Created. *
*=============================================================================================*/
WWINLINE void Vector3::Cap_Absolute_To(const Vector3 & a)
{
if (X > 0)
{
if (a.X < X) X = a.X;
}
else
{
if (-a.X > X) X = -a.X;
}
if (Y > 0)
{
if (a.Y < Y) Y = a.Y;
}
else
{
if (-a.Y > Y) Y = -a.Y;
}
if (Z > 0)
{
if (a.Z < Z) Z = a.Z;
}
else
{
if (-a.Z > Z) Z = -a.Z;
}
}
/***********************************************************************************************
* Vector3::Scale -- scale this vector by 3 independent scale factors *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 10/18/99 gth : Created. *
*=============================================================================================*/
WWINLINE void Vector3::Scale(const Vector3 & scale)
{
X *= scale.X;
Y *= scale.Y;
Z *= scale.Z;
}
/***********************************************************************************************
* Vector3::Rotate_X -- rotates this vector around the X axis *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 10/18/99 gth : Created. *
*=============================================================================================*/
WWINLINE void Vector3::Rotate_X(float angle)
{
Rotate_X(sinf(angle),cosf(angle));
}
/***********************************************************************************************
* Vector3::Rotate_X -- Rotates this vector around the x axis *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 10/18/99 gth : Created. *
*=============================================================================================*/
WWINLINE void Vector3::Rotate_X(float s_angle,float c_angle)
{
float tmp_y = Y;
float tmp_z = Z;
Y = c_angle * tmp_y - s_angle * tmp_z;
Z = s_angle * tmp_y + c_angle * tmp_z;
}
/***********************************************************************************************
* Vector3::Rotate_Y -- Rotates this vector around the y axis *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 10/18/99 gth : Created. *
*=============================================================================================*/
WWINLINE void Vector3::Rotate_Y(float angle)
{
Rotate_Y(sinf(angle),cosf(angle));
}
/***********************************************************************************************
* Vector3::Rotate_Y -- Rotates this vector around the Y axis *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 10/18/99 gth : Created. *
*=============================================================================================*/
WWINLINE void Vector3::Rotate_Y(float s_angle,float c_angle)
{
float tmp_x = X;
float tmp_z = Z;
X = c_angle * tmp_x + s_angle * tmp_z;
Z = -s_angle * tmp_x + c_angle * tmp_z;
}
/***********************************************************************************************
* Vector3::Rotate_Z -- Rotates this vector around the Z axis *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 10/18/99 gth : Created. *
*=============================================================================================*/
WWINLINE void Vector3::Rotate_Z(float angle)
{
Rotate_Z(sinf(angle),cosf(angle));
}
/***********************************************************************************************
* Vector3::Rotate_Z -- Rotates this vector around the Z axis *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 10/18/99 gth : Created. *
*=============================================================================================*/
WWINLINE void Vector3::Rotate_Z(float s_angle,float c_angle)
{
float tmp_x = X;
float tmp_y = Y;
X = c_angle * tmp_x - s_angle * tmp_y;
Y = s_angle * tmp_x + c_angle * tmp_y;
}
/***********************************************************************************************
* Vector3::Is_Valid -- Verifies that each component of this vector is a valid float *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 10/18/99 gth : Created. *
*=============================================================================================*/
WWINLINE bool Vector3::Is_Valid(void) const
{
return (WWMath::Is_Valid_Float(X) && WWMath::Is_Valid_Float(Y) && WWMath::Is_Valid_Float(Z));
}
WWINLINE float Vector3::Find_X_At_Y(float y, const Vector3 &p1, const Vector3 &p2)
{
return(p1.X + ((y - p1.Y) * ((p2.X - p1.X) / (p2.Y - p1.Y))));
}
WWINLINE float Vector3::Find_X_At_Z(float z, const Vector3 &p1, const Vector3 &p2)
{
return(p1.X + ((z - p1.Z) * ((p2.X - p1.X) / (p2.Z - p1.Z))));
}
WWINLINE float Vector3::Find_Y_At_X(float x, const Vector3 &p1, const Vector3 &p2)
{
return(p1.Y + ((x - p1.X) * ((p2.Y - p1.Y) / (p2.X - p1.X))));
}
WWINLINE float Vector3::Find_Y_At_Z(float z, const Vector3 &p1, const Vector3 &p2)
{
return(p1.Y + ((z - p1.Z) * ((p2.Y - p1.Y) / (p2.Z - p1.Z))));
}
WWINLINE float Vector3::Find_Z_At_X(float x, const Vector3 &p1, const Vector3 &p2)
{
return(p1.Z + ((x - p1.X) * ((p2.Z - p1.Z) / (p2.X - p1.X))));
}
WWINLINE float Vector3::Find_Z_At_Y(float y, const Vector3 &p1, const Vector3 &p2)
{
return(p1.Z + ((y - p1.Y) * ((p2.Z - p1.Z) / (p2.Y - p1.Y))));
}
/***********************************************************************************************
* Vector3::Distance -- Accurate distance calculation. *
* *
* *
* *
* *
* HISTORY: *
* 11/29/1999MLL: Created. *
*=============================================================================================*/
WWINLINE float Vector3::Distance(const Vector3 &p1, const Vector3 &p2)
{
Vector3 temp;
temp = p1 - p2;
return (temp.Length());
}
/***********************************************************************************************
* Vector3::Quick_Distance -- Fast but inaccurate distance calculation. *
* *
* *
* *
* *
* HISTORY: *
* 11/29/1999MLL: Created. *
*=============================================================================================*/
WWINLINE float Vector3::Quick_Distance(const Vector3 &p1, const Vector3 &p2)
{
Vector3 temp;
temp = p1 - p2;
return (temp.Quick_Length());
}
/***********************************************************************************************
* Vector3::Convert_To_ABGR -- Converts to SR packed color . *
* *
* *
* *
* *
* HISTORY: *
* 11/29/1999MLL: Created. *
*=============================================================================================*/
WWINLINE unsigned long Vector3::Convert_To_ABGR( void ) const
{
return (unsigned(255)<<24) |
(unsigned(Z*255.0f)<<16) |
(unsigned(Y*255.0f)<<8) |
(unsigned(X*255.0f));
}
/***********************************************************************************************
* Vector3::Convert_To_ARGB -- Converts to packed color . *
* *
* *
* *
* *
* HISTORY: *
* 11/29/1999MLL: Created. *
*=============================================================================================*/
WWINLINE unsigned long Vector3::Convert_To_ARGB( void ) const
{
return (unsigned(255)<<24) |
(unsigned(X*255.0f)<<16) |
(unsigned(Y*255.0f)<<8) |
(unsigned(Z*255.0f));
}
#endif /* Vector3_H */

View File

@@ -0,0 +1,412 @@
/*
** Command & Conquer Generals Zero Hour(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/>.
*/
/* $Header: /Commando/Code/wwmath/vector4.h 16 5/11/01 7:11p Jani_p $ */
/***************************************************************************
*** Confidential - Westwood Studios ***
***************************************************************************
* *
* Project Name : Westwood 3D *
* *
* File Name : VECTOR4.H *
* *
* Programmer : Greg Hjelstrom *
* *
* Start Date : 02/24/97 *
* *
* Last Update : June 2, 1997 [GH] *
* *
*-------------------------------------------------------------------------*
* Functions: *
* Scalar Division Operator -- Divide a vector by a scalar *
* Scalar Multiply Operator -- Multiply a vector by a scalar *
* Vector Addition Operator -- Add two vectors *
* Vector Subtraction Operator -- Subract two vectors *
* Vector Inner Product Operator -- Compute the inner or dot product *
* Vector Equality Operator -- Detemine if two vectors are identical *
* Vector Inequality Operator -- Detemine if two vectors are identical *
* Swap -- swap two Vector4's *
* Vector4::Is_Valid -- Vector4::Is_Valid *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef VECTOR4_H
#define VECTOR4_H
#include "always.h"
#include "wwmath.h"
#include <math.h>
/*
** Vector4 - 4 dimensional vectors
*/
class Vector4
{
public:
float X;
float Y;
float Z;
float W;
// Constructors
WWINLINE Vector4(void) {};
WWINLINE Vector4(const Vector4 & v) { X = v.X; Y = v.Y; Z = v.Z; W = v.W; }
WWINLINE Vector4(float x, float y, float z, float w) { X = x; Y = y; Z = z; W = w; }
WWINLINE Vector4(const float vector[4]) { X = vector[0]; Y = vector[1]; Z = vector[2]; W = vector[3]; }
// Assignment
WWINLINE Vector4 & operator = (const Vector4 & v) { X = v.X; Y = v.Y; Z = v.Z; W = v.W; return *this; }
WWINLINE void Set(float x, float y, float z, float w) { X = x; Y = y; Z = z; W = w; }
// Array access
WWINLINE float & operator [](int i) { return (&X)[i]; }
WWINLINE const float & operator [](int i) const { return (&X)[i]; }
// normalize, compute length
void Normalize(void);
WWINLINE float Length(void) const;
WWINLINE float Length2(void) const;
// unary operators
WWINLINE Vector4 operator-() const { return(Vector4(-X,-Y,-Z,-W)); }
WWINLINE Vector4 operator+() const { return *this; }
WWINLINE Vector4 & operator += (const Vector4 & v) { X += v.X; Y += v.Y; Z += v.Z; W += v.W; return *this; }
WWINLINE Vector4 & operator -= (const Vector4 & v) { X -= v.X; Y -= v.Y; Z -= v.Z; W += v.W; return *this; }
WWINLINE Vector4 & operator *= (float k) { X = X*k; Y=Y*k; Z=Z*k; W=W*k; return *this; }
WWINLINE Vector4 & operator /= (float k) { k=1.0f/k; X = X*k; Y=Y*k; Z=Z*k; W=W*k; return *this; }
// scalar multiplication, division
WWINLINE friend Vector4 operator * (const Vector4 &a,float k);
WWINLINE friend Vector4 operator * (float k,const Vector4 &a);
WWINLINE friend Vector4 operator / (const Vector4 &a,float k);
// vector addition,subtraction
WWINLINE friend Vector4 operator + (const Vector4 &a,const Vector4 &b);
WWINLINE friend Vector4 operator - (const Vector4 &a,const Vector4 &b);
// dot product / inner product
WWINLINE friend float operator * (const Vector4 &a,const Vector4 &b);
static WWINLINE float Dot_Product(const Vector4 &a,const Vector4 &b);
// Equality operators
friend bool operator == (const Vector4 &a,const Vector4 &b);
friend bool operator != (const Vector4 &a,const Vector4 &b);
// Linearly interpolate between two Vector4's
static Vector4 Lerp(const Vector4 & a, const Vector4 & b, float alpha);
static void Lerp(const Vector4 & a, const Vector4 & b, float alpha,Vector4 * set_result);
// verify that none of the members of this vector are invalid floats
WWINLINE bool Is_Valid(void) const;
};
/**************************************************************************
* Scalar Multiply Operator -- Multiply a vector by a scalar *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 02/24/1997 GH : Created. *
*========================================================================*/
WWINLINE Vector4 operator * (const Vector4 &a,float k)
{
return Vector4((a.X * k),(a.Y * k),(a.Z * k),(a.W * k));
}
WWINLINE Vector4 operator * (float k, const Vector4 &a)
{
return a*k;
}
/**************************************************************************
* Scalar Division Operator -- Divide a vector by a scalar *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*========================================================================*/
WWINLINE Vector4 operator / (const Vector4 &a,float k)
{
float ook=1.0f/k;
return Vector4((a[0] * ook),(a[1] * ook),(a[2] * ook),(a[3] * ook));
}
/**************************************************************************
* Vector Addition Operator -- Add two vectors *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 02/24/1997 GH : Created. *
*========================================================================*/
WWINLINE Vector4 operator + (const Vector4 &a,const Vector4 &b)
{
return Vector4(
a[0]+b[0],
a[1]+b[1],
a[2]+b[2],
a[3]+b[3]
);
}
/**************************************************************************
* Vector Subtraction Operator -- Subract two vectors *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 02/24/1997 GH : Created. *
*========================================================================*/
WWINLINE Vector4 operator - (const Vector4 &a,const Vector4 &b)
{
return Vector4(
a[0]-b[0],
a[1]-b[1],
a[2]-b[2],
a[3]-b[3]
);
}
/**************************************************************************
* Vector Inner Product -- Compute the inner or dot product of two vector *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*========================================================================*/
WWINLINE float operator * (const Vector4 &a,const Vector4 &b)
{
return a[0]*b[0] +
a[1]*b[1] +
a[2]*b[2] +
a[3]*b[3];
}
WWINLINE float Vector4::Dot_Product(const Vector4 &a,const Vector4 &b)
{
return a*b;
}
/**************************************************************************
* Vector Equality Operator -- Detemine if two vectors are identical *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*========================================================================*/
WWINLINE bool operator == (const Vector4 &a,const Vector4 &b)
{
return ( (a[0] == b[0]) && (a[1] == b[1]) && (a[2] == b[2]) && (a[3] == b[3]));
}
/**************************************************************************
* Vector Inequality Operator -- Detemine if two vectors are identical *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*========================================================================*/
WWINLINE bool operator != (const Vector4 &a,const Vector4 &b)
{
return ( (a[0] != b[0]) || (a[1] != b[1]) || (a[2] != b[2]) || (a[3] != b[3]));
}
/**************************************************************************
* Vector4::Normalize -- Normalizes the vector. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*========================================================================*/
WWINLINE void Vector4::Normalize()
{
float len2 = Length2();
if (len2 != 0.0f) {
float oolen = WWMath::Inv_Sqrt(len2);
X *= oolen;
Y *= oolen;
Z *= oolen;
W *= oolen;
}
}
WWINLINE Vector4 Normalize(const Vector4 & vec)
{
float len2 = vec.Length2();
if (len2 != 0.0f) {
float oolen = WWMath::Inv_Sqrt(len2);
return vec * oolen;
}
return Vector4(0.0f,0.0f,0.0f,0.0f);
}
/**************************************************************************
* Vector4::Length -- Returns the length of the vector *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*========================================================================*/
WWINLINE float Vector4::Length() const
{
return WWMath::Sqrt(Length2());
}
/**************************************************************************
* Vector4::Length -- Returns the square of the length of the vector *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*========================================================================*/
WWINLINE float Vector4::Length2() const
{
return X*X + Y*Y + Z*Z + W*W;
}
/***********************************************************************************************
* Swap -- swap two Vector4's *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/02/1997 GH : Created. *
*=============================================================================================*/
WWINLINE void Swap(Vector4 & a,Vector4 & b)
{
Vector4 tmp(a);
a = b;
b = tmp;
}
/***********************************************************************************************
* Lerp -- linearly interpolate two Vector4's by an interpolation factor. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: No checking is done to ensure that alpha is between 0 and 1. *
* *
* HISTORY: *
* 01/14/1999 NH : Created. *
*=============================================================================================*/
WWINLINE Vector4 Lerp(const Vector4 & a, const Vector4 & b, float alpha)
{
return Vector4(
(a.X + (b.X - a.X)*alpha),
(a.Y + (b.Y - a.Y)*alpha),
(a.Z + (b.Z - a.Z)*alpha),
(a.W + (b.W - a.W)*alpha)
);
}
WWINLINE Vector4 Vector4::Lerp(const Vector4 & a, const Vector4 & b, float alpha)
{
return Vector4(
(a.X + (b.X - a.X)*alpha),
(a.Y + (b.Y - a.Y)*alpha),
(a.Z + (b.Z - a.Z)*alpha),
(a.W + (b.W - a.W)*alpha)
);
}
WWINLINE void Vector4::Lerp(const Vector4 & a, const Vector4 & b, float alpha,Vector4 * set_result)
{
set_result->X = (a.X + (b.X - a.X)*alpha);
set_result->Y = (a.Y + (b.Y - a.Y)*alpha);
set_result->Z = (a.Z + (b.Z - a.Z)*alpha);
set_result->X = (a.W + (b.W - a.W)*alpha);
}
/***********************************************************************************************
* Vector4::Is_Valid -- Vector4::Is_Valid *
* *
* verifies that all members of this vector are valid floats *
* *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 10/18/99 gth : Created. *
*=============================================================================================*/
WWINLINE bool Vector4::Is_Valid(void) const
{
return (WWMath::Is_Valid_Float(X) && WWMath::Is_Valid_Float(Y) && WWMath::Is_Valid_Float(Z) && WWMath::Is_Valid_Float(W));
}
#endif /* VECTOR4_H */

View File

@@ -0,0 +1,686 @@
/*
** Command & Conquer Generals Zero Hour(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 : LevelEdit *
* *
* $Archive:: /Commando/Code/wwmath/vehiclecurve.cpp $*
* *
* Author:: Patrick Smith *
* *
* $Modtime:: 6/12/01 10:02a $*
* *
* $Revision:: 8 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "vehiclecurve.h"
#include "vector3.h"
#include "matrix3d.h"
#include "persistfactory.h"
#include "wwmathids.h"
#include "wwmemlog.h"
//////////////////////////////////////////////////////////////////////
// Save-Load stuff
//////////////////////////////////////////////////////////////////////
SimplePersistFactoryClass<VehicleCurveClass,WWMATH_CHUNKID_VEHICLECURVE> _VehicleCurveFactory;
////////////////////////////////////////////////////////////////
// Save/Load constants
////////////////////////////////////////////////////////////////
enum
{
CHUNKID_PARENT = 0x11071217,
CHUNKID_ARC_INFO,
CHUNKID_VARIABLES
};
enum
{
VARID_IS_DIRTY = 1,
VARID_RADIUS,
};
//////////////////////////////////////////////////////////////////////
// Local prototypes
//////////////////////////////////////////////////////////////////////
bool Find_Tangent (const Vector3 &center, float radius, const Vector3 &point, bool clockwise, float *result);
float Get_Angle_Delta (float angle1, float angle2, bool clockwise);
void Find_Turn_Arc (const Matrix3D &transform, float radius, const Vector3 &prev_pt, const Vector3 &curr_pt, const Vector3 &next_pt, Vector3 *arc_center, bool *is_right_turn);
void Find_Tangents (float radius, const Vector3 &prev_pt, const Vector3 &curr_pt, const Vector3 &next_pt, const Vector3 &arc_center, bool is_right_turn, float *point_angle, float *angle_in_delta, float *angle_out_delta);
//////////////////////////////////////////////////////////////////////
//
// Find_Tangent
//
//////////////////////////////////////////////////////////////////////
bool
Find_Tangent
(
const Vector3 & center,
float radius,
const Vector3 & point,
bool clockwise,
float * result
)
{
bool retval = false;
//
// Calculate the distance from the point to the center of the circle
//
float delta_x = point.X - center.X;
float delta_y = point.Y - center.Y;
float dist = ::sqrt (delta_x * delta_x + delta_y * delta_y);
if (dist >= radius) {
//
// Determine the offset angle (from the line between the point and center)
// where the 2 tangent points lie.
//
float angle_offset = WWMath::Acos (radius / dist);
float base_angle = WWMath::Atan2 (delta_x, -delta_y);
base_angle = WWMath::Wrap (base_angle, 0, DEG_TO_RADF (360));
//
// Determine which tangent angle we would come across first, depending
// on our orientation
//
float angle = 0;
if (clockwise) {
angle = base_angle - angle_offset;
} else {
angle = base_angle + angle_offset;
}
angle = WWMath::Wrap (angle, 0, DEG_TO_RADF (360));
(*result) = angle;
retval = true;
}
return retval;
}
//////////////////////////////////////////////////////////////////////
//
// Get_Angle_Delta
//
// Angle deltas need to be wrapped around 360 degrees differently
// depending on the orientation (clockwise/counterclockwise). This
// function takes orientation into consideration when determining
// the delta.
//
//////////////////////////////////////////////////////////////////////
float
Get_Angle_Delta
(
float angle1,
float angle2,
bool clockwise
)
{
float result = angle1 - angle2;
if (clockwise) {
if (angle1 < angle2) {
result = angle1 - (angle2 - DEG_TO_RADF (360));
}
} else {
if (angle1 > angle2) {
result = (angle1 - DEG_TO_RADF (360)) - angle2;
}
}
return result;
}
//////////////////////////////////////////////////////////////////////
//
// Find_Turn_Arc
//
//////////////////////////////////////////////////////////////////////
void
Find_Turn_Arc
(
const Matrix3D & transform,
float radius,
const Vector3 & prev_pt,
const Vector3 & curr_pt,
const Vector3 & next_pt,
Vector3 * arc_center,
bool * is_right_turn
)
{
//
// The center of the turn arc can lie anywhere on the circle centered
// at the current point and 'radius' meters in radius.
//
// We will assume the optimal center of the turn arc will lie at
// the point halfway between the angles formed by the (prev-curr) and
// (next-curr) vectors.
//
float angle1 = ::WWMath::Atan2 ((prev_pt.Y - curr_pt.Y), prev_pt.X - curr_pt.X);
angle1 = WWMath::Wrap (angle1, 0, DEG_TO_RADF (360));
float angle2 = ::WWMath::Atan2 ((next_pt.Y - curr_pt.Y), next_pt.X - curr_pt.X);
angle2 = WWMath::Wrap (angle2, 0, DEG_TO_RADF (360));
float avg_angle = (angle1 + angle2) * 0.5F;
//
// Find the shortest delta between the two angles (either clockwise or
// counterclockwise).
//
float delta1 = WWMath::Fabs (::Get_Angle_Delta (angle1, angle2, true));
float delta2 = WWMath::Fabs (::Get_Angle_Delta (angle1, angle2, false));
if (delta1 < delta2) {
avg_angle = angle1 - (delta1 * 0.5F);
} else {
avg_angle = angle1 + (delta2 * 0.5F);
}
//
// Find the point on the circle at this angle
//
arc_center->X = curr_pt.X + (radius * ::WWMath::Cos (avg_angle));
arc_center->Y = curr_pt.Y + (radius * ::WWMath::Sin (avg_angle));
arc_center->Z = curr_pt.Z;
//
// Will we be making a right turn or a left turn?
//
Vector3 rel_center;
Matrix3D::Inverse_Transform_Vector (transform, *arc_center, &rel_center);
(*is_right_turn) = (rel_center.Y > 0);
return ;
}
//////////////////////////////////////////////////////////////////////
//
// Find_Tangents
//
//////////////////////////////////////////////////////////////////////
void
Find_Tangents
(
float radius,
const Vector3 & prev_pt,
const Vector3 & curr_pt,
const Vector3 & next_pt,
const Vector3 & arc_center,
bool is_right_turn,
float * point_angle,
float * angle_in_delta,
float * angle_out_delta
)
{
//
// Find the 'in' and 'out' tangent angles
//
float angle_in = 0;
float angle_out = 0;
bool valid_in = ::Find_Tangent (arc_center, radius, prev_pt, is_right_turn, &angle_in);
bool valid_out = ::Find_Tangent (arc_center, radius, next_pt, !is_right_turn, &angle_out);
//
// Find the angle where the current position lies on the turn arc
//
(*point_angle) = ::WWMath::Atan2 (curr_pt.X - arc_center.X, -(curr_pt.Y - arc_center.Y));
(*point_angle) = WWMath::Wrap ((*point_angle), 0, DEG_TO_RADF (360));
//
// If the tangent-in is valid, find its delta from the 'point angle.
//
if (valid_in) {
(*angle_in_delta) = ::Get_Angle_Delta (angle_in, (*point_angle), is_right_turn);
} else {
(*angle_in_delta) = 0;
}
//
// If the tangent-out is valid, find its delta from the 'point angle.
//
if (valid_out) {
(*angle_out_delta) = ::Get_Angle_Delta (angle_out, (*point_angle), !is_right_turn);
} else {
(*angle_out_delta) = 0;
}
return ;
}
//////////////////////////////////////////////////////////////////////
//
// Update_Arc_List
//
//////////////////////////////////////////////////////////////////////
void
VehicleCurveClass::Update_Arc_List (void)
{
WWMEMLOG(MEM_PATHFIND);
m_ArcList.Delete_All ();
//
// Bail out if there is nothing to do
//
int count = Key_Count ();
if (count == 0) {
return ;
}
//
// Add a record for the starting point of the arc...
//
ArcInfoStruct arc_start;
arc_start.point_in = Keys[0].Point;
arc_start.point_out = Keys[0].Point;
arc_start.center = Keys[0].Point;
arc_start.point_angle = 0;
arc_start.radius = 0;
arc_start.angle_in_delta = 0;
arc_start.angle_out_delta = 0;
m_ArcList.Add (arc_start);
//
// Loop over each 'interior' point and generate arc information
// for each.
//
for (int index = 1; index < count - 1; index ++) {
//
// Get information about the previous, next, and current points.
//
Vector3 prev_pt;
Vector3 next_pt;
Vector3 curr_pt;
float time = 0;
Get_Key (index-1, &prev_pt, &time);
Get_Key (index, &curr_pt, &time);
Get_Key (index+1, &next_pt, &time);
//
// Determine the last known point on the path
//
Vector3 last_path_pt = m_ArcList[index-1].point_out;
//
// Create a transformation matrix to simulate the vehicle's position and
// orientation at the last point...
//
Vector3 x_vector (curr_pt - last_path_pt);
Vector3 z_vector (0, 0, 1);
x_vector.Normalize ();
#ifdef ALLOW_TEMPORARIES
Vector3 y_vector = Vector3::Cross_Product (x_vector, z_vector);
#else
Vector3 y_vector;
Vector3::Cross_Product (x_vector, z_vector, &y_vector);
#endif
Matrix3D tm (x_vector, y_vector, z_vector, last_path_pt);
//
// Find where the turn arc should be centered and whether we should
// make a right-turn or a left turn...
//
bool is_right_turn = false;
Vector3 arc_center (0, 0, 0);
::Find_Turn_Arc ( tm,
m_Radius,
last_path_pt,
curr_pt,
next_pt,
&arc_center,
&is_right_turn);
//
// Determine where the vehicle should enter and exit the turn
//
float angle_in_delta = 0;
float angle_out_delta = 0;
float point_angle = 0;
::Find_Tangents ( m_Radius,
last_path_pt,
curr_pt,
next_pt,
arc_center,
is_right_turn,
&point_angle,
&angle_in_delta,
&angle_out_delta);
//
// Determine at what points these angles intersect the arc
//
Vector3 point_in (0, 0, 0);
point_in.X = arc_center.X + (m_Radius * ::WWMath::Sin (point_angle + angle_in_delta));
point_in.Y = arc_center.Y + (m_Radius * -::WWMath::Cos (point_angle + angle_in_delta));
Vector3 point_out (0, 0, 0);
point_out.X = arc_center.X + (m_Radius * ::WWMath::Sin (point_angle + angle_out_delta));
point_out.Y = arc_center.Y + (m_Radius * -::WWMath::Cos (point_angle + angle_out_delta));
//
// Sanity check to ensure the vehicle doesn't try to go the long way around the
// turn arc...
//
if ( angle_in_delta > DEG_TO_RADF (200) || angle_out_delta > DEG_TO_RADF (200) ||
angle_in_delta < -DEG_TO_RADF (200) || angle_out_delta < -DEG_TO_RADF (200) )
{
//
// Record information about this arc
//
ArcInfoStruct arc_info;
arc_info.center = curr_pt;
arc_info.point_angle = 0;
arc_info.point_in = curr_pt;
arc_info.point_out = curr_pt;
arc_info.radius = 0;
arc_info.angle_in_delta = 0;
arc_info.angle_out_delta = 0;
m_ArcList.Add (arc_info);
} else {
//
// Record information about this arc
//
ArcInfoStruct arc_info;
arc_info.center = arc_center;
arc_info.point_angle = point_angle;
arc_info.point_in = point_in;
arc_info.point_out = point_out;
arc_info.radius = m_Radius;
arc_info.angle_in_delta = angle_in_delta;
arc_info.angle_out_delta = angle_out_delta;
m_ArcList.Add (arc_info);
}
}
//
// Add a record for the starting point of the arc...
//
if (count > 1) {
ArcInfoStruct arc_end;
arc_end.point_in = Keys[count-1].Point;
arc_end.point_out = Keys[count-1].Point;
arc_end.center = Keys[count-1].Point;
arc_end.point_angle = 0;
arc_end.radius = 0;
arc_end.angle_in_delta = 0;
arc_end.angle_out_delta = 0;
m_ArcList.Add (arc_end);
}
m_IsDirty = false;
return ;
}
//////////////////////////////////////////////////////////////////////
//
// Evaluate
//
//////////////////////////////////////////////////////////////////////
void
VehicleCurveClass::Evaluate (float time, Vector3 *set_val)
{
int count = Keys.Count ();
m_Sharpness = 0;
if (time < Keys[0].Time) {
*set_val = Keys[0].Point;
m_LastTime = Keys[0].Time;
return;
}
if (time >= Keys[count - 1].Time) {
*set_val = Keys[count - 1].Point;
m_LastTime = Keys[count - 1].Time;
return;
}
//
// Update the arc information if any of the keys have changed...
//
if (m_IsDirty) {
Update_Arc_List ();
}
//
// Determine which segment we are on
//
int index0 = 0;
int index1 = 0;
float seg_time = 0;
Find_Interval (time, &index0, &index1, &seg_time);
ArcInfoStruct &arc_info0 = m_ArcList[index0];
ArcInfoStruct &arc_info1 = m_ArcList[index1];
//
// Determine the lengths of each segment of this curve.
// The segments are:
// - Exit curve from prev point
// - Straight line from exit of last curve to enter of this curve
// - Enter curve for the current point
//
float arc_length0 = arc_info0.radius * WWMath::Fabs (arc_info0.angle_out_delta);
float arc_length1 = arc_info1.radius * WWMath::Fabs (arc_info1.angle_in_delta);
float other_length = ((arc_info1.point_in - arc_info0.point_out).Length ()) / 2;
float total_length = arc_length0 + arc_length1 + other_length;
//
// Determine at what times we should switch between parts of the segment
//
float time1 = arc_length0 / total_length;
float time2 = (arc_length0 + other_length) / total_length;
//
// Determine which part of the segment we are on
//
if (seg_time < time1) {
//
// We are on the initial curve of the segment, so calculate where
// on the curve we are...
//
//float percent = seg_time / time1;
//float angle = arc_info0.point_angle + (arc_info0.angle_out_delta) * percent;
float angle = arc_info0.point_angle + arc_info0.angle_out_delta;
set_val->X = arc_info0.center.X + (arc_info0.radius * ::WWMath::Sin (angle));
set_val->Y = arc_info0.center.Y + (arc_info0.radius * -::WWMath::Cos (angle));
m_Sharpness = WWMath::Clamp (WWMath::Fabs (arc_info0.angle_out_delta) / DEG_TO_RADF (15), 0, 1.0F);
m_SharpnessPos.X = set_val->X;
m_SharpnessPos.Y = set_val->Y;
m_SharpnessPos.Z = Keys[index0].Point.Z + (Keys[index1].Point.Z - Keys[index0].Point.Z) * seg_time;
m_LastTime = Keys[index0].Time + (Keys[index1].Time - Keys[index0].Time) * time1;
} else if (seg_time < time2) {
//
// We are on the line between the two curves, so calculate where on
// the line we are
//
float percent = (seg_time - time1) / (time2 - time1);
if (percent == 0) {
set_val->X = arc_info0.point_out.X;
set_val->Y = arc_info0.point_out.Y;
} else {
set_val->X = arc_info1.point_in.X;
set_val->Y = arc_info1.point_in.Y;
}
//set_val->X = arc_info0.point_out.X + (arc_info1.point_in.X - arc_info0.point_out.X) * percent;
//set_val->Y = arc_info0.point_out.Y + (arc_info1.point_in.Y - arc_info0.point_out.Y) * percent;
m_Sharpness = WWMath::Clamp (WWMath::Fabs (arc_info1.angle_out_delta) / DEG_TO_RADF (15), 0, 1.0F);
m_SharpnessPos = arc_info1.point_in;
m_LastTime = Keys[index0].Time + (Keys[index1].Time - Keys[index0].Time) * time2;
} else {
//
// We are on the ending curve of the segment, so calculate where
// on the curve we are...
//
/*float percent = 1.0F - ((seg_time - time2) / (1.0F - time2));
float angle = arc_info1.point_angle + (arc_info1.angle_in_delta * percent);
set_val->X = arc_info1.center.X + (arc_info1.radius * ::WWMath::Sin (angle));
set_val->Y = arc_info1.center.Y + (arc_info1.radius * -::WWMath::Cos (angle)); */
float angle = arc_info1.point_angle + (arc_info1.angle_out_delta);
set_val->X = arc_info1.center.X + (arc_info1.radius * ::WWMath::Sin (angle));
set_val->Y = arc_info1.center.Y + (arc_info1.radius * -::WWMath::Cos (angle));
m_Sharpness = WWMath::Clamp (WWMath::Fabs (arc_info1.angle_out_delta) / DEG_TO_RADF (15), 0, 1.0F);
m_SharpnessPos.X = set_val->X;
m_SharpnessPos.Y = set_val->Y;
m_SharpnessPos.Z = Keys[index0].Point.Z + (Keys[index1].Point.Z - Keys[index0].Point.Z) * seg_time;
m_LastTime = Keys[index1].Time;
}
//
// Our Z value is just a linear interpolation
//
set_val->Z = Keys[index0].Point.Z + (Keys[index1].Point.Z - Keys[index0].Point.Z) * seg_time;
return ;
}
const PersistFactoryClass & VehicleCurveClass::Get_Factory(void) const
{
return _VehicleCurveFactory;
}
////////////////////////////////////////////////////////////////////////////////////////////
//
// Save
//
////////////////////////////////////////////////////////////////////////////////////////////
bool
VehicleCurveClass::Save (ChunkSaveClass &csave)
{
csave.Begin_Chunk (CHUNKID_PARENT);
Curve3DClass::Save (csave);
csave.End_Chunk ();
csave.Begin_Chunk (CHUNKID_VARIABLES);
//
// Save each variable to its own microchunk
//
WRITE_MICRO_CHUNK (csave, VARID_IS_DIRTY, m_IsDirty);
WRITE_MICRO_CHUNK (csave, VARID_RADIUS, m_Radius);
csave.End_Chunk ();
//
// Save each arc info struct to its own chunk
//
for (int index = 0; index < m_ArcList.Count (); index ++) {
ArcInfoStruct &arc_info = m_ArcList[index];
csave.Begin_Chunk (CHUNKID_ARC_INFO);
csave.Write (&arc_info, sizeof (arc_info));
csave.End_Chunk ();
}
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////
//
// Load
//
////////////////////////////////////////////////////////////////////////////////////////////
bool
VehicleCurveClass::Load (ChunkLoadClass &cload)
{
while (cload.Open_Chunk ()) {
switch (cload.Cur_Chunk_ID ()) {
case CHUNKID_PARENT:
Curve3DClass::Load (cload);
break;
case CHUNKID_ARC_INFO:
{
ArcInfoStruct arc_info;
cload.Read (&arc_info, sizeof (arc_info));
m_ArcList.Add (arc_info);
}
break;
case CHUNKID_VARIABLES:
Load_Variables (cload);
break;
}
cload.Close_Chunk ();
}
return true;
}
///////////////////////////////////////////////////////////////////////
//
// Load_Variables
//
///////////////////////////////////////////////////////////////////////
void
VehicleCurveClass::Load_Variables (ChunkLoadClass &cload)
{
//
// Loop through all the microchunks that define the variables
//
while (cload.Open_Micro_Chunk ()) {
switch (cload.Cur_Micro_Chunk_ID ()) {
READ_MICRO_CHUNK (cload, VARID_IS_DIRTY, m_IsDirty);
READ_MICRO_CHUNK (cload, VARID_RADIUS, m_Radius);
}
cload.Close_Micro_Chunk ();
}
return ;
}

View File

@@ -0,0 +1,210 @@
/*
** Command & Conquer Generals Zero Hour(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 : LevelEdit *
* *
* $Archive:: /VSS_Sync/wwmath/vehiclecurve.h $*
* *
* Author:: Patrick Smith *
* *
* $Modtime:: 6/13/01 2:18p $*
* *
* $Revision:: 6 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef __VEHICLE_CURVE_H
#define __VEHICLE_CURVE_H
#include "curve.h"
#include "vector.h"
////////////////////////////////////////////////////////////////////////////////////////////
//
// VehicleCurveClass
//
// A vehicle curve represents the path a vehicle would take through a series of points.
// Each point on the curve passes through a turn-arc of the vehicle. The size of this
// arc is determined by the turn radius which is used to initialize the curve.
//
////////////////////////////////////////////////////////////////////////////////////////////
class VehicleCurveClass : public Curve3DClass
{
public:
///////////////////////////////////////////////////////////////////////////
// Public constructors/destructors
///////////////////////////////////////////////////////////////////////////
VehicleCurveClass (void)
: m_IsDirty (true),
m_Radius (0),
m_LastTime (0),
m_Sharpness (0),
m_SharpnessPos (0, 0, 0),
Curve3DClass () { }
VehicleCurveClass (float radius)
: m_IsDirty (true),
m_Radius (radius),
m_LastTime (0),
m_Sharpness (0),
m_SharpnessPos (0, 0, 0),
Curve3DClass () { }
virtual ~VehicleCurveClass () {}
///////////////////////////////////////////////////////////////////////////
// Public methods
///////////////////////////////////////////////////////////////////////////
//
// Initialization
//
void Initialize_Arc (float radius);
//
// From Curve3DClass
//
void Evaluate (float time, Vector3 *set_val);
void Set_Key (int i,const Vector3 & point);
int Add_Key (const Vector3 & point,float t);
void Remove_Key (int i);
void Clear_Keys (void);
//
// Vehicle curve specific
//
float Get_Current_Sharpness (Vector3 *position) const { *position = m_SharpnessPos; return m_Sharpness; }
float Get_Last_Eval_Time (void) const { return m_LastTime; }
//
// Save-load support
//
virtual const PersistFactoryClass & Get_Factory(void) const;
virtual bool Save(ChunkSaveClass &csave);
virtual bool Load(ChunkLoadClass &cload);
protected:
///////////////////////////////////////////////////////////////////////////
// Protected methods
///////////////////////////////////////////////////////////////////////////
void Update_Arc_List (void);
void Load_Variables (ChunkLoadClass &cload);
///////////////////////////////////////////////////////////////////////////
// Protected data types
///////////////////////////////////////////////////////////////////////////
typedef struct _ArcInfoStruct
{
Vector3 center;
Vector3 point_in;
Vector3 point_out;
float point_angle;
float radius;
float angle_in_delta;
float angle_out_delta;
_ArcInfoStruct (void)
: center (0, 0, 0),
point_in (0, 0, 0),
point_out (0, 0, 0),
point_angle (0),
radius (0),
angle_in_delta (0),
angle_out_delta (0) { }
bool operator== (const _ArcInfoStruct &src) { return false; }
bool operator!= (const _ArcInfoStruct &src) { return true; }
} ArcInfoStruct;
typedef DynamicVectorClass<ArcInfoStruct> ARC_LIST;
///////////////////////////////////////////////////////////////////////////
// Protected member data
///////////////////////////////////////////////////////////////////////////
bool m_IsDirty;
float m_Radius;
ARC_LIST m_ArcList;
float m_LastTime;
float m_Sharpness;
Vector3 m_SharpnessPos;
};
///////////////////////////////////////////////////////////////////////////
// Set_Key
///////////////////////////////////////////////////////////////////////////
inline void
VehicleCurveClass::Set_Key (int i,const Vector3 & point)
{
m_IsDirty = true;
Curve3DClass::Set_Key (i, point);
return ;
}
///////////////////////////////////////////////////////////////////////////
// Add_Key
///////////////////////////////////////////////////////////////////////////
inline int
VehicleCurveClass::Add_Key (const Vector3 & point,float t)
{
m_IsDirty = true;
return Curve3DClass::Add_Key (point, t);
}
///////////////////////////////////////////////////////////////////////////
// Remove_Key
///////////////////////////////////////////////////////////////////////////
inline void
VehicleCurveClass::Remove_Key (int i)
{
m_IsDirty = true;
Curve3DClass::Remove_Key (i);
return ;
}
///////////////////////////////////////////////////////////////////////////
// Clear_Keys
///////////////////////////////////////////////////////////////////////////
inline void
VehicleCurveClass::Clear_Keys (void)
{
m_IsDirty = true;
Curve3DClass::Clear_Keys ();
return ;
}
#endif //__VEHICLE_CURVE_H

View File

@@ -0,0 +1,539 @@
/*
** Command & Conquer Generals Zero Hour(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 : wwmath *
* *
* $Archive:: /Commando/Code/WWMath/vp.cpp $*
* *
* Org Author:: Hector Yee *
* *
* Author : Kenny Mitchell *
* *
* $Modtime:: 06/26/02 4:04p $*
* *
* $Revision:: 12 $*
* *
* 06/26/02 KM Matrix name change to avoid MAX conflicts *
*---------------------------------------------------------------------------------------------*/
#include "vp.h"
#include "vector2.h"
#include "vector3.h"
#include "vector4.h"
#include "matrix3d.h"
#include "matrix4.h"
#include "wwdebug.h"
#include "cpudetect.h"
#include <memory.h>
#define SHUFFLE(x, y, z, w) (((x)&3)<< 6|((y)&3)<<4|((z)&3)<< 2|((w)&3))
#define BROADCAST(XMM, INDEX) __asm shufps XMM,XMM,(((INDEX)&3)<< 6|((INDEX)&3)<<4|((INDEX)&3)<< 2|((INDEX)&3))
#define TRANSPOSE(BX, BY, BZ, BW, TV) \
__asm movaps TV,BZ \
__asm unpcklps BZ,BW \
__asm unpckhps TV,BW \
__asm movaps BW,BX \
__asm unpcklps BX,BY \
__asm unpckhps BW,BY \
__asm movaps BY,BX \
__asm shufps BX,BZ,SHUFFLE(1, 0, 1, 0) \
__asm shufps BY,BZ,SHUFFLE(3, 2, 3, 2) \
__asm movaps BZ,BW \
__asm shufps BZ,TV,SHUFFLE(1, 0, 1, 0) \
__asm shufps BW,TV,SHUFFLE(3, 2, 3, 2)
void VectorProcessorClass::Prefetch(void* address)
{
#if defined (__ICL) // Detect Intel compiler
if (CPUDetectClass::_Has_SSE_Instruction_Set()) {
__asm {
// mov edx,address
// mov eax,[edx]
// prefetchT1 address
}
}
#endif
}
static Vector4 lastrow(0.0f,0.0f,0.0f,1.0f);
void VectorProcessorClass::Transform (Vector3* dst,const Vector3 *src, const Matrix3D& mtx, const int count)
{
if (count<=0) return;
#if defined (__ICL) // Detect Intel compiler
if (CPUDetectClass::_Has_SSE_Instruction_Set()) {
__asm {
mov edx,dst
mov eax,src
mov ebx,mtx
mov edi,count
movups xmm4,[ebx+0]
movups xmm5,[ebx+16]
movups xmm6,[ebx+32]
movups xmm7,lastrow //[ebx+48]
TRANSPOSE(xmm4, xmm5, xmm6, xmm7, xmm0);
shufps xmm4,xmm4,SHUFFLE(2,1,0,0)
shufps xmm5,xmm5,SHUFFLE(2,1,0,0)
shufps xmm6,xmm6,SHUFFLE(2,1,0,0)
shufps xmm7,xmm7,SHUFFLE(2,1,0,0)
mov esi,edx
_lp:
test edi,edi
jz _ulos
test esi,0xf
jz _aligned
movss xmm0,[eax]
movss xmm1,[eax+4]
movss xmm2,[eax+8]
BROADCAST(xmm0,0)
BROADCAST(xmm1,0)
BROADCAST(xmm2,0)
mulps xmm0,xmm4
mulps xmm1,xmm5
mulps xmm2,xmm6
addps xmm0,xmm1
addps xmm0,xmm2
addps xmm0,xmm7
movss [edx],xmm0
movhps [edx+4],xmm0
add eax,12
add edx,12
add esi,12
dec edi
jmp _lp
_aligned:
mov esi,1
mov ecx,edi
and edi,3
and ecx,~3
jz _lp
lea ecx,[ecx+ecx*2]
shl ecx,2
add eax,ecx
add edx,ecx
neg ecx
cmp dword ptr [ebx+12],0
jne _xlatelp
cmp dword ptr [ebx+28],0
jne _xlatelp
cmp dword ptr [ebx+44],0
jne _xlatelp
jmp _noxlatelp
align 16
_noxlatelp:
prefetchnta [eax+ecx+48]
prefetchnta [eax+ecx+48+32]
movss xmm0,[eax+ecx]
BROADCAST(xmm0,0)
movss xmm1,[eax+ecx+4]
BROADCAST(xmm1,0)
movss xmm2,[eax+ecx+8]
BROADCAST(xmm2,0)
mulps xmm0,xmm4
mulps xmm1,xmm5
mulps xmm2,xmm6
addps xmm0,xmm1
addps xmm0,xmm2
movss xmm1,[eax+ecx+12]
BROADCAST(xmm1,0)
movss xmm2,[eax+ecx+16]
BROADCAST(xmm2,0)
movss xmm3,[eax+ecx+20]
BROADCAST(xmm3,0)
mulps xmm1,xmm4
mulps xmm2,xmm5
mulps xmm3,xmm6
addps xmm1,xmm2
addps xmm3,xmm1
movss xmm0,xmm3
shufps xmm0,xmm0,SHUFFLE(0,3,2,1)
movaps [edx+ecx],xmm0
prefetcht0 [edx+ecx+48]
prefetcht0 [edx+ecx+48+32]
movss xmm0,[eax+ecx+24]
BROADCAST(xmm0,0)
movss xmm1,[eax+ecx+24+4]
BROADCAST(xmm1,0)
movss xmm2,[eax+ecx+24+8]
BROADCAST(xmm2,0)
mulps xmm0,xmm4
mulps xmm1,xmm5
mulps xmm2,xmm6
addps xmm0,xmm1
addps xmm0,xmm2
shufps xmm3,xmm0,SHUFFLE(2,1,3,2)
movaps [edx+ecx+16],xmm3
movss xmm1,[eax+ecx+24+12]
BROADCAST(xmm1,0)
movss xmm2,[eax+ecx+24+16]
BROADCAST(xmm2,0)
movss xmm3,[eax+ecx+24+20]
BROADCAST(xmm3,0)
mulps xmm1,xmm4
mulps xmm2,xmm5
mulps xmm3,xmm6
addps xmm1,xmm2
addps xmm1,xmm3
shufps xmm0,xmm0,SHUFFLE(2,1,0,3)
movss xmm1,xmm0
movaps [edx+ecx+32],xmm1
add ecx,48
js _noxlatelp
jmp _lp
align 16
_xlatelp:
prefetchnta [eax+ecx+48]
prefetchnta [eax+ecx+48+32]
movss xmm0,[eax+ecx]
BROADCAST(xmm0,0)
movss xmm1,[eax+ecx+4]
BROADCAST(xmm1,0)
movss xmm2,[eax+ecx+8]
BROADCAST(xmm2,0)
mulps xmm0,xmm4
mulps xmm1,xmm5
mulps xmm2,xmm6
addps xmm0,xmm1
addps xmm0,xmm2
addps xmm0,xmm7
movss xmm1,[eax+ecx+12]
BROADCAST(xmm1,0)
movss xmm2,[eax+ecx+16]
BROADCAST(xmm2,0)
movss xmm3,[eax+ecx+20]
BROADCAST(xmm3,0)
mulps xmm1,xmm4
mulps xmm2,xmm5
mulps xmm3,xmm6
addps xmm1,xmm2
addps xmm3,xmm1
addps xmm3,xmm7
movss xmm0,xmm3
shufps xmm0,xmm0,SHUFFLE(0,3,2,1)
movaps [edx+ecx],xmm0
prefetcht0 [edx+ecx+48]
prefetcht0 [edx+ecx+48+32]
movss xmm0,[eax+ecx+24]
BROADCAST(xmm0,0)
movss xmm1,[eax+ecx+24+4]
BROADCAST(xmm1,0)
movss xmm2,[eax+ecx+24+8]
BROADCAST(xmm2,0)
mulps xmm0,xmm4
mulps xmm1,xmm5
mulps xmm2,xmm6
addps xmm0,xmm1
addps xmm0,xmm2
addps xmm0,xmm7
shufps xmm3,xmm0,SHUFFLE(2,1,3,2)
movaps [edx+ecx+16],xmm3
movss xmm1,[eax+ecx+24+12]
BROADCAST(xmm1,0)
movss xmm2,[eax+ecx+24+16]
BROADCAST(xmm2,0)
movss xmm3,[eax+ecx+24+20]
BROADCAST(xmm3,0)
mulps xmm1,xmm4
mulps xmm2,xmm5
mulps xmm3,xmm6
addps xmm1,xmm2
addps xmm1,xmm3
addps xmm1,xmm7
shufps xmm0,xmm0,SHUFFLE(2,1,0,3)
movss xmm1,xmm0
movaps [edx+ecx+32],xmm1
add ecx,48
js _xlatelp
jmp _lp
_ulos:
}
}
else
#endif
{
mtx.mulVector3Array(src, dst, count);
}
}
void VectorProcessorClass::Transform(Vector4* dst,const Vector3 *src, const Matrix4x4& matrix, const int count)
{
if (count<=0) return;
int i;
for (i=0; i<count; i++)
{
dst[i]=matrix*src[i];
}
}
void VectorProcessorClass::Copy(Vector2 *dst, const Vector2 *src, int count)
{
if (count<=0) return;
memcpy(dst,src,sizeof(Vector2)*count);
}
void VectorProcessorClass::Copy(unsigned *dst, const unsigned *src, int count)
{
if (count<=0) return;
memcpy(dst,src,sizeof(unsigned)*count);
}
void VectorProcessorClass::Copy(Vector3 *dst, const Vector3 *src, int count)
{
if (count<=0) return;
memcpy(dst,src,sizeof(Vector3)*count);
}
void VectorProcessorClass::Copy(Vector4 *dst, const Vector4 *src, int count)
{
if (count<=0) return;
memcpy(dst,src,sizeof(Vector4)*count);
}
void VectorProcessorClass::Copy(Vector4 *dst,const Vector3 *src, const float * srca, const int count)
{
if (count<=0) return;
int i;
for (i=0; i<count; i++)
{
dst[i].X=src[i].X;
dst[i].Y=src[i].Y;
dst[i].Z=src[i].Z;
dst[i].W=srca[i];
}
}
void VectorProcessorClass::Copy(Vector4 *dst,const Vector3 *src, const float srca, const int count)
{
if (count<=0) return;
int i;
for (i=0; i<count; i++)
{
dst[i].X=src[i].X;
dst[i].Y=src[i].Y;
dst[i].Z=src[i].Z;
dst[i].W=srca;
}
}
void VectorProcessorClass::Copy(Vector4 *dst,const Vector3 &src, const float * srca, const int count)
{
if (count<=0) return;
int i;
for (i=0; i<count; i++)
{
dst[i].X=src.X;
dst[i].Y=src.Y;
dst[i].Z=src.Z;
dst[i].W=srca[i];
}
}
void VectorProcessorClass::CopyIndexed (unsigned *dst,const unsigned *src, const unsigned int *index, int count)
{
if (count<=0) return;
int i;
for (i=0; i<count; i++)
{
dst[i]=src[index[i]];
}
}
void VectorProcessorClass::CopyIndexed (Vector2 *dst,const Vector2 *src, const unsigned int *index, int count)
{
if (count<=0) return;
int i;
for (i=0; i<count; i++)
{
dst[i]=src[index[i]];
}
}
void VectorProcessorClass::CopyIndexed (Vector3 *dst,const Vector3 *src, const unsigned int *index, int count)
{
if (count<=0) return;
int i;
for (i=0; i<count; i++)
{
dst[i]=src[index[i]];
}
}
void VectorProcessorClass::CopyIndexed (Vector4 *dst,const Vector4 *src, const unsigned int *index, int count)
{
if (count<=0) return;
int i;
for (i=0; i<count; i++)
{
dst[i]=src[index[i]];
}
}
void VectorProcessorClass::CopyIndexed(unsigned char* dst, const unsigned char* src, const unsigned int *index, int count)
{
if (count<=0) return;
int i;
for (i=0; i<count; i++)
{
dst[i]=src[index[i]];
}
}
void VectorProcessorClass::CopyIndexed(float* dst, float* src, const unsigned int *index, int count)
{
if (count<=0) return;
int i;
for (i=0; i<count; i++)
{
dst[i]=src[index[i]];
}
}
void VectorProcessorClass::Clamp(Vector4 *dst,const Vector4 *src, const float min, const float max, const int count)
{
if (count<=0) return;
int i;
for (i=0; i<count; i++)
{
dst[i].X=(src[i].X<min)?min:src[i].X;
dst[i].X=(src[i].X>max)?max:src[i].X;
dst[i].Y=(src[i].Y<min)?min:src[i].Y;
dst[i].Y=(src[i].Y>max)?max:src[i].Y;
dst[i].Z=(src[i].Z<min)?min:src[i].Z;
dst[i].Z=(src[i].Z>max)?max:src[i].Z;
dst[i].W=(src[i].W<min)?min:src[i].W;
dst[i].W=(src[i].W>max)?max:src[i].W;
}
}
void VectorProcessorClass::Clear(Vector3*dst, const int count)
{
if (count<=0) return;
memset(dst,0,sizeof(Vector3)*count);
}
void VectorProcessorClass::Normalize(Vector3 *dst, const int count)
{
if (count<=0) return;
int i;
for (i=0; i<count; i++)
dst[i].Normalize();
}
void VectorProcessorClass::MinMax(Vector3 *src, Vector3 &min, Vector3 &max, const int count)
{
if (count<=0) return;
min=*src;
max=*src;
int i;
for (i=1; i<count; i++)
{
min.X=MIN(min.X,src[i].X);
min.Y=MIN(min.Y,src[i].Y);
min.Z=MIN(min.Z,src[i].Z);
max.X=MAX(max.X,src[i].X);
max.Y=MAX(max.Y,src[i].Y);
max.Z=MAX(max.Z,src[i].Z);
}
}
void VectorProcessorClass::MulAdd(float * dest,float multiplier,float add,int count)
{
for (int i=0; i<count; i++) {
dest[i] = dest[i] * multiplier + add;
}
}
void VectorProcessorClass::DotProduct(float *dst, const Vector3 &a, const Vector3 *b,const int count)
{
for (int i=0; i<count; i++)
dst[i]=Vector3::Dot_Product(a,b[i]);
}
void VectorProcessorClass::ClampMin(float *dst, float *src, const float min, const int count)
{
for (int i=0; i<count; i++)
dst[i]=(src[i]>min?src[i]:min);
}
void VectorProcessorClass::Power(float *dst, float *src, const float pow, const int count)
{
for (int i=0; i<count; i++)
dst[i]=powf(src[i],pow);
}

View File

@@ -0,0 +1,93 @@
/*
** Command & Conquer Generals Zero Hour(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 : wwmath *
* *
* $Archive:: /Commando/Code/WWMath/vp.h $*
* *
* Org Author:: Hector Yee *
* *
* Author : Kenny Mitchell *
* *
* $Modtime:: 06/26/02 4:04p $*
* *
* $Revision:: 13 $*
* *
* 06/26/02 KM Matrix name change to avoid MAX conflicts *
*---------------------------------------------------------------------------------------------*
* Functions: *
* Transform - transforms a vector array given Matrix3D *
* Copy - Copies data from source to destination *
* CopyIndexed-copies dst[]=src[index[]] *
* Clear - clears array to zero *
* Normalize - normalize the array *
* MinMax - Finds the min and max of the array *
* *
*----------------------------------------------------------------------------------------------*
*/
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef VECTORPROCESSOR_H
#define VECTORPROCESSOR_H
class Vector2;
class Vector3;
class Vector4;
class Matrix3D;
class Matrix4x4;
class VectorProcessorClass
{
public:
static void Transform(Vector3* dst,const Vector3 *src, const Matrix3D& matrix, const int count);
static void Transform(Vector4* dst,const Vector3 *src, const Matrix4x4& matrix, const int count);
static void Copy(unsigned *dst,const unsigned *src, const int count);
static void Copy(Vector2 *dst,const Vector2 *src, const int count);
static void Copy(Vector3 *dst,const Vector3 *src, const int count);
static void Copy(Vector4 *dst,const Vector4 *src, const int count);
static void Copy(Vector4 *dst,const Vector3 *src, const float * srca, const int count);
static void Copy(Vector4 *dst,const Vector3 *src, const float srca, const int count);
static void Copy(Vector4 *dst,const Vector3 &src, const float * srca, const int count);
static void CopyIndexed(unsigned *dst,const unsigned *src, const unsigned int *index, const int count);
static void CopyIndexed(Vector2 *dst,const Vector2 *src, const unsigned int *index, const int count);
static void CopyIndexed(Vector3 *dst,const Vector3 *src, const unsigned int *index, const int count);
static void CopyIndexed(Vector4 *dst,const Vector4 *src, const unsigned int *index, const int count);
static void CopyIndexed(unsigned char* dst, const unsigned char* src, const unsigned int *index, int count);
static void CopyIndexed(float* dst, float* src, const unsigned int *index, int count);
static void Clamp(Vector4 *dst,const Vector4 *src, const float min, const float max, const int count);
static void Clear (Vector3 *dst, const int count);
static void Normalize(Vector3 *dst, const int count);
static void MinMax(Vector3 *src, Vector3 &min, Vector3 &max, const int count);
static void MulAdd(float * dest,float multiplier,float add,int count);
static void Prefetch(void* address);
static void DotProduct(float *dst, const Vector3 &a, const Vector3 *b,const int count);
static void ClampMin(float *dst, float *src, const float min, const int count);
static void Power(float *dst, float *src, const float pow, const int count);
};
#endif // VECTORPROCESSOR_H

View File

@@ -0,0 +1,95 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/wwmath.cpp $*
* *
* Author:: Eric_c *
* *
* $Modtime:: 5/10/01 10:52p $*
* *
* $Revision:: 11 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "wwmath.h"
#include "wwhack.h"
#include "lookuptable.h"
#include <stdlib.h>
#include "wwdebug.h"
#include "wwprofile.h"
// TODO: convert to use loouptablemanager...
float _FastAcosTable[ARC_TABLE_SIZE];
float _FastAsinTable[ARC_TABLE_SIZE];
float _FastSinTable[SIN_TABLE_SIZE];
float _FastInvSinTable[SIN_TABLE_SIZE];
void WWMath::Init(void)
{
LookupTableMgrClass::Init();
for (int a=0;a<ARC_TABLE_SIZE;++a) {
float cv=float(a-ARC_TABLE_SIZE/2)*(1.0f/(ARC_TABLE_SIZE/2));
_FastAcosTable[a]=acos(cv);
_FastAsinTable[a]=asin(cv);
}
for (a=0;a<SIN_TABLE_SIZE;++a) {
float cv= (float)a * 2.0f * WWMATH_PI / SIN_TABLE_SIZE; //float(a-SIN_TABLE_SIZE/2)*(1.0f/(SIN_TABLE_SIZE/2));
_FastSinTable[a]=sin(cv);
if (a>0) {
_FastInvSinTable[a]=1.0f/_FastSinTable[a];
} else {
_FastInvSinTable[a]=WWMATH_FLOAT_MAX;
}
}
}
void WWMath::Shutdown(void)
{
LookupTableMgrClass::Shutdown();
}
float WWMath::Random_Float(void)
{
return ((float)(rand() & 0xFFF)) / (float)(0xFFF);
}
/*
** Force link some modules from this library.
*/
void Do_Force_Links(void)
{
FORCE_LINK(curve);
FORCE_LINK(hermitespline);
FORCE_LINK(catmullromspline);
FORCE_LINK(cardinalspline);
FORCE_LINK(tcbspline);
}

View File

@@ -0,0 +1,484 @@
# Microsoft Developer Studio Project File - Name="wwmath" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Static Library" 0x0104
CFG=wwmath - Win32 DebugW3D
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "wwmath.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "wwmath.mak" CFG="wwmath - Win32 DebugW3D"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "wwmath - Win32 Debug" (based on "Win32 (x86) Static Library")
!MESSAGE "wwmath - Win32 Release" (based on "Win32 (x86) Static Library")
!MESSAGE "wwmath - Win32 Profile" (based on "Win32 (x86) Static Library")
!MESSAGE "wwmath - Win32 Internal" (based on "Win32 (x86) Static Library")
!MESSAGE "wwmath - Win32 DebugW3D" (based on "Win32 (x86) Static Library")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""$/Commando/Code/wwmath", UEFAAAAA"
# PROP Scc_LocalPath "."
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "wwmath - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "wwmath_3"
# PROP BASE Intermediate_Dir "wwmath_3"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /Z7 /Od /I "..\Library" /D "G_CODE_BASE" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR /YX /FD /c
# ADD CPP /nologo /G6 /MDd /W3 /WX /Gi /GX /Zi /O2 /Ob2 /I "..\wwlib" /I "..\wwdebug" /I "..\wwsaveload" /D "G_CODE_BASE" /D "DIRECTX" /D "_DEBUG" /D WINVER=0x400 /D "_WINDOWS" /D "WIN32_LEAN_AND_MEAN" /D "WIN32" /YX /FD /c
# SUBTRACT CPP /Fr
# ADD BASE RSC /l 0x409
# ADD RSC /l 0x409
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo /out:"..\Libs\wwmathd.lib"
# ADD LIB32 /nologo /out:"..\..\..\Lib\WWMathDebug.lib"
!ELSEIF "$(CFG)" == "wwmath - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "wwmath_4"
# PROP BASE Intermediate_Dir "wwmath_4"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /I "..\Library" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c
# ADD CPP /nologo /G6 /MD /W3 /WX /Gi /GX /O2 /Ob2 /I "..\wwlib" /I "..\wwdebug" /I "..\wwsaveload" /D WINVER=0x400 /D "_WINDOWS" /D "WIN32_LEAN_AND_MEAN" /D "NDEBUG" /D "WIN32" /D "IG_DEBUG_STACKTRACE" /YX /FD /c
# SUBTRACT CPP /Fr
# ADD BASE RSC /l 0x409
# ADD RSC /l 0x409
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo /out:"..\Libs\wwmath.lib"
# ADD LIB32 /nologo /out:"..\..\..\Lib\WWMath.lib"
!ELSEIF "$(CFG)" == "wwmath - Win32 Profile"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "wwmath__"
# PROP BASE Intermediate_Dir "wwmath__"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Profile"
# PROP Intermediate_Dir "Profile"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MT /W3 /GX /O2 /I "..\Library" /I "..\wwdebug" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /FR /YX /FD /c
# ADD CPP /nologo /MD /W3 /WX /GX /Zi /O2 /Op /Ob2 /I "..\wwlib" /I "..\wwdebug" /I "..\wwsaveload" /D "NDEBUG" /D "WWDEBUG" /D WINVER=0x400 /D "_WINDOWS" /D "WIN32_LEAN_AND_MEAN" /D "WIN32" /D "_PROFILE" /YX /FD /Gh /c
# ADD BASE RSC /l 0x409
# ADD RSC /l 0x409
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo /out:"..\Libs\release\wwmath.lib"
# ADD LIB32 /nologo /out:"..\..\..\Lib\WWMathProfile.lib"
!ELSEIF "$(CFG)" == "wwmath - Win32 Internal"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Internal"
# PROP BASE Intermediate_Dir "Internal"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Internal"
# PROP Intermediate_Dir "Internal"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MD /W4 /WX /Gi /GX /O2 /Ob2 /I "..\wwlib" /I "..\wwdebug" /I "..\wwsaveload" /D "NDEBUG" /D WINVER=0x400 /D "_WINDOWS" /D "WIN32_LEAN_AND_MEAN" /D "WIN32" /YX /FD /c
# SUBTRACT BASE CPP /Fr
# ADD CPP /nologo /G6 /MD /W3 /WX /Gi /GX /Zi /O2 /I "..\wwlib" /I "..\wwdebug" /I "..\wwsaveload" /D "NDEBUG" /D WINVER=0x400 /D "_WINDOWS" /D "WIN32_LEAN_AND_MEAN" /D "WIN32" /D "_INTERNAL" /YX /FD /c
# SUBTRACT CPP /Fr
# ADD BASE RSC /l 0x409
# ADD RSC /l 0x409
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo /out:"..\..\..\Lib\WWMath.lib"
# ADD LIB32 /nologo /out:"..\..\..\Lib\WWMathInternal.lib"
!ELSEIF "$(CFG)" == "wwmath - Win32 DebugW3D"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "DebugW3D"
# PROP BASE Intermediate_Dir "DebugW3D"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "DebugW3D"
# PROP Intermediate_Dir "DebugW3D"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MDd /W3 /WX /Gm /Gi /GR /GX /ZI /Od /I "..\wwlib" /I "..\wwdebug" /I "..\wwsaveload" /D "G_CODE_BASE" /D "DIRECTX" /D "_DEBUG" /D "WWDEBUG" /D WINVER=0x400 /D "_WINDOWS" /D "WIN32_LEAN_AND_MEAN" /D "WIN32" /Fr /YX /FD /c
# ADD CPP /nologo /G6 /MDd /W3 /WX /Gm /Gi /GX /ZI /Od /I "..\wwlib" /I "..\wwdebug" /I "..\wwsaveload" /D "G_CODE_BASE" /D "DIRECTX" /D "_DEBUG" /D "WWDEBUG" /D WINVER=0x400 /D "_WINDOWS" /D "WIN32_LEAN_AND_MEAN" /D "WIN32" /YX /FD /c
# SUBTRACT CPP /Fr
# ADD BASE RSC /l 0x409
# ADD RSC /l 0x409
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo /out:"..\..\..\Lib\WWMathDebug.lib"
# ADD LIB32 /nologo /out:"..\..\..\Lib\WWMathDebugW3D.lib"
!ENDIF
# Begin Target
# Name "wwmath - Win32 Debug"
# Name "wwmath - Win32 Release"
# Name "wwmath - Win32 Profile"
# Name "wwmath - Win32 Internal"
# Name "wwmath - Win32 DebugW3D"
# Begin Group "Source"
# PROP Default_Filter "c;cpp"
# Begin Source File
SOURCE=.\aabox.cpp
# End Source File
# Begin Source File
SOURCE=.\aabtreecull.cpp
# End Source File
# Begin Source File
SOURCE=.\cardinalspline.cpp
# End Source File
# Begin Source File
SOURCE=.\catmullromspline.cpp
# End Source File
# Begin Source File
SOURCE=.\colmath.cpp
# End Source File
# Begin Source File
SOURCE=.\colmathaabox.cpp
# End Source File
# Begin Source File
SOURCE=.\colmathaabtri.cpp
# End Source File
# Begin Source File
SOURCE=.\colmathfrustum.cpp
# End Source File
# Begin Source File
SOURCE=.\colmathline.cpp
# End Source File
# Begin Source File
SOURCE=.\colmathobbobb.cpp
# End Source File
# Begin Source File
SOURCE=.\colmathobbox.cpp
# End Source File
# Begin Source File
SOURCE=.\colmathobbtri.cpp
# End Source File
# Begin Source File
SOURCE=.\colmathplane.cpp
# End Source File
# Begin Source File
SOURCE=.\colmathsphere.cpp
# End Source File
# Begin Source File
SOURCE=.\cullsys.cpp
# End Source File
# Begin Source File
SOURCE=.\curve.cpp
# End Source File
# Begin Source File
SOURCE=.\euler.cpp
# End Source File
# Begin Source File
SOURCE=.\frustum.cpp
# End Source File
# Begin Source File
SOURCE=.\gridcull.cpp
# End Source File
# Begin Source File
SOURCE=.\hermitespline.cpp
# End Source File
# Begin Source File
SOURCE=.\lineseg.cpp
# End Source File
# Begin Source File
SOURCE=.\lookuptable.cpp
# End Source File
# Begin Source File
SOURCE=.\matrix3.cpp
# End Source File
# Begin Source File
SOURCE=.\matrix3d.cpp
# End Source File
# Begin Source File
SOURCE=.\matrix4.cpp
# End Source File
# Begin Source File
SOURCE=.\obbox.cpp
# End Source File
# Begin Source File
SOURCE=.\ode.cpp
# End Source File
# Begin Source File
SOURCE=.\pot.cpp
# End Source File
# Begin Source File
SOURCE=.\quat.cpp
# End Source File
# Begin Source File
SOURCE=.\tcbspline.cpp
# End Source File
# Begin Source File
SOURCE=.\tri.cpp
# End Source File
# Begin Source File
SOURCE=.\v3_rnd.cpp
# End Source File
# Begin Source File
SOURCE=.\vehiclecurve.cpp
# End Source File
# Begin Source File
SOURCE=.\vp.cpp
# End Source File
# Begin Source File
SOURCE=.\wwmath.cpp
# End Source File
# End Group
# Begin Group "Headers"
# PROP Default_Filter "h"
# Begin Source File
SOURCE=.\aabox.h
# End Source File
# Begin Source File
SOURCE=.\aabtreecull.h
# End Source File
# Begin Source File
SOURCE=.\aaplane.h
# End Source File
# Begin Source File
SOURCE=.\cardinalspline.h
# End Source File
# Begin Source File
SOURCE=.\castres.h
# End Source File
# Begin Source File
SOURCE=.\catmullromspline.h
# End Source File
# Begin Source File
SOURCE=.\colmath.h
# End Source File
# Begin Source File
SOURCE=.\colmathaabox.h
# End Source File
# Begin Source File
SOURCE=.\colmathfrustum.h
# End Source File
# Begin Source File
SOURCE=.\colmathinlines.h
# End Source File
# Begin Source File
SOURCE=.\colmathline.h
# End Source File
# Begin Source File
SOURCE=.\colmathplane.h
# End Source File
# Begin Source File
SOURCE=.\cullsys.h
# End Source File
# Begin Source File
SOURCE=.\culltype.h
# End Source File
# Begin Source File
SOURCE=.\curve.h
# End Source File
# Begin Source File
SOURCE=.\euler.h
# End Source File
# Begin Source File
SOURCE=.\frustum.h
# End Source File
# Begin Source File
SOURCE=.\gridcull.h
# End Source File
# Begin Source File
SOURCE=.\hermitespline.h
# End Source File
# Begin Source File
SOURCE=.\lineseg.h
# End Source File
# Begin Source File
SOURCE=.\lookuptable.h
# End Source File
# Begin Source File
SOURCE=.\matrix3.h
# End Source File
# Begin Source File
SOURCE=.\matrix3d.h
# End Source File
# Begin Source File
SOURCE=.\matrix4.h
# End Source File
# Begin Source File
SOURCE=.\obbox.h
# End Source File
# Begin Source File
SOURCE=.\ode.h
# End Source File
# Begin Source File
SOURCE=.\plane.h
# End Source File
# Begin Source File
SOURCE=.\pot.h
# End Source File
# Begin Source File
SOURCE=.\quat.h
# End Source File
# Begin Source File
SOURCE=.\rect.h
# End Source File
# Begin Source File
SOURCE=.\sphere.h
# End Source File
# Begin Source File
SOURCE=.\tcbspline.h
# End Source File
# Begin Source File
SOURCE=.\tri.h
# End Source File
# Begin Source File
SOURCE=.\v3_rnd.h
# End Source File
# Begin Source File
SOURCE=.\vector2.h
# End Source File
# Begin Source File
SOURCE=.\vector2i.h
# End Source File
# Begin Source File
SOURCE=.\vector3.h
# End Source File
# Begin Source File
SOURCE=.\Vector3i.h
# End Source File
# Begin Source File
SOURCE=.\vector4.h
# End Source File
# Begin Source File
SOURCE=.\vehiclecurve.h
# End Source File
# Begin Source File
SOURCE=.\vp.h
# End Source File
# Begin Source File
SOURCE=.\wwmath.h
# End Source File
# Begin Source File
SOURCE=.\wwmathids.h
# End Source File
# End Group
# End Target
# End Project

View File

@@ -0,0 +1,658 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/wwmath.h $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 8/26/01 2:22p $*
* *
* $Revision:: 64 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef WWMATH_H
#define WWMATH_H
#include "always.h"
#include <math.h>
#include <float.h>
#include <assert.h>
#include <float.h>
/*
** Some global constants.
*/
#define WWMATH_EPSILON 0.0001f
#define WWMATH_EPSILON2 WWMATH_EPSILON * WWMATH_EPSILON
#define WWMATH_PI 3.141592654f
#define WWMATH_FLOAT_MAX (FLT_MAX)
#define WWMATH_FLOAT_MIN (FLT_MIN)
#define WWMATH_SQRT2 1.414213562f
#define WWMATH_SQRT3 1.732050808f
#define WWMATH_OOSQRT2 0.707106781f
#define WWMATH_OOSQRT3 0.577350269f
// (DRM 05/07/01) Temporarily eliminated _fastcall
// on non-Microsoft compatible compilers. Jani
// should be replacing this soon.
#ifndef _MSC_VER
#define __fastcall
#endif // _MSC_VER
/*
** Macros to convert between degrees and radians
*/
#ifndef RAD_TO_DEG
#define RAD_TO_DEG(x) (((double)x)*180.0/WWMATH_PI)
#endif
#ifndef DEG_TO_RAD
#define DEG_TO_RAD(x) (((double)x)*WWMATH_PI/180.0)
#endif
#ifndef RAD_TO_DEGF
#define RAD_TO_DEGF(x) (((float)x)*180.0f/WWMATH_PI)
#endif
#ifndef DEG_TO_RADF
#define DEG_TO_RADF(x) (((float)x)*WWMATH_PI/180.0f)
#endif
const int ARC_TABLE_SIZE=1024;
const int SIN_TABLE_SIZE=1024;
extern float _FastAcosTable[ARC_TABLE_SIZE];
extern float _FastAsinTable[ARC_TABLE_SIZE];
extern float _FastSinTable[SIN_TABLE_SIZE];
extern float _FastInvSinTable[SIN_TABLE_SIZE];
/*
** Some simple math functions which work on the built-in types.
** Include the various other header files in the WWMATH library
** in order to get matrices, quaternions, etc.
*/
class WWMath
{
public:
// Initialization and Shutdown. Other math sub-systems which require initialization and
// shutdown processing will be handled in these functions
static void Init(void);
static void Shutdown(void);
// These are meant to be a collection of small math utility functions to be optimized at some point.
static WWINLINE float Fabs(float val)
{
int value=*(int*)&val;
value&=0x7fffffff;
return *(float*)&value;
}
static WWINLINE int Float_To_Int_Chop(const float& f);
static WWINLINE int Float_To_Int_Floor(const float& f);
#if defined(_MSC_VER) && defined(_M_IX86)
static WWINLINE float Cos(float val);
static WWINLINE float Sin(float val);
static WWINLINE float Sqrt(float val);
static float __fastcall Inv_Sqrt(float a); // Some 30% faster inverse square root than regular C++ compiled, from Intel's math library
static WWINLINE long Float_To_Long(float f);
#else
static float Cos(float val);
static float Sin(float val);
static float Sqrt(float val);
static float Inv_Sqrt(float a);
static long Float_To_Long(float f);
#endif
static WWINLINE float Fast_Sin(float val);
static WWINLINE float Fast_Inv_Sin(float val);
static WWINLINE float Fast_Cos(float val);
static WWINLINE float Fast_Inv_Cos(float val);
static WWINLINE float Fast_Acos(float val);
static WWINLINE float Acos(float val);
static WWINLINE float Fast_Asin(float val);
static WWINLINE float Asin(float val);
static float Atan(float x) { return static_cast<float>(atan(x)); }
static float Atan2(float y,float x) { return static_cast<float>(atan2(y,x)); }
static float Sign(float val);
static float Ceil(float val) { return ceilf(val); }
static float Floor(float val) { return floorf(val); }
static bool Fast_Is_Float_Positive(const float & val);
static bool Is_Power_Of_2(const unsigned int val);
static float Random_Float(void);
static float Random_Float(float min,float max);
static float Clamp(float val, float min = 0.0f, float max = 1.0f);
static double Clamp(double val, double min = 0.0f, double max = 1.0f);
static int Clamp_Int(int val, int min_val, int max_val);
static float Wrap(float val, float min = 0.0f, float max = 1.0f);
static double Wrap(double val, double min = 0.0f, double max = 1.0f);
static float Min(float a, float b);
static float Max(float a, float b);
static int Float_As_Int(const float f) { return *((int*)&f); }
static float Lerp(float a, float b, float lerp );
static double Lerp(double a, double b, float lerp );
static long Float_To_Long(double f);
static unsigned char Unit_Float_To_Byte(float f) { return (unsigned char)(f*255.0f); }
static float Byte_To_Unit_Float(unsigned char byte) { return ((float)byte) / 255.0f; }
static bool Is_Valid_Float(float x);
static bool Is_Valid_Double(double x);
};
WWINLINE float WWMath::Sign(float val)
{
if (val > 0.0f) {
return +1.0f;
}
if (val < 0.0f) {
return -1.0f;
}
return 0.0f;
}
WWINLINE bool WWMath::Fast_Is_Float_Positive(const float & val)
{
return !((*(int *)(&val)) & 0x80000000);
}
WWINLINE bool WWMath::Is_Power_Of_2(const unsigned int val)
{
return !((val)&val-1);
}
WWINLINE float WWMath::Random_Float(float min,float max)
{
return Random_Float() * (max-min) + min;
}
WWINLINE float WWMath::Clamp(float val, float min /*= 0.0f*/, float max /*= 1.0f*/)
{
if(val < min) return min;
if(val > max) return max;
return val;
}
WWINLINE double WWMath::Clamp(double val, double min /*= 0.0f*/, double max /*= 1.0f*/)
{
if(val < min) return min;
if(val > max) return max;
return val;
}
WWINLINE int WWMath::Clamp_Int(int val, int min_val, int max_val)
{
if(val < min_val) return min_val;
if(val > max_val) return max_val;
return val;
}
WWINLINE float WWMath::Wrap(float val, float min /*= 0.0f*/, float max /*= 1.0f*/)
{
// Implemented as an if rather than a while, to long loops
if ( val >= max ) val -= (max-min);
if ( val < min ) val += (max-min);
if ( val < min ) {
val = min;
}
if ( val > max ) {
val = max;
}
return val;
}
WWINLINE double WWMath::Wrap(double val, double min /*= 0.0f*/, double max /*= 1.0f*/)
{
// Implemented as an if rather than a while, to long loops
if ( val >= max ) val -= (max-min);
if ( val < min ) val += (max-min);
if ( val < min ) {
val = min;
}
if ( val > max ) {
val = max;
}
return val;
}
WWINLINE float WWMath::Min(float a, float b)
{
if (a<b) return a;
return b;
}
WWINLINE float WWMath::Max(float a, float b)
{
if (a>b) return a;
return b;
}
WWINLINE float WWMath::Lerp(float a, float b, float lerp )
{
return (a + (b - a)*lerp);
}
WWINLINE double WWMath::Lerp(double a, double b, float lerp )
{
return (a + (b - a)*lerp);
}
WWINLINE bool WWMath::Is_Valid_Float(float x)
{
unsigned long * plong = (unsigned long *)(&x);
unsigned long exponent = ((*plong) & 0x7F800000) >> (32-9);
// if exponent is 0xFF, this is a NAN
if (exponent == 0xFF) {
return false;
}
return true;
}
WWINLINE bool WWMath::Is_Valid_Double(double x)
{
unsigned long * plong = (unsigned long *)(&x) + 1;
unsigned long exponent = ((*plong) & 0x7FF00000) >> (32-12);
// if exponent is 0x7FF, this is a NAN
if (exponent == 0x7FF) {
return false;
}
return true;
}
// ----------------------------------------------------------------------------
// Float to long
// ----------------------------------------------------------------------------
#if defined(_MSC_VER) && defined(_M_IX86)
WWINLINE long WWMath::Float_To_Long(float f)
{
long i;
__asm {
fld [f]
fistp [i]
}
return i;
}
#else
WWINLINE long WWMath::Float_To_Long(float f)
{
return (long) f;
}
#endif
WWINLINE long WWMath::Float_To_Long(double f)
{
#if defined(_MSC_VER) && defined(_M_IX86)
long retval;
__asm fld qword ptr [f]
__asm fistp dword ptr [retval]
return retval;
#else
return (long) f;
#endif
}
// ----------------------------------------------------------------------------
// Cos
// ----------------------------------------------------------------------------
#if defined(_MSC_VER) && defined(_M_IX86)
WWINLINE float WWMath::Cos(float val)
{
float retval;
__asm {
fld [val]
fcos
fstp [retval]
}
return retval;
}
#else
WWINLINE float WWMath::Cos(float val)
{
return cosf(val);
}
#endif
// ----------------------------------------------------------------------------
// Sin
// ----------------------------------------------------------------------------
#if defined(_MSC_VER) && defined(_M_IX86)
WWINLINE float WWMath::Sin(float val)
{
float retval;
__asm {
fld [val]
fsin
fstp [retval]
}
return retval;
}
#else
WWINLINE float WWMath::Sin(float val)
{
return sinf(val);
}
#endif
// ----------------------------------------------------------------------------
// Fast, table based sin
// ----------------------------------------------------------------------------
WWINLINE float WWMath::Fast_Sin(float val)
{
val*=float(SIN_TABLE_SIZE) / (2.0f * WWMATH_PI);
int idx0=Float_To_Int_Floor(val);
int idx1=idx0+1;
float frac=val-(float)idx0;
idx0 = ((unsigned)idx0) & (SIN_TABLE_SIZE-1);
idx1 = ((unsigned)idx1) & (SIN_TABLE_SIZE-1);
return (1.0f - frac) * _FastSinTable[idx0] + frac * _FastSinTable[idx1];
}
// ----------------------------------------------------------------------------
// Fast, table based 1.0f/sin
// ----------------------------------------------------------------------------
WWINLINE float WWMath::Fast_Inv_Sin(float val)
{
#if 0 // TODO: more testing, not reliable!
float index = val * float(SIN_TABLE_SIZE) / (2.0f * WWMATH_PI);
int idx0=Float_To_Int_Floor(index);
int idx1=idx0+1;
float frac=val-(float)idx0;
idx0 = ((unsigned)idx0) & (SIN_TABLE_SIZE-1);
idx1 = ((unsigned)idx1) & (SIN_TABLE_SIZE-1);
// The table becomes inaccurate near 0 and 2pi so fall back to doing a divide.
const int BUFFER = 16;
if ((idx0 <= BUFFER) || (idx0 >= SIN_TABLE_SIZE-BUFFER-1)) {
return 1.0f / WWMath::Fast_Sin(val);
} else {
return (1.0f - frac) * _FastInvSinTable[idx0] + frac * _FastInvSinTable[idx1];
}
#else
return 1.0f / WWMath::Fast_Sin(val);
#endif
}
// ----------------------------------------------------------------------------
// Fast, table based cos
// ----------------------------------------------------------------------------
WWINLINE float WWMath::Fast_Cos(float val)
{
val+=(WWMATH_PI * 0.5f);
val*=float(SIN_TABLE_SIZE) / (2.0f * WWMATH_PI);
int idx0=Float_To_Int_Floor(val);
int idx1=idx0+1;
float frac=val-(float)idx0;
idx0 = ((unsigned)idx0) & (SIN_TABLE_SIZE-1);
idx1 = ((unsigned)idx1) & (SIN_TABLE_SIZE-1);
return (1.0f - frac) * _FastSinTable[idx0] + frac * _FastSinTable[idx1];
}
// ----------------------------------------------------------------------------
// Fast, table based 1.0f/cos
// ----------------------------------------------------------------------------
WWINLINE float WWMath::Fast_Inv_Cos(float val)
{
#if 0 // TODO: more testing, not reliable!
float index = val + (WWMATH_PI * 0.5f);
index *= float(SIN_TABLE_SIZE) / (2.0f * WWMATH_PI);
int idx0=Float_To_Int_Chop(index);
int idx1=idx0+1;
float frac=val-(float)idx0;
idx0 = ((unsigned)idx0) & (SIN_TABLE_SIZE-1);
idx1 = ((unsigned)idx1) & (SIN_TABLE_SIZE-1);
// The table becomes inaccurate near 0 and 2pi so fall back to doing a divide.
if ((idx0 <= 2) || (idx0 >= SIN_TABLE_SIZE-3)) {
return 1.0f / WWMath::Fast_Cos(val);
} else {
return (1.0f - frac) * _FastInvSinTable[idx0] + frac * _FastInvSinTable[idx1];
}
#else
return 1.0f / WWMath::Fast_Cos(val);
#endif
}
// ----------------------------------------------------------------------------
// Fast, table based arc cos
// ----------------------------------------------------------------------------
WWINLINE float WWMath::Fast_Acos(float val)
{
// Near -1 and +1, the table becomes too inaccurate
if (WWMath::Fabs(val) > 0.975f) {
return WWMath::Acos(val);
}
val*=float(ARC_TABLE_SIZE/2);
int idx0=Float_To_Int_Floor(val);
int idx1=idx0+1;
float frac=val-(float)idx0;
idx0+=ARC_TABLE_SIZE/2;
idx1+=ARC_TABLE_SIZE/2;
// we dont even get close to the edge of the table...
assert((idx0 >= 0) && (idx0 < ARC_TABLE_SIZE));
assert((idx1 >= 0) && (idx1 < ARC_TABLE_SIZE));
// compute and return the interpolated value
return (1.0f - frac) * _FastAcosTable[idx0] + frac * _FastAcosTable[idx1];
}
// ----------------------------------------------------------------------------
// Arc cos
// ----------------------------------------------------------------------------
WWINLINE float WWMath::Acos(float val)
{
return (float)acos(val);
}
// ----------------------------------------------------------------------------
// Fast, table based arc sin
// ----------------------------------------------------------------------------
WWINLINE float WWMath::Fast_Asin(float val)
{
// Near -1 and +1, the table becomes too inaccurate
if (WWMath::Fabs(val) > 0.975f) {
return WWMath::Asin(val);
}
val*=float(ARC_TABLE_SIZE/2);
int idx0=Float_To_Int_Floor(val);
int idx1=idx0+1;
float frac=val-(float)idx0;
idx0+=ARC_TABLE_SIZE/2;
idx1+=ARC_TABLE_SIZE/2;
// we dont even get close to the edge of the table...
assert((idx0 >= 0) && (idx0 < ARC_TABLE_SIZE));
assert((idx1 >= 0) && (idx1 < ARC_TABLE_SIZE));
// compute and return the interpolated value
return (1.0f - frac) * _FastAsinTable[idx0] + frac * _FastAsinTable[idx1];
}
// ----------------------------------------------------------------------------
// Arc sin
// ----------------------------------------------------------------------------
WWINLINE float WWMath::Asin(float val)
{
return (float)asin(val);
}
// ----------------------------------------------------------------------------
// Sqrt
// ----------------------------------------------------------------------------
#if defined(_MSC_VER) && defined(_M_IX86)
WWINLINE float WWMath::Sqrt(float val)
{
float retval;
__asm {
fld [val]
fsqrt
fstp [retval]
}
return retval;
}
#else
WWINLINE float WWMath::Sqrt(float val)
{
return (float)sqrt(val);
}
#endif
WWINLINE int WWMath::Float_To_Int_Chop(const float& f)
{
int a = *reinterpret_cast<const int*>(&f); // take bit pattern of float into a register
int sign = (a>>31); // sign = 0xFFFFFFFF if original value is negative, 0 if positive
int mantissa = (a&((1<<23)-1))|(1<<23); // extract mantissa and add the hidden bit
int exponent = ((a&0x7fffffff)>>23)-127; // extract the exponent
int r = ((unsigned int)(mantissa)<<8)>>(31-exponent); // ((1<<exponent)*mantissa)>>24 -- (we know that mantissa > (1<<24))
return ((r ^ (sign)) - sign ) &~ (exponent>>31); // add original sign. If exponent was negative, make return value 0.
}
WWINLINE int WWMath::Float_To_Int_Floor (const float& f)
{
int a = *reinterpret_cast<const int*>(&f); // take bit pattern of float into a register
int sign = (a>>31); // sign = 0xFFFFFFFF if original value is negative, 0 if positive
a&=0x7fffffff; // we don't need the sign any more
int exponent = (a>>23)-127; // extract the exponent
int expsign = ~(exponent>>31); // 0xFFFFFFFF if exponent is positive, 0 otherwise
int imask = ( (1<<(31-(exponent))))-1; // mask for true integer values
int mantissa = (a&((1<<23)-1)); // extract mantissa (without the hidden bit)
int r = ((unsigned int)(mantissa|(1<<23))<<8)>>(31-exponent); // ((1<<exponent)*(mantissa|hidden bit))>>24 -- (we know that mantissa > (1<<24))
r = ((r & expsign) ^ (sign)) + ((!((mantissa<<8)&imask)&(expsign^((a-1)>>31)))&sign); // if (fabs(value)<1.0) value = 0; copy sign; if (value < 0 && value==(int)(value)) value++;
return r;
}
// ----------------------------------------------------------------------------
// Inverse square root
// ----------------------------------------------------------------------------
#if defined(_MSC_VER) && defined(_M_IX86)
WWINLINE __declspec(naked) float __fastcall WWMath::Inv_Sqrt(float a)
{
__asm {
mov eax, 0be6eb508h
mov DWORD PTR [esp-12],03fc00000h ; 1.5 on the stack
sub eax, DWORD PTR [esp+4]; a
sub DWORD PTR [esp+4], 800000h ; a/2 a=Y0
shr eax, 1 ; firs approx in eax=R0
mov DWORD PTR [esp-8], eax
fld DWORD PTR [esp-8] ;r
fmul st, st ;r*r
fld DWORD PTR [esp-8] ;r
fxch st(1)
fmul DWORD PTR [esp+4];a ;r*r*y0
fld DWORD PTR [esp-12];load 1.5
fld st(0)
fsub st,st(2) ;r1 = 1.5 - y1
;x1 = st(3)
;y1 = st(2)
;1.5 = st(1)
;r1 = st(0)
fld st(1)
fxch st(1)
fmul st(3),st ; y2=y1*r1*...
fmul st(3),st ; y2=y1*r1*r1
fmulp st(4),st ; x2=x1*r1
fsub st,st(2) ; r2=1.5-y2
;x2=st(3)
;y2=st(2)
;1.5=st(1)
;r2 = st(0)
fmul st(2),st ;y3=y2*r2*...
fmul st(3),st ;x3=x2*r2
fmulp st(2),st ;y3=y2*r2*r2
fxch st(1)
fsubp st(1),st ;r3= 1.5 - y3
;x3 = st(1)
;r3 = st(0)
fmulp st(1), st
ret 4
}
}
#else
WWINLINE float WWMath::Inv_Sqrt(float val)
{
return 1.0f / (float)sqrt(val);
}
#endif
#endif

View File

@@ -0,0 +1,66 @@
/*
** Command & Conquer Generals Zero Hour(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 : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/wwmathids.h $*
* *
* Original Author:: Greg Hjelstrom *
* *
* $Author:: Jani_p $*
* *
* $Modtime:: 5/04/01 8:42p $*
* *
* $Revision:: 3 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#ifndef WWMATHIDS_H
#define WWMATHIDS_H
#include "saveloadids.h"
/*
** Persist Factory ID's for WWMATH
*/
enum
{
WWMATH_CHUNKID_LINEARCURVE1D = CHUNKID_WWMATH_BEGIN,
WWMATH_CHUNKID_HERMITESPLINE1D,
WWMATH_CHUNKID_CATMULLROMSPLINE1D,
WWMATH_CHUNKID_CARDINALSPLINE1D,
WWMATH_CHUNKID_TCBSPLINE1D,
WWMATH_CHUNKID_LINEARCURVE3D = CHUNKID_WWMATH_BEGIN + 0x100,
WWMATH_CHUNKID_HERMITESPLINE3D,
WWMATH_CHUNKID_CATMULLROMSPLINE3D,
WWMATH_CHUNKID_CARDINALSPLINE3D,
WWMATH_CHUNKID_TCBSPLINE3D,
WWMATH_CHUNKID_VEHICLECURVE
};
#endif //WWMATHIDS_H