Complete disaster-recovery snapshot: engine/game source, game data assets, VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive. Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false, no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
655 lines
16 KiB
C++
655 lines
16 KiB
C++
#pragma once
|
|
#define MLR_MLRPRIMITIVEBASE_HPP
|
|
|
|
#include <MLR\MLR.hpp>
|
|
#include <MLR\GOSVertexPool.hpp>
|
|
|
|
namespace MidLevelRenderer {
|
|
|
|
struct ClipPolygon2;
|
|
|
|
struct DataStorage {
|
|
Stuff::DynamicArrayOf<BYTE> index;
|
|
Stuff::DynamicArrayOf<BYTE> lengths;
|
|
Stuff::DynamicArrayOf<Stuff::Point3D> coords;
|
|
Stuff::DynamicArrayOf<Vector2DScalar> texCoords;
|
|
Stuff::DynamicArrayOf<ColorType> colors;
|
|
Stuff::DynamicArrayOf<Stuff::Vector3D> normals;
|
|
};
|
|
|
|
class MLRPrimitiveBase__ClassData;
|
|
class MLRTexture;
|
|
class MLRLight;
|
|
class MLRClippingState;
|
|
|
|
extern Stuff::ReadOnlyArrayOf<BYTE> temp_lengths;
|
|
|
|
//##########################################################################
|
|
//#################### MLRPrimitiveBase ##############################
|
|
//##########################################################################
|
|
// this is the abstract base class for all geometry. it has contains geometry
|
|
// with one and only one renderer state !!!
|
|
|
|
class _declspec(novtable) MLRPrimitiveBase:
|
|
public Stuff::RegisteredClass
|
|
{
|
|
friend class MLRShape;
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Initialization
|
|
//
|
|
public:
|
|
static void
|
|
InitializeClass();
|
|
static void
|
|
TerminateClass();
|
|
typedef MLRPrimitiveBase__ClassData ClassData;
|
|
static ClassData
|
|
*DefaultData;
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Constructors/Destructors
|
|
//
|
|
protected:
|
|
MLRPrimitiveBase(
|
|
ClassData *class_data,
|
|
Stuff::MemoryStream *stream,
|
|
int version
|
|
);
|
|
|
|
~MLRPrimitiveBase();
|
|
|
|
public:
|
|
MLRPrimitiveBase(ClassData *class_data);
|
|
|
|
typedef MLRPrimitiveBase*
|
|
(*Factory)(
|
|
Stuff::MemoryStream *stream,
|
|
int version
|
|
);
|
|
|
|
static MLRPrimitiveBase*
|
|
Make(
|
|
Stuff::MemoryStream *stream,
|
|
int version
|
|
);
|
|
|
|
virtual void
|
|
Save(Stuff::MemoryStream *stream);
|
|
|
|
// Subprimitves are units in which this geometry is split off
|
|
// ie. nr of polygons in a polygon mesh or number of tripstrips
|
|
// in a tristriped mesh, every of this subprimitves has another
|
|
// number which is type specific
|
|
// ie. number of vertices in a polygon or number of vertices in
|
|
// a tristrip
|
|
// the data for the coord/color/texcoord/normal or index
|
|
// ARE IN THIS ORDER
|
|
virtual int
|
|
GetNumPrimitives() = 0;
|
|
|
|
virtual void
|
|
SetSubprimitiveLengths(
|
|
const BYTE *length_array,
|
|
int subprimitive_count,
|
|
bool setFace=true
|
|
) = 0;
|
|
|
|
// returns the number of subprimitives
|
|
virtual void
|
|
GetSubprimitiveLengths(const BYTE **length_array, int*) = 0;
|
|
|
|
|
|
// ==============================================================
|
|
|
|
virtual void SetReferenceState(const MLRState& _state, int=0)
|
|
{ Check_Object(this); referenceState = _state; };
|
|
virtual const MLRState&
|
|
GetReferenceState(int=0) const
|
|
{ Check_Object(this); return referenceState; };
|
|
virtual const MLRStateBase&
|
|
GetCurrentState(int=0) const
|
|
{ Check_Object(this); return state; };
|
|
|
|
virtual void
|
|
CombineStates (const MLRState& master)
|
|
{ Check_Object(this); state.CombineIntoBase(master, referenceState); };
|
|
|
|
int
|
|
GetNumVertices()
|
|
{ Check_Object(this); return coords.GetLength(); }
|
|
|
|
virtual void
|
|
SetCoordData(
|
|
const Stuff::Point3D *array,
|
|
int point_count
|
|
);
|
|
virtual void
|
|
GetCoordData(
|
|
const Stuff::Point3D **array,
|
|
int *point_count
|
|
);
|
|
|
|
virtual void
|
|
SetTexCoordData(
|
|
const Vector2DScalar *array,
|
|
int point_count
|
|
);
|
|
virtual void
|
|
GetTexCoordData(
|
|
const Vector2DScalar **array,
|
|
int *point_count
|
|
);
|
|
|
|
// is to call befor clipping, parameter: camera point
|
|
virtual int FindBackFace(const Stuff::Point3D&, const Stuff::Normal3D&) = 0;
|
|
|
|
virtual void Lighting(MLRLight* const*, int nrLights, BYTE=3) = 0;
|
|
virtual DWORD CenterLighting(MLRLight* const*, int nrLights, BYTE=3) = 0;
|
|
|
|
static void InitializeDraw();
|
|
|
|
virtual void InitializeDrawPrimitive(BYTE, int=0);
|
|
|
|
virtual int GetVisible ()
|
|
{ Check_Object(this); return visible; }
|
|
|
|
virtual GOSVertex*
|
|
GetGOSVertices(int=0)
|
|
{ Check_Object(this); return gos_vertices; }
|
|
|
|
int
|
|
GetNumGOSVertices()
|
|
{ Check_Object(this); return numGOSVertices; }
|
|
|
|
virtual GOSVertex2UV*
|
|
GetGOSVertices2UV(int=0)
|
|
{ Check_Object(this); return NULL; }
|
|
|
|
virtual GOSVertex3UV*
|
|
GetGOSVertices3UV(int=0)
|
|
{ Check_Object(this); return NULL; }
|
|
|
|
virtual DWORD
|
|
GetTexture2()
|
|
{ Check_Object(this); return 0; }
|
|
|
|
virtual DWORD
|
|
GetTexture3()
|
|
{ Check_Object(this); return 0; }
|
|
|
|
int
|
|
GetSortDataMode()
|
|
{ Check_Object(this); return drawMode; }
|
|
|
|
virtual bool
|
|
CastRay(
|
|
Stuff::Line3D *line,
|
|
Stuff::Normal3D *normal
|
|
);
|
|
|
|
virtual void
|
|
PaintMe(const ColorType *paintMe) = 0;
|
|
|
|
virtual int
|
|
TransformAndClip(Stuff::Matrix4D*, MLRClippingState, GOSVertexPool*, FogData*, DWORD=0xffffffff, bool=false) = 0;
|
|
|
|
virtual void
|
|
TransformNoClip(Stuff::Matrix4D*, GOSVertexPool*, FogData*, DWORD=0xffffffff, bool=false) = 0;
|
|
|
|
virtual int
|
|
GetNumPasses()
|
|
{ Check_Object(this); return passes; }
|
|
|
|
virtual void
|
|
CleanMe()
|
|
{ Check_Object(this); }
|
|
|
|
virtual void
|
|
HurtMe(const Stuff::LinearMatrix4D&, Stuff::Scalar radius)
|
|
{ Check_Object(this); }
|
|
|
|
virtual MLRPrimitiveBase*
|
|
HurtMe(const Stuff::LinearMatrix4D&, Stuff::Scalar radius, MLRTexture*)
|
|
{ Check_Object(this); return NULL; }
|
|
|
|
virtual void
|
|
DrawMyShadow(const Stuff::LinearMatrix4D&, Stuff::UnitVector3D&, Stuff::Scalar, DWORD=0xff800000)
|
|
{ Check_Object(this); return; }
|
|
|
|
virtual bool
|
|
FogMesh(const Stuff::LinearMatrix4D *worldToShape, const Stuff::Point3D *cameraPos, FogData *fogData)
|
|
{ Check_Object(this); return false; }
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// This functions using the static buffers
|
|
//
|
|
void
|
|
SetClipCoord(const Stuff::Point3D &point, int index)
|
|
{
|
|
Check_Object(this); Verify(clipExtraCoords->GetLength() > index);
|
|
(*clipExtraCoords)[index].x = point.x;
|
|
(*clipExtraCoords)[index].y = point.y;
|
|
(*clipExtraCoords)[index].z = point.z;
|
|
}
|
|
void
|
|
FlashClipCoords(int num)
|
|
{
|
|
Check_Object(this); Verify(clipExtraCoords->GetLength() > num);
|
|
dataStore->coords.SetLength(num);
|
|
Stuff::Point3D *c = dataStore->coords.GetData();
|
|
|
|
for(int i=0;i<num;i++)
|
|
{
|
|
c[i].x = (*clipExtraCoords)[i].x;
|
|
c[i].y = (*clipExtraCoords)[i].y;
|
|
c[i].z = (*clipExtraCoords)[i].z;
|
|
}
|
|
SetCoordData(dataStore->coords.GetData(), num);
|
|
}
|
|
void
|
|
SetClipTexCoord(const Vector2DScalar &uvs, int index)
|
|
{
|
|
Check_Object(this); Verify(clipExtraTexCoords->GetLength() > index);
|
|
|
|
(*clipExtraTexCoords)[index] = uvs;
|
|
}
|
|
void
|
|
FlashClipTexCoords(int num)
|
|
{
|
|
Check_Object(this); Verify(clipExtraTexCoords->GetLength() > num);
|
|
dataStore->texCoords.SetLength(num);
|
|
Mem_Copy(dataStore->texCoords.GetData(), clipExtraTexCoords->GetData(), sizeof(Vector2DScalar)*num, sizeof(Vector2DScalar)*num);
|
|
SetTexCoordData(dataStore->texCoords.GetData(), num);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// extra channel enum
|
|
//
|
|
enum ExtraChannel {
|
|
FogChannel = 0,
|
|
SpecularRedChannel,
|
|
MaxNumberOfExtraChannel
|
|
};
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Reference counting
|
|
//
|
|
public:
|
|
void
|
|
AttachReference()
|
|
{Check_Object(this); ++referenceCount;}
|
|
void
|
|
DetachReference()
|
|
{
|
|
Check_Object(this); Verify(referenceCount > 0);
|
|
if ((--referenceCount) == 0)
|
|
{
|
|
Unregister_Object(this);
|
|
delete this;
|
|
}
|
|
}
|
|
|
|
int
|
|
GetReferenceCount()
|
|
{return referenceCount;}
|
|
|
|
protected:
|
|
WORD
|
|
referenceCount;
|
|
WORD
|
|
numGOSVertices;
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Testing
|
|
//
|
|
public:
|
|
void
|
|
TestInstance() const;
|
|
|
|
void
|
|
GetExtend(Stuff::ExtentBox *box);
|
|
|
|
DataStorage *dataStore;
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Fog data
|
|
//
|
|
public:
|
|
static void
|
|
SetFogTableEntry(int entry, Stuff::Scalar fogNearClip, Stuff::Scalar fogFarClip, Stuff::Scalar fogDensity);
|
|
static void
|
|
GetFogTableEntry(int entry, Stuff::Scalar * pfogNearClip, Stuff::Scalar * pfogFarClip, Stuff::Scalar * pfogDensity);
|
|
static Stuff::Scalar
|
|
fogTable[Limits::Max_Number_Of_FogStates][4];
|
|
|
|
protected:
|
|
virtual void
|
|
Transform(Stuff::Matrix4D*);
|
|
|
|
static ClipPolygon2 *clipBuffer;
|
|
|
|
BYTE visible; // primitive visibilty per frame
|
|
BYTE passes;
|
|
BYTE drawMode;
|
|
BYTE channelUse;
|
|
|
|
// int numPrimitives; // Number of primitives, e.g. - num quads
|
|
// Replaced by GetNumPrimitives
|
|
|
|
// int numVertices; // number of verts for stats and vert arrays
|
|
// Replaced by GetNumVertices
|
|
|
|
Stuff::ReadOnlyArrayOf<Stuff::Point3D> coords; // Base address of coordinate list
|
|
Stuff::ReadOnlyArrayOf<Vector2DScalar> texCoords; // Base address of texture coordinate list
|
|
|
|
static Stuff::DynamicArrayOf<Stuff::Vector4D> *transformedCoords;
|
|
|
|
static Stuff::DynamicArrayOf<ColorType> *clipExtraColors; // , Max_Number_Vertices_Per_Mesh
|
|
|
|
static Stuff::DynamicArrayOf<MLRClippingState> *clipPerVertex; // , Max_Number_Vertices_Per_Mesh
|
|
|
|
static Stuff::DynamicArrayOf<Stuff::Vector4D> *clipExtraCoords; // , Max_Number_Vertices_Per_Mesh
|
|
static Stuff::DynamicArrayOf<Vector2DScalar> *clipExtraTexCoords; // , Max_Number_Vertices_Per_Mesh
|
|
|
|
static Stuff::DynamicArrayOf<WORD> *clipExtraLength; // , Max_Number_Primitives_Per_Frame
|
|
|
|
MLRStateBase state;
|
|
MLRState referenceState;
|
|
|
|
GOSVertex *gos_vertices;
|
|
// WORD
|
|
// numGOSVertices; moved up for alignment reasons
|
|
|
|
static Stuff::DynamicArrayOf<BYTE> *extraChannels[2*MaxNumberOfExtraChannel];
|
|
};
|
|
|
|
struct IcoInfo {
|
|
int type;
|
|
int depth;
|
|
bool indexed;
|
|
Stuff::Scalar radius;
|
|
Stuff::Scalar all;
|
|
bool onOff;
|
|
const char *GetTypeName();
|
|
};
|
|
|
|
MLRShape*
|
|
CreateIndexedIcosahedron(
|
|
IcoInfo&,
|
|
Stuff::DynamicArrayOf<MLRState>*
|
|
);
|
|
|
|
//##########################################################################
|
|
//################### MLRPrimitiveBase__ClassData ####################
|
|
//##########################################################################
|
|
|
|
class MLRPrimitiveBase__ClassData:
|
|
public Stuff::RegisteredClass::ClassData
|
|
{
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
public:
|
|
MLRPrimitiveBase__ClassData(
|
|
Stuff::RegisteredClass::ClassID class_id,
|
|
const char* class_name,
|
|
Stuff::RegisteredClass::ClassData *parent_class,
|
|
MLRPrimitiveBase::Factory primitive_factory
|
|
):
|
|
RegisteredClass__ClassData(class_id, class_name, parent_class),
|
|
primitiveFactory(primitive_factory)
|
|
{}
|
|
|
|
MLRPrimitiveBase::Factory
|
|
primitiveFactory;
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
public:
|
|
void
|
|
TestInstance();
|
|
};
|
|
|
|
struct ClipData2
|
|
{
|
|
Stuff::Vector4D *coords;
|
|
Stuff::RGBAColor *colors;
|
|
Vector2DScalar *texCoords;
|
|
MLRClippingState *clipPerVertex;
|
|
BYTE *extraChannel[MLRPrimitiveBase::MaxNumberOfExtraChannel];
|
|
|
|
WORD length;
|
|
};
|
|
|
|
struct ClipPolygon2
|
|
{
|
|
void Init(int);
|
|
void Destroy();
|
|
|
|
Stuff::DynamicArrayOf<Stuff::Vector4D> coords; // [Max_Number_Vertices_Per_Polygon]
|
|
Stuff::DynamicArrayOf<Stuff::RGBAColor> colors; //[Max_Number_Vertices_Per_Polygon];
|
|
Stuff::DynamicArrayOf<Vector2DScalar> texCoords; //[2*Max_Number_Vertices_Per_Polygon];
|
|
Stuff::DynamicArrayOf<MLRClippingState> clipPerVertex; //[Max_Number_Vertices_Per_Polygon];
|
|
Stuff::DynamicArrayOf<BYTE> extraChannel[MLRPrimitiveBase::MaxNumberOfExtraChannel]; //[Max_Number_Vertices_Per_Polygon];
|
|
};
|
|
|
|
#if 0 // still defined in "MLRPrimitive.hpp"
|
|
inline Stuff::Scalar
|
|
GetBC(int nr, const Stuff::Vector4D& v4d)
|
|
{
|
|
switch(nr)
|
|
{
|
|
case 0:
|
|
return (v4d.w - v4d.y);
|
|
case 1:
|
|
return v4d.y;
|
|
case 2:
|
|
return (v4d.w - v4d.x);
|
|
case 3:
|
|
return v4d.x;
|
|
case 4:
|
|
return v4d.z;
|
|
case 5:
|
|
return (v4d.w - v4d.z);
|
|
}
|
|
return 0.0f;
|
|
}
|
|
|
|
inline void
|
|
GetDoubleBC
|
|
(
|
|
int nr,
|
|
Stuff::Scalar& result1,
|
|
Stuff::Scalar& result2,
|
|
const Stuff::Vector4D& v4d1,
|
|
const Stuff::Vector4D& v4d2
|
|
)
|
|
{
|
|
switch(nr)
|
|
{
|
|
case 0:
|
|
result1 = (v4d1.w - v4d1.y);
|
|
result2 = (v4d2.w - v4d2.y);
|
|
break;
|
|
case 1:
|
|
result1 = v4d1.y;
|
|
result2 = v4d2.y;
|
|
break;
|
|
case 2:
|
|
result1 = (v4d1.w - v4d1.x);
|
|
result2 = (v4d2.w - v4d2.x);
|
|
break;
|
|
case 3:
|
|
result1 = v4d1.x;
|
|
result2 = v4d2.x;
|
|
break;
|
|
case 4:
|
|
result1 = v4d1.z;
|
|
result2 = v4d2.z;
|
|
break;
|
|
case 5:
|
|
result1 = (v4d1.w - v4d1.z);
|
|
result2 = (v4d2.w - v4d2.z);
|
|
break;
|
|
}
|
|
}
|
|
|
|
inline Stuff::Scalar
|
|
GetLerpFactor
|
|
(
|
|
int nr,
|
|
const Stuff::Vector4D& v4d1,
|
|
const Stuff::Vector4D& v4d2
|
|
)
|
|
{
|
|
Stuff::Scalar result1, result2;
|
|
|
|
switch(nr)
|
|
{
|
|
case 0:
|
|
result1 = (v4d1.w - v4d1.y);
|
|
result2 = (v4d2.w - v4d2.y);
|
|
break;
|
|
case 1:
|
|
result1 = v4d1.y;
|
|
result2 = v4d2.y;
|
|
break;
|
|
case 2:
|
|
result1 = (v4d1.w - v4d1.x);
|
|
result2 = (v4d2.w - v4d2.x);
|
|
break;
|
|
case 3:
|
|
result1 = v4d1.x;
|
|
result2 = v4d2.x;
|
|
break;
|
|
case 4:
|
|
result1 = v4d1.z;
|
|
result2 = v4d2.z;
|
|
break;
|
|
case 5:
|
|
result1 = (v4d1.w - v4d1.z);
|
|
result2 = (v4d2.w - v4d2.z);
|
|
break;
|
|
default:
|
|
result1 = 0.0f;
|
|
result2 = 0.0f;
|
|
Abort_Program("Invalid plane number used !");
|
|
break;
|
|
}
|
|
Verify(!Stuff::Close_Enough(result1, result2));
|
|
return result1 / (result1 - result2);
|
|
}
|
|
|
|
#endif
|
|
|
|
inline Stuff::Scalar
|
|
GetBC(int nr, const Stuff::Vector4D& v4d)
|
|
{
|
|
switch(nr)
|
|
{
|
|
case 0:
|
|
return (v4d.w - v4d.y);
|
|
case 1:
|
|
return v4d.y;
|
|
case 2:
|
|
return (v4d.w - v4d.x);
|
|
case 3:
|
|
return v4d.x;
|
|
case 4:
|
|
return v4d.z;
|
|
case 5:
|
|
return (v4d.w - v4d.z);
|
|
}
|
|
return 0.0f;
|
|
}
|
|
|
|
inline void
|
|
GetDoubleBC
|
|
(
|
|
int nr,
|
|
Stuff::Scalar& result1,
|
|
Stuff::Scalar& result2,
|
|
const Stuff::Vector4D& v4d1,
|
|
const Stuff::Vector4D& v4d2
|
|
)
|
|
{
|
|
switch(nr)
|
|
{
|
|
case 0:
|
|
result1 = (v4d1.w - v4d1.y);
|
|
result2 = (v4d2.w - v4d2.y);
|
|
break;
|
|
case 1:
|
|
result1 = v4d1.y;
|
|
result2 = v4d2.y;
|
|
break;
|
|
case 2:
|
|
result1 = (v4d1.w - v4d1.x);
|
|
result2 = (v4d2.w - v4d2.x);
|
|
break;
|
|
case 3:
|
|
result1 = v4d1.x;
|
|
result2 = v4d2.x;
|
|
break;
|
|
case 4:
|
|
result1 = v4d1.z;
|
|
result2 = v4d2.z;
|
|
break;
|
|
case 5:
|
|
result1 = (v4d1.w - v4d1.z);
|
|
result2 = (v4d2.w - v4d2.z);
|
|
break;
|
|
}
|
|
}
|
|
|
|
inline Stuff::Scalar
|
|
GetLerpFactor
|
|
(
|
|
int nr,
|
|
const Stuff::Vector4D& v4d1,
|
|
const Stuff::Vector4D& v4d2
|
|
)
|
|
{
|
|
Stuff::Scalar result1, result2;
|
|
|
|
switch(nr)
|
|
{
|
|
case 0:
|
|
result1 = (v4d1.w - v4d1.y);
|
|
result2 = (v4d2.w - v4d2.y);
|
|
break;
|
|
case 1:
|
|
result1 = v4d1.y;
|
|
result2 = v4d2.y;
|
|
break;
|
|
case 2:
|
|
result1 = (v4d1.w - v4d1.x);
|
|
result2 = (v4d2.w - v4d2.x);
|
|
break;
|
|
case 3:
|
|
result1 = v4d1.x;
|
|
result2 = v4d2.x;
|
|
break;
|
|
case 4:
|
|
result1 = v4d1.z;
|
|
result2 = v4d2.z;
|
|
break;
|
|
case 5:
|
|
result1 = (v4d1.w - v4d1.z);
|
|
result2 = (v4d2.w - v4d2.z);
|
|
break;
|
|
default:
|
|
result1 = 0.0f;
|
|
result2 = 0.0f;
|
|
STOP(("Invalid plane number used !"));
|
|
break;
|
|
}
|
|
if(result1 == 0.0f)
|
|
{
|
|
return 0.0f;
|
|
}
|
|
Verify(!Stuff::Close_Enough(result1, result2, Stuff::SMALL*0.1f));
|
|
return result1 / (result1 - result2);
|
|
}
|
|
}
|