This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/WWMath/normalcone.h

243 lines
9.2 KiB
C++

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