Initial commit of Command & Conquer Renegade source code.

This commit is contained in:
LFeenanEA
2025-02-27 16:39:46 +00:00
parent 74ab8fa5e0
commit 58ed459113
4918 changed files with 1366710 additions and 0 deletions

79
Code/WWMath/aabox.cpp Normal file
View File

@@ -0,0 +1,79 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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);
}

679
Code/WWMath/aabox.h Normal file
View File

@@ -0,0 +1,679 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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

1616
Code/WWMath/aabtreecull.cpp Normal file

File diff suppressed because it is too large Load Diff

344
Code/WWMath/aabtreecull.h Normal file
View File

@@ -0,0 +1,344 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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

83
Code/WWMath/aaplane.h Normal file
View File

@@ -0,0 +1,83 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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 Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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 Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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

75
Code/WWMath/castres.h Normal file
View File

@@ -0,0 +1,75 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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 Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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 Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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

68
Code/WWMath/colmath.cpp Normal file
View File

@@ -0,0 +1,68 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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;
}

312
Code/WWMath/colmath.h Normal file
View File

@@ -0,0 +1,312 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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,598 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/colmathaabox.cpp $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 10/31/02 11:27a $*
* *
* $Revision:: 24 $*
* *
*---------------------------------------------------------------------------------------------*
* 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;
}

109
Code/WWMath/colmathaabox.h Normal file
View File

@@ -0,0 +1,109 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/colmathaabox.h $*
* *
* Original Author:: Greg Hjelstrom *
* *
* $Author:: Byon_g $*
* *
* $Modtime:: 10/31/02 11:26a $*
* *
* $Revision:: 10 $*
* *
*---------------------------------------------------------------------------------------------*
* 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 Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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 Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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 Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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

484
Code/WWMath/colmathline.cpp Normal file
View File

@@ -0,0 +1,484 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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;
}

64
Code/WWMath/colmathline.h Normal file
View File

@@ -0,0 +1,64 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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,168 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/colmathobbox.cpp $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 11/14/00 2:46p $*
* *
* $Revision:: 8 $*
* *
*---------------------------------------------------------------------------------------------*
* 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;
Matrix3::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,194 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/colmathplane.cpp $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 3/29/00 4:41p $*
* *
* $Revision:: 9 $*
* *
*---------------------------------------------------------------------------------------------*
* 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;
Matrix3::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
Matrix3::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;
}

154
Code/WWMath/colmathplane.h Normal file
View File

@@ -0,0 +1,154 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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 Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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 );
}

154
Code/WWMath/cullsys.cpp Normal file
View File

@@ -0,0 +1,154 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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;
}

201
Code/WWMath/cullsys.h Normal file
View File

@@ -0,0 +1,201 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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

62
Code/WWMath/culltype.h Normal file
View File

@@ -0,0 +1,62 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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

591
Code/WWMath/curve.cpp Normal file
View File

@@ -0,0 +1,591 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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;
}

179
Code/WWMath/curve.h Normal file
View File

@@ -0,0 +1,179 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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

373
Code/WWMath/euler.cpp Normal file
View File

@@ -0,0 +1,373 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* $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);
}

127
Code/WWMath/euler.h Normal file
View File

@@ -0,0 +1,127 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* $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*/

138
Code/WWMath/frustum.cpp Normal file
View File

@@ -0,0 +1,138 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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).
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;
}
}

73
Code/WWMath/frustum.h Normal file
View File

@@ -0,0 +1,73 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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

1024
Code/WWMath/gridcull.cpp Normal file

File diff suppressed because it is too large Load Diff

706
Code/WWMath/gridcull.h Normal file
View File

@@ -0,0 +1,706 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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 Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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;
}

143
Code/WWMath/hermitespline.h Normal file
View File

@@ -0,0 +1,143 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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

217
Code/WWMath/lineseg.cpp Normal file
View File

@@ -0,0 +1,217 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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;
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;
//
// 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;
}

93
Code/WWMath/lineseg.h Normal file
View File

@@ -0,0 +1,93 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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

250
Code/WWMath/lookuptable.cpp Normal file
View File

@@ -0,0 +1,250 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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 = new 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);
}
}

161
Code/WWMath/lookuptable.h Normal file
View File

@@ -0,0 +1,161 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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

390
Code/WWMath/matrix3.cpp Normal file
View File

@@ -0,0 +1,390 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/matrix3.cpp $*
* *
* Author:: Greg_h *
* *
* $Modtime:: 5/11/01 10:10a $*
* *
* $Revision:: 16 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "matrix3.h"
#include "matrix3d.h"
#include "matrix4.h"
#include "quat.h"
/*
** Some pre-initialized Matrix3's
*/
const Matrix3 Matrix3::Identity
(
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0
);
const Matrix3 Matrix3::RotateX90
(
1.0, 0.0, 0.0,
0.0, 0.0, -1.0,
0.0, 1.0, 0.0
);
const Matrix3 Matrix3::RotateX180
(
1.0, 0.0, 0.0,
0.0, -1.0, 0.0,
0.0, 0.0, -1.0
);
const Matrix3 Matrix3::RotateX270
(
1.0, 0.0, 0.0,
0.0, 0.0, 1.0,
0.0, -1.0, 0.0
);
const Matrix3 Matrix3::RotateY90
(
0.0, 0.0, 1.0,
0.0, 1.0, 0.0,
-1.0, 0.0, 0.0
);
const Matrix3 Matrix3::RotateY180
(
-1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, -1.0
);
const Matrix3 Matrix3::RotateY270
(
0.0, 0.0, -1.0,
0.0, 1.0, 0.0,
1.0, 0.0, 0.0
);
const Matrix3 Matrix3::RotateZ90
(
0.0, -1.0, 0.0,
1.0, 0.0, 0.0,
0.0, 0.0, 1.0
);
const Matrix3 Matrix3::RotateZ180
(
-1.0, 0.0, 0.0,
0.0, -1.0, 0.0,
0.0, 0.0, 1.0
);
const Matrix3 Matrix3::RotateZ270
(
0.0, 1.0, 0.0,
-1.0, 0.0, 0.0,
0.0, 0.0, 1.0
);
/***********************************************************************************************
* Matrix3::Matrix3 -- Convert a Matrix3D (fake 4x4) to a Matrix3 *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/02/1997 GH : Created. *
*=============================================================================================*/
Matrix3::Matrix3(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]);
}
Matrix3::Matrix3(const Matrix4 & 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 Matrix3::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 Matrix3::Set(const Matrix4 & 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 Matrix3::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]));
}
Matrix3 & Matrix3::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;
}
Matrix3 & Matrix3::operator = (const Matrix4 & 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 Matrix3::Multiply(const Matrix3D & a, const Matrix3 & b,Matrix3 * 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 Matrix3::Multiply(const Matrix3 & a, const Matrix3D & b,Matrix3 * 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
}
Matrix3 operator * (const Matrix3D & a, const Matrix3 & b)
{
#define ROWCOL(i,j) a[i][0]*b[0][j] + a[i][1]*b[1][j] + a[i][2]*b[2][j]
return Matrix3(
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
}
Matrix3 operator * (const Matrix3 & 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 Matrix3(
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 Matrix3::Compute_Jacobi_Rotation(int i,int j,Matrix3 * r,Matrix3 * rinv)
{
}
void Matrix3::Symmetric_Eigen_Solve(void)
{
Matrix3 eigen_vals = *this;
Matrix3 eigen_vecs(1);
Matrix3 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 Matrix3::Multiply(const Matrix3 & A,const Matrix3 & B,Matrix3 * set_res)
{
Matrix3 tmp;
Matrix3 * 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 = (Matrix3 *)&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 Matrix3::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 Matrix3::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;
}

1029
Code/WWMath/matrix3.h Normal file

File diff suppressed because it is too large Load Diff

1177
Code/WWMath/matrix3d.cpp Normal file

File diff suppressed because it is too large Load Diff

1623
Code/WWMath/matrix3d.h Normal file

File diff suppressed because it is too large Load Diff

174
Code/WWMath/matrix4.cpp Normal file
View File

@@ -0,0 +1,174 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/matrix4.cpp $*
* *
* Author:: Greg_h *
* *
* $Modtime:: 11/13/99 10:50a $*
* *
* $Revision:: 5 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* Matrix4::Multiply -- Multiply two Matrix4's together *
* Matrix4::Multiply -- Multiply a Matrix3D * Matrix4 *
* Matrix4::Multiply -- Multiply a Matrix4 * Matrix3D *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "matrix4.h"
#include <assert.h>
/***********************************************************************************************
* Matrix4::Multiply -- Multiply two Matrix4'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 Matrix4::Multiply(const Matrix4 &a,const Matrix4 &b,Matrix4 * 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
}
/***********************************************************************************************
* Matrix4::Multiply -- Multiply a Matrix3D * Matrix4 *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/13/99 gth : Created. *
*=============================================================================================*/
void Matrix4::Multiply(const Matrix3D &a,const Matrix4 &b,Matrix4 * 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
}
/***********************************************************************************************
* Matrix4::Multiply -- Multiply a Matrix4 * Matrix3D *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*=============================================================================================*/
void Matrix4::Multiply(const Matrix4 & a,const Matrix3D & b,Matrix4 * 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
}

797
Code/WWMath/matrix4.h Normal file
View File

@@ -0,0 +1,797 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* $Header: /Commando/Code/wwmath/matrix4.h 20 10/04/01 10:33a Greg_h $ */
/***********************************************************************************************
*** Confidential - Westwood Studios ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* File Name : MATRIX4.H *
* *
* Programmer : Greg Hjelstrom *
* *
* Start Date : 06/02/97 *
* *
* Last Update : June 2, 1997 [GH] *
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* Matrix4::Matrix4 -- Constructor, optionally initialize to Identitiy matrix *
* Matrix4::Matrix4 -- Copy Constructor *
* Matrix4::Matrix4 -- Convert a Matrix3D (fake 4x4) to a Matrix4 *
* Matrix4::Matrix4 -- Constructor *
* Matrix4::Make_Identity -- Initializes the matrix to Identity *
* Matrix4::Init -- Initializes from the contents of the give Matrix3D *
* Matrix4::Init -- Initializes the rows from the given Vector4s *
* Matrix4::Init_Ortho -- Initialize to an orthographic projection matrix *
* Matrix4::Init_Perspective -- Initialize to a perspective projection matrix *
* Matrix4::Init_Perspective -- Initialize to a perspective projection matrix *
* Matrix4::Transpose -- Returns transpose of the matrix *
* Matrix4::Inverse -- returns the inverse of the matrix *
* Matrix4::operator = -- assignment operator *
* Matrix4::operator += -- "plus equals" operator *
* Matrix4::operator-= -- "minus equals" operator *
* Matrix4::operator *= -- "times equals" operator *
* Matrix4::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 Matrix4
{
public:
/*
** Constructors
*/
Matrix4(void) {};
Matrix4(const Matrix4 & m);
WWINLINE explicit Matrix4(bool identity);
WWINLINE explicit Matrix4(const Matrix3D & m);
WWINLINE explicit Matrix4(const Matrix3 & m);
WWINLINE explicit Matrix4(const Vector4 & v0, const Vector4 & v1, const Vector4 & v2, const Vector4 & v3);
WWINLINE void Make_Identity(void);
WWINLINE void Init(const Matrix3D & m);
WWINLINE void Init(const Matrix3 & m);
WWINLINE void Init(const Vector4 & v0, const Vector4 & v1, const Vector4 & v2, const Vector4 & v3);
/*
** 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 Matrix4 Transpose(void) const;
WWINLINE Matrix4 Inverse(void) const;
/*
** Assignment operators
*/
WWINLINE Matrix4 & operator = (const Matrix4 & m);
WWINLINE Matrix4 & operator += (const Matrix4 & m);
WWINLINE Matrix4 & operator -= (const Matrix4 & m);
WWINLINE Matrix4 & operator *= (float d);
WWINLINE Matrix4 & operator /= (float d);
/*
** Negation
*/
WWINLINE friend Matrix4 operator - (const Matrix4& a);
/*
** Scalar multiplication and division
*/
WWINLINE friend Matrix4 operator * (const Matrix4& a,float d);
WWINLINE friend Matrix4 operator * (float d,const Matrix4& a);
WWINLINE friend Matrix4 operator / (const Matrix4& a,float d);
/*
** matrix addition
*/
WWINLINE friend Matrix4 operator + (const Matrix4& a, const Matrix4& b);
WWINLINE friend Matrix4 Add(const Matrix4& a);
/*
** matrix subtraction
*/
WWINLINE friend Matrix4 operator - (const Matrix4 & a, const Matrix4 & b);
WWINLINE friend Matrix4 Subtract(const Matrix4 & a, const Matrix4 & b);
/*
** matrix multiplication
*/
WWINLINE friend Matrix4 operator * (const Matrix4 & a, const Matrix4 & b);
WWINLINE friend Matrix4 Multiply(const Matrix4 & a, const Matrix4 & b);
WWINLINE friend Matrix4 operator * (const Matrix4 & a, const Matrix3D & b);
WWINLINE friend Matrix4 operator * (const Matrix3D & a, const Matrix4 & b);
/*
** Comparison operators
*/
friend int operator == (const Matrix4 & a, const Matrix4 & b);
friend int operator != (const Matrix4 & a, const Matrix4 & b);
/*
** Swap two matrices in place
*/
WWINLINE friend void Swap(Matrix4 & a,Matrix4 & b);
/*
** Linear Transforms
*/
WWINLINE friend Vector4 operator * (const Matrix4 & a, const Vector4 & v);
WWINLINE friend Vector4 operator * (const Matrix4 & a, const Vector3 & v);
/*
** Matrix multiplication without temporaries...
*/
static void Multiply(const Matrix4 &A,const Matrix4 &B,Matrix4 * set_result);
static void Multiply(const Matrix3D &A,const Matrix4 &B,Matrix4 * set_result);
static void Multiply(const Matrix4 &A,const Matrix3D &B,Matrix4 * set_result);
static WWINLINE void Transform_Vector(const Matrix4 & tm,const Vector3 & in,Vector3 * out);
static WWINLINE void Transform_Vector(const Matrix4 & tm,const Vector3 & in,Vector4 * out);
static WWINLINE void Transform_Vector(const Matrix4 & tm,const Vector4 & in,Vector4 * out);
protected:
Vector4 Row[4];
};
/***********************************************************************************************
* Matrix4::Matrix4 -- Constructor, optionally initialize to Identitiy matrix *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/02/1997 GH : Created. *
*=============================================================================================*/
WWINLINE Matrix4::Matrix4(bool identity)
{
if (identity) {
Make_Identity();
}
}
/***********************************************************************************************
* Matrix4::Matrix4 -- Copy Constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/02/1997 GH : Created. *
*=============================================================================================*/
WWINLINE Matrix4::Matrix4(const Matrix4 & m)
{
Row[0] = m.Row[0]; Row[1] = m.Row[1]; Row[2] = m.Row[2]; Row[3] = m.Row[3];
}
/***********************************************************************************************
* Matrix4::Matrix4 -- Convert a Matrix3D (fake 4x4) to a Matrix4 *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/02/1997 GH : Created. *
*=============================================================================================*/
WWINLINE Matrix4::Matrix4(const Matrix3D & m)
{
Init(m);
}
/***********************************************************************************************
* Matrix4::Matrix4 -- Constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/02/1997 GH : Created. *
*=============================================================================================*/
WWINLINE Matrix4::Matrix4(const Vector4 & r0, const Vector4 & r1, const Vector4 & r2, const Vector4 & r3)
{
Init(r0,r1,r2,r3);
}
/***********************************************************************************************
* Matrix4::Make_Identity -- Initializes the matrix to Identity *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/5/99 gth : Created. *
*=============================================================================================*/
WWINLINE void Matrix4::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);
}
/***********************************************************************************************
* Matrix4::Init -- Initializes from the contents of the give Matrix3D *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/5/99 gth : Created. *
*=============================================================================================*/
WWINLINE void Matrix4::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);
}
/***********************************************************************************************
* Matrix4::Init -- Initializes the rows from the given Vector4s *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/5/99 gth : Created. *
*=============================================================================================*/
WWINLINE void Matrix4::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;
}
/***********************************************************************************************
* Matrix4::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 Matrix4::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);
}
/***********************************************************************************************
* Matrix4::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 Matrix4::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;
}
/***********************************************************************************************
* Matrix4::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 Matrix4::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;
}
/***********************************************************************************************
* Matrix4::Transpose -- Returns transpose of the matrix *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/02/1997 GH : Created. *
*=============================================================================================*/
WWINLINE Matrix4 Matrix4::Transpose() const
{
return Matrix4(
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])
);
}
/***********************************************************************************************
* Matrix4::Inverse -- returns the inverse of the matrix *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/02/1997 GH : Created. *
*=============================================================================================*/
WWINLINE Matrix4 Matrix4::Inverse() const // Gauss-Jordan elimination with partial pivoting
{
WWASSERT_PRINT(0,"Matrix4::Inverse does not work, re-implement!");
Matrix4 a(*this); // As a evolves from original mat into identity
Matrix4 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("Matrix4::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;
}
/***********************************************************************************************
* Matrix4::operator = -- assignment operator *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/02/1997 GH : Created. *
*=============================================================================================*/
WWINLINE Matrix4 & Matrix4::operator = (const Matrix4 & m)
{
Row[0] = m.Row[0]; Row[1] = m.Row[1]; Row[2] = m.Row[2]; Row[3] = m.Row[3];
return *this;
}
/***********************************************************************************************
* Matrix4::operator += -- "plus equals" operator *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/02/1997 GH : Created. *
*=============================================================================================*/
WWINLINE Matrix4& Matrix4::operator += (const Matrix4 & m)
{
Row[0] += m.Row[0]; Row[1] += m.Row[1]; Row[2] += m.Row[2]; Row[3] += m.Row[3];
return *this;
}
/***********************************************************************************************
* Matrix4::operator-= -- "minus equals" operator *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/02/1997 GH : Created. *
*=============================================================================================*/
WWINLINE Matrix4& Matrix4::operator -= (const Matrix4 & m)
{
Row[0] -= m.Row[0]; Row[1] -= m.Row[1]; Row[2] -= m.Row[2]; Row[3] -= m.Row[3];
return *this;
}
/***********************************************************************************************
* Matrix4::operator *= -- "times equals" operator *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/02/1997 GH : Created. *
*=============================================================================================*/
WWINLINE Matrix4& Matrix4::operator *= (float d)
{
Row[0] *= d; Row[1] *= d; Row[2] *= d; Row[3] *= d;
return *this;
}
/***********************************************************************************************
* Matrix4::operator /= -- "divide equals" operator *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/02/1997 GH : Created. *
*=============================================================================================*/
WWINLINE Matrix4& Matrix4::operator /= (float d)
{
float ood = d;
Row[0] *= ood; Row[1] *= ood; Row[2] *= ood; Row[3] *= ood;
return *this;
}
WWINLINE Matrix4 operator - (const Matrix4 & a)
{
return Matrix4(-a.Row[0], -a.Row[1], -a.Row[2], -a.Row[3]);
}
WWINLINE Matrix4 operator * (const Matrix4 & a, float d)
{
return Matrix4(a.Row[0] * d, a.Row[1] * d, a.Row[2] * d, a.Row[3] * d);
}
WWINLINE Matrix4 operator * (float d, const Matrix4 & a)
{
return a*d;
}
WWINLINE Matrix4 operator / (const Matrix4 & a, float d)
{
float ood = 1.0f / d;
return Matrix4(a.Row[0] * ood, a.Row[1] * ood, a.Row[2] * ood, a.Row[3] * ood);
}
/*
** matrix addition
*/
WWINLINE Matrix4 operator + (const Matrix4 & a, const Matrix4 & b)
{
return Matrix4(
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 Matrix4 Add(const Matrix4 & a, const Matrix4 & b)
{ return a+b; }
/*
** matrix subtraction
*/
WWINLINE Matrix4 operator - (const Matrix4 & a, const Matrix4 & b)
{
return Matrix4(
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 Matrix4 Subtract(const Matrix4 & a, const Matrix4 & b)
{ return a-b; }
/*
** matrix multiplication
*/
WWINLINE Matrix4 operator * (const Matrix4 & a, const Matrix4 & 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 Matrix4(
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 Matrix4 Multiply(const Matrix4 & a, const Matrix4 & b)
{ return a*b; }
WWINLINE Matrix4 operator * (const Matrix4 & 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 Matrix4(
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 Matrix4 operator * (const Matrix3D & a, const Matrix4 & 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 Matrix4(
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 Matrix4 by a Vector3 (assumes w=1.0!!!). Yeilds a Vector4 result
*/
WWINLINE Vector4 operator * (const Matrix4 & 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 Matrix4 by a Vector4
*/
WWINLINE Vector4 operator * (const Matrix4 & 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 Matrix4 by a Vector4
*/
WWINLINE void Matrix4::Transform_Vector(const Matrix4 & 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 Matrix4::Transform_Vector(const Matrix4 & 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 Matrix4::Transform_Vector(const Matrix4 & 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*/

243
Code/WWMath/normalcone.h Normal file
View File

@@ -0,0 +1,243 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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

784
Code/WWMath/obbox.cpp Normal file
View File

@@ -0,0 +1,784 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/obbox.cpp $*
* *
* Author:: Greg_h *
* *
* $Modtime:: 8/23/99 2:10p $*
* *
* $Revision:: 23 $*
* *
*---------------------------------------------------------------------------------------------*
* 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;
axis0 = Normalize(dp[1]);
axis1 = Normalize(dp[2]);
Vector3::Cross_Product(axis0,axis1,&axis2);
Basis = Matrix3(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;
}

268
Code/WWMath/obbox.h Normal file
View File

@@ -0,0 +1,268 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : wwmath *
* *
* $Archive:: /Commando/Code/wwmath/obbox.h $*
* *
* Author:: Greg_h *
* *
* $Modtime:: 6/29/00 6:51p $*
* *
* $Revision:: 23 $*
* *
*---------------------------------------------------------------------------------------------*
* 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 Matrix3 & 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;
Matrix3 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));
Matrix3::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];
Matrix3::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

367
Code/WWMath/ode.cpp Normal file
View File

@@ -0,0 +1,367 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* $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);
}

134
Code/WWMath/ode.h Normal file
View File

@@ -0,0 +1,134 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* $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

246
Code/WWMath/plane.h Normal file
View File

@@ -0,0 +1,246 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* $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)
{
N = Vector3::Cross_Product((point2 - point1), (point3 - point1));
if (N != Vector3(0.0f, 0.0f, 0.0f)) {
// Points are not colinear. Normalize N and calculate D.
N.Normalize();
D = 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*/

118
Code/WWMath/pot.cpp Normal file
View File

@@ -0,0 +1,118 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***************************************************************************
* *
* Project Name : 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;
}

45
Code/WWMath/pot.h Normal file
View File

@@ -0,0 +1,45 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***************************************************************************
* *
* Project Name : 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

918
Code/WWMath/quat.cpp Normal file
View File

@@ -0,0 +1,918 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* $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 Matrix3 & 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 Matrix4 & 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. *
*=============================================================================================*/
Matrix3 Build_Matrix3(const Quaternion & q)
{
Matrix3 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;
}
Matrix3D Build_Matrix3D(const Quaternion & q)
{
Matrix3D 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;
return m;
}
Matrix4 Build_Matrix4(const Quaternion & q)
{
Matrix4 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();
}

296
Code/WWMath/quat.h Normal file
View File

@@ -0,0 +1,296 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* $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"
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 Matrix3 & matrix);
Quaternion Build_Quaternion(const Matrix3D & matrix);
Quaternion Build_Quaternion(const Matrix4 & matrix);
// Convert a quaternion into a rotation matrix
Matrix3 Build_Matrix3(const Quaternion & quat);
Matrix3D Build_Matrix3D(const Quaternion & quat);
Matrix4 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 */

108
Code/WWMath/rect.h Normal file
View File

@@ -0,0 +1,108 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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

520
Code/WWMath/sphere.h Normal file
View File

@@ -0,0 +1,520 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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 Vector3 & center,const SphereClass & s0);
inline SphereClass(const Vector3 *Position, const int VertCount);
inline void Init(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::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
Center = tm * Center;
}
/***********************************************************************************************
* 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 = 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
res.Center = m*s.Center;
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

248
Code/WWMath/tcbspline.cpp Normal file
View File

@@ -0,0 +1,248 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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;
}

86
Code/WWMath/tcbspline.h Normal file
View File

@@ -0,0 +1,86 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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

291
Code/WWMath/tri.cpp Normal file
View File

@@ -0,0 +1,291 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/tri.cpp $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 3/12/02 10:21a $*
* *
* $Revision:: 10 $*
* *
*---------------------------------------------------------------------------------------------*
* 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"
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
int vi;
int axis1 = 0;
int axis2 = 0;
int axis3 = 0;
find_dominant_plane(*this,&axis1,&axis2,&axis3);
int side_mask = 0;
const int POS = 0x01;
const int NEG = 0x02;
/*
** 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;
if (cross > WWMATH_EPSILON) {
side_mask |= POS;
}
if (cross < -WWMATH_EPSILON) {
side_mask |= NEG;
}
}
bool my_intersect = (side_mask != (POS | NEG));
return my_intersect;
#endif
}

306
Code/WWMath/tri.h Normal file
View File

@@ -0,0 +1,306 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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

173
Code/WWMath/v3_rnd.cpp Normal file
View File

@@ -0,0 +1,173 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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;
}

235
Code/WWMath/v3_rnd.h Normal file
View File

@@ -0,0 +1,235 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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 new 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 new 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 new 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 new 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

671
Code/WWMath/vector2.h Normal file
View File

@@ -0,0 +1,671 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* $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 */

113
Code/WWMath/vector2i.h Normal file
View File

@@ -0,0 +1,113 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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

926
Code/WWMath/vector3.h Normal file
View File

@@ -0,0 +1,926 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* $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
static WWINLINE Vector3 Cross_Product(const Vector3 &a,const Vector3 &b);
static WWINLINE void 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*b;
}
/**************************************************************************
* 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: *
*========================================================================*/
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)
);
}
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 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(Length2());
X *= oolen;
Y *= oolen;
Z *= oolen;
}
}
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;
}
/**************************************************************************
* 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 by an interpolation factor. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: No checking is done to ensure that alpha is between 0 and 1. *
* *
* HISTORY: *
* 08/11/1997 GH : Created. *
*=============================================================================================*/
WWINLINE Vector3 Lerp(const Vector3 & a, const Vector3 & b, float alpha)
{
return Vector3(
(a.X + (b.X - a.X)*alpha),
(a.Y + (b.Y - a.Y)*alpha),
(a.Z + (b.Z - a.Z)*alpha)
);
}
/***********************************************************************************************
* Lerp -- linearly interpolate two Vector3's without return-by-value *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 10/18/99 gth : Created. *
*=============================================================================================*/
WWINLINE void 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);
}
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 */

143
Code/WWMath/vector3i.h Normal file
View File

@@ -0,0 +1,143 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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

412
Code/WWMath/vector4.h Normal file
View File

@@ -0,0 +1,412 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* $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,681 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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 ();
Vector3 y_vector = Vector3::Cross_Product (x_vector, z_vector);
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 ;
}

210
Code/WWMath/vehiclecurve.h Normal file
View File

@@ -0,0 +1,210 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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

541
Code/WWMath/vp.cpp Normal file
View File

@@ -0,0 +1,541 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : wwmath *
* *
* $Archive:: /Commando/Code/WWMath/vp.cpp $*
* *
* Author:: Hector Yee *
* *
* $Modtime:: 6/27/01 4:16p $*
* *
* $Revision:: 11 $*
* *
*---------------------------------------------------------------------------------------------*/
#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
{
int i;
for (i=0; i<count; i++)
{
dst[i]=mtx*src[i];
}
}
}
void VectorProcessorClass::Transform(Vector4* dst,const Vector3 *src, const Matrix4& 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);
}

90
Code/WWMath/vp.h Normal file
View File

@@ -0,0 +1,90 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : wwmath *
* *
* $Archive:: /Commando/Code/WWMath/vp.h $*
* *
* Author:: Hector Yee *
* *
* $Modtime:: 6/27/01 11:39a $*
* *
* $Revision:: 12 $*
* *
*---------------------------------------------------------------------------------------------*
* 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 Matrix4;
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 Matrix4& 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

95
Code/WWMath/wwmath.cpp Normal file
View File

@@ -0,0 +1,95 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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);
}

531
Code/WWMath/wwmath.dsp Normal file
View File

@@ -0,0 +1,531 @@
# 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 RELEASE
!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 RELEASE"
!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
# 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 /MTd /W4 /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" /D "_USE_INTEL_COMPILER" /FD /c
# SUBTRACT CPP /WX /Fr /YX
# 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:"..\Libs\debug\wwmath.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 /MT /W4 /Zi /O2 /Ob2 /I "..\wwlib" /I "..\wwdebug" /I "..\wwsaveload" /D "NDEBUG" /D WINVER=0x400 /D "_WINDOWS" /D "WIN32_LEAN_AND_MEAN" /D "WIN32" /FD /c
# SUBTRACT CPP /WX /Fr /YX
# 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:"..\Libs\release\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 /MT /W4 /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" /FD /c
# SUBTRACT CPP /WX /Fr /YX
# 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:"..\Libs\profile\wwmath.lib"
!ENDIF
# Begin Target
# Name "wwmath - Win32 Debug"
# Name "wwmath - Win32 Release"
# Name "wwmath - Win32 Profile"
# 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
!IF "$(CFG)" == "wwmath - Win32 Debug"
# SUBTRACT CPP /WX
!ELSEIF "$(CFG)" == "wwmath - Win32 Release"
# ADD CPP /D "_USE_INTEL_COMPILER"
# SUBTRACT CPP /WX
!ELSEIF "$(CFG)" == "wwmath - Win32 Profile"
# ADD CPP /D "_USE_INTEL_COMPILER"
# SUBTRACT CPP /WX
!ENDIF
# 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
!IF "$(CFG)" == "wwmath - Win32 Debug"
# SUBTRACT CPP /WX
!ELSEIF "$(CFG)" == "wwmath - Win32 Release"
# ADD CPP /D "_USE_INTEL_COMPILER"
# SUBTRACT CPP /WX
!ELSEIF "$(CFG)" == "wwmath - Win32 Profile"
# ADD CPP /D "_USE_INTEL_COMPILER"
# SUBTRACT CPP /WX
!ENDIF
# End Source File
# Begin Source File
SOURCE=.\matrix3d.cpp
!IF "$(CFG)" == "wwmath - Win32 Debug"
# SUBTRACT CPP /WX
!ELSEIF "$(CFG)" == "wwmath - Win32 Release"
# ADD CPP /D "_USE_INTEL_COMPILER"
# SUBTRACT CPP /WX
!ELSEIF "$(CFG)" == "wwmath - Win32 Profile"
# ADD CPP /D "_USE_INTEL_COMPILER"
# SUBTRACT CPP /WX
!ENDIF
# End Source File
# Begin Source File
SOURCE=.\matrix4.cpp
!IF "$(CFG)" == "wwmath - Win32 Debug"
# SUBTRACT CPP /WX
!ELSEIF "$(CFG)" == "wwmath - Win32 Release"
# ADD CPP /D "_USE_INTEL_COMPILER"
# SUBTRACT CPP /WX
!ELSEIF "$(CFG)" == "wwmath - Win32 Profile"
# ADD CPP /D "_USE_INTEL_COMPILER"
# SUBTRACT CPP /WX
!ENDIF
# 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
!IF "$(CFG)" == "wwmath - Win32 Debug"
# SUBTRACT CPP /WX
!ELSEIF "$(CFG)" == "wwmath - Win32 Release"
# ADD CPP /D "_USE_INTEL_COMPILER"
# SUBTRACT CPP /WX
!ELSEIF "$(CFG)" == "wwmath - Win32 Profile"
# ADD CPP /D "_USE_INTEL_COMPILER"
# SUBTRACT CPP /WX
!ENDIF
# 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
!IF "$(CFG)" == "wwmath - Win32 Debug"
!ELSEIF "$(CFG)" == "wwmath - Win32 Release"
# ADD CPP /D "_USE_INTEL_COMPILER"
!ELSEIF "$(CFG)" == "wwmath - Win32 Profile"
# ADD CPP /D "_USE_INTEL_COMPILER"
# SUBTRACT CPP /WX
!ENDIF
# 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

650
Code/WWMath/wwmath.h Normal file
View File

@@ -0,0 +1,650 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/wwmath.h $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 3/01/02 9:06a $*
* *
* $Revision:: 65 $*
* *
*---------------------------------------------------------------------------------------------*
* 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 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 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 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

66
Code/WWMath/wwmathids.h Normal file
View File

@@ -0,0 +1,66 @@
/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : 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