mirror of
https://github.com/OpenTrespasser/JurassicParkTrespasser.git
synced 2024-12-18 22:51:56 +00:00
7b67af383e
- std::* treatment
309 lines
9.2 KiB
C++
309 lines
9.2 KiB
C++
/***********************************************************************************************
|
|
*
|
|
* Copyright © DreamWorks Interactive. 1996
|
|
*
|
|
* Contents:
|
|
* Implementation of Material.hpp.
|
|
*
|
|
***********************************************************************************************
|
|
*
|
|
* $Log:: /JP2_PC/Source/Lib/Renderer/Material.cpp $
|
|
*
|
|
* 15 98/10/06 18:48 Speter
|
|
* Fixed silly bug.
|
|
*
|
|
* 14 98/10/06 16:23 Speter
|
|
* Hack to fix incorrect spec angle data.
|
|
*
|
|
* 13 8/27/98 1:51p Asouth
|
|
* fully specified the include file (there are two Materials in the DB)
|
|
*
|
|
* 12 98.08.03 6:40p Mmouni
|
|
* Removed new material message.
|
|
*
|
|
* 11 98/07/22 14:44 Speter
|
|
* Now contains functions for performing all reflection calculations, independent of clut.
|
|
* Slightly fixed diffuse/specular combining functions.
|
|
*
|
|
* 10 98.06.30 9:30p Mmouni
|
|
* pmatFindShared() now returns a pointer to matDEFAULT if the requested material is equal to
|
|
* matDEFAULT.
|
|
*
|
|
* 9 98/04/26 20:16 Speter
|
|
* Cleaned up comments.
|
|
*
|
|
* 8 4/06/98 3:43p Agrant
|
|
* tsmMaterialInstances now accessible outside of file (for WDBase reset)
|
|
*
|
|
* 7 2/25/98 6:52p Agrant
|
|
* Printout for a new material definition.
|
|
*
|
|
* 6 10/24/97 7:31p Agrant
|
|
* Material instancing
|
|
*
|
|
* 5 97/10/12 21:44 Speter
|
|
* Added lighting functions fDiffuse(), fSpecular(), fReflected(). Changed rvMAX_COLOURED to
|
|
* 1.0, rvMAX_WHITE to 2.0; works better with cluts.
|
|
*
|
|
* 4 97/08/05 13:29 Speter
|
|
* Changed some default settings.
|
|
*
|
|
* 3 97/06/23 19:26 Speter
|
|
* Moved const material initialisation to .cpp files.
|
|
*
|
|
* 2 97/02/13 18:16 Speter
|
|
* Added fAngleFromAngleWidth function.
|
|
*
|
|
* 1 97/02/13 14:03 Speter
|
|
* Contains implementation of some TAngleWidth functions.
|
|
*
|
|
**********************************************************************************************/
|
|
|
|
#include "Common.hpp"
|
|
#include "Lib/Renderer/Material.hpp"
|
|
#include "Lib/Math/FastTrig.hpp"
|
|
#include "Lib/Math/FastSqrt.hpp"
|
|
|
|
#include "Lib/Sys/DebugConsole.hpp"
|
|
|
|
#include <math.h>
|
|
#include <set>
|
|
#include <memory.h>
|
|
|
|
|
|
//**********************************************************************************************
|
|
//
|
|
// TAngleWidth functions.
|
|
//
|
|
|
|
// The width in radians of the Sun in the sky (radius divided by distance).
|
|
const float fRADIANS_OF_SUN = 0.05;
|
|
const TAngleWidth angwDEFAULT_SIZE = angwFromSin(fRADIANS_OF_SUN);
|
|
|
|
//**********************************************************************************************
|
|
TAngleWidth angwFromAngle(float f_angle)
|
|
{
|
|
// Assert for range, but add some tolerance.
|
|
Assert(bWithin(f_angle, 0, dPI_2 * 1.1));
|
|
return cos(f_angle);
|
|
}
|
|
|
|
//**********************************************************************************************
|
|
TAngleWidth angwFromSin(float f_sin)
|
|
{
|
|
// Convert the sine to a cosine.
|
|
Assert(bWithin(f_sin, 0, 1));
|
|
return sqrt(1.0 - f_sin * f_sin);
|
|
}
|
|
|
|
//**********************************************************************************************
|
|
TAngleWidth angwFromPower(float f_power)
|
|
{
|
|
//
|
|
// Check this funky derivation:
|
|
//
|
|
// We want to find an angw such that the cosine at which intensity is half-maximum
|
|
// is the same as for the power function. Or:
|
|
//
|
|
// cos_half_max ^ power = 0.5
|
|
// cos_half_max = 0.5 ^ (1/power)
|
|
//
|
|
// The angw describes the cosine at which intensity is zero. Since our angle interpolation
|
|
// is linear with cosine, this cosine is just twice the distance from 1 as cos_half_max:
|
|
//
|
|
// cos_zero = 1 - 2(1 - cos_half_max)
|
|
// = 2 cos_half_max - 1
|
|
// = 2 * (0.5 ^ (1/power)) - 1
|
|
//
|
|
// The final wrinkle. Since the power factor is for use with Phong lighting, and we are
|
|
// doing Blinn lighting, the angle we want is half the one just calculated. To turn a
|
|
// cosine into the cosine of half the angle, we do this:
|
|
//
|
|
// cos_half = sqrt((cos_zero + 1)/2)
|
|
// = sqrt(0.5 ^ (1/power))
|
|
// = 0.5 ^ (0.5/power)
|
|
//
|
|
|
|
Assert(f_power > 0);
|
|
return pow(0.5, 0.5 / f_power);
|
|
}
|
|
|
|
//**********************************************************************************************
|
|
float fAngleFromAngleWidth(TAngleWidth angw)
|
|
{
|
|
Assert(bWithin(angw, 0, 1));
|
|
return acos(angw);
|
|
}
|
|
|
|
/*
|
|
Sample conversions:
|
|
|
|
Power Angle TAngleWidth (cosine half-angle)
|
|
1 90 d .7071
|
|
4.4 45 d .9239
|
|
10 30 d .9659
|
|
40 15 d .9914
|
|
91 10 d .9962
|
|
364 5 d .99905
|
|
2275 2 d .99985
|
|
9012 1 d .99996
|
|
*/
|
|
|
|
/*
|
|
(Bump angle resolution is .05 radians; max cosine is .99880; cosine levels = 830)
|
|
*/
|
|
|
|
|
|
//******************************************************************************************
|
|
//
|
|
// CMaterial implementation
|
|
//
|
|
|
|
//**************************************************************************************
|
|
const CMaterial* CMaterial::pmatFindShared
|
|
(
|
|
TReflectVal rv_diffuse,
|
|
TReflectVal rv_specular,
|
|
TAngleWidth angw_specular,
|
|
bool b_reflective,
|
|
bool b_refractive,
|
|
float f_refract_index,
|
|
TReflectVal rv_emissive
|
|
)
|
|
{
|
|
CMaterial mat(rv_diffuse, rv_specular, angw_specular, b_reflective, b_refractive, f_refract_index, rv_emissive);
|
|
|
|
// Hack to fix incorrect data. Somehow, the degrees got replaced with the cosine,
|
|
// and amazingly, it happened more than once! Thus, the while loop.
|
|
if (mat.rvSpecular && mat.angwSpecular != angwZERO)
|
|
{
|
|
float f_degrees;
|
|
while ((f_degrees = fAngleFromAngleWidth(mat.angwSpecular) / dDEGREES) < 1.0f)
|
|
mat.angwSpecular = f_degrees;
|
|
}
|
|
|
|
return pmatFindShared(&mat);
|
|
}
|
|
|
|
typedef std::set<CMaterial, std::less<CMaterial> > TSM;
|
|
TSM tsmMaterialInstances;
|
|
|
|
//**************************************************************************************
|
|
const CMaterial* CMaterial::pmatFindShared(const CMaterial *pmat)
|
|
{
|
|
// Specail case for default material, just return a pointer to it.
|
|
// This is necessary so that when &matDEFAULT is used directly
|
|
// it is equivalent to calling pmatFindShared with the default
|
|
// parameters.
|
|
if (*pmat == matDEFAULT)
|
|
return &matDEFAULT;
|
|
|
|
// Insert or find, please.
|
|
std::pair<TSM::iterator, bool> p = tsmMaterialInstances.insert(*pmat);
|
|
|
|
// If we found a duplicate, it will do.
|
|
// If we inserted a new one, the new one will do.
|
|
//const CInfo* pinfo_ret = &(*p.first);
|
|
|
|
return &(*p.first);
|
|
}
|
|
|
|
//**************************************************************************************
|
|
bool CMaterial::operator< (const CMaterial& mat) const
|
|
{
|
|
return 0 > memcmp(this, &mat, sizeof(CMaterial));
|
|
}
|
|
|
|
//**************************************************************************************
|
|
bool CMaterial::operator== (const CMaterial& mat) const
|
|
{
|
|
return memcmp(this, &mat, sizeof(CMaterial)) == 0;
|
|
}
|
|
|
|
//*****************************************************************************************
|
|
TReflectVal CMaterial::rvSingle(TLightVal lv) const
|
|
{
|
|
Assert(bWithin(lv, 0.0, 1.0));
|
|
|
|
if (rvSpecular > rvDiffuse)
|
|
{
|
|
TLightVal lv_spec = fSpecular(lv, angwDEFAULT_SIZE);
|
|
return rvCombined(lv, lv_spec);
|
|
}
|
|
return rvCombined(lv, 0);
|
|
}
|
|
|
|
//******************************************************************************************
|
|
float CMaterial::fReflected(float f_cos_in) const
|
|
{
|
|
//
|
|
// Apply the formula giving reflectance for a given angle. If i is angle of incidence,
|
|
// and r is angle of reflection, then reflectance is:
|
|
//
|
|
// R = sin²(i-r) / 2 sin²(i+r) + tan²(i-r) / 2 tan²(i+r)
|
|
// = (sin²(i-r) / 2 sin²(i+r)) (1 + (1 - sin²(i+r)) / 2 (1 - sin²(i-r)))
|
|
//
|
|
// By Snell's law, if n is the index of refraction:
|
|
//
|
|
// n(i) sin i = n(r) sin r
|
|
//
|
|
// Therefore,
|
|
//
|
|
// r = asin (sin i * n(i) / n(r))
|
|
//
|
|
|
|
if (!bRefractive)
|
|
return 0.0;
|
|
|
|
// No reflection from opposite side.
|
|
if (f_cos_in < 0.0)
|
|
return 0.0;
|
|
|
|
if (f_cos_in == 1.0)
|
|
{
|
|
// Return the limit of the formula, which is (n-1)² / (n+1)².
|
|
return Square( (fRefractiveIndex - 1.0) / (fRefractiveIndex + 1.0) );
|
|
}
|
|
|
|
float f_angle_in = acos(f_cos_in);
|
|
float f_sin_out = sin(f_angle_in) / fRefractiveIndex;
|
|
|
|
if (f_sin_out >= 1.0)
|
|
// Total internal reflection; occurs only when passing from slower to faster medium.
|
|
return 1.0;
|
|
else
|
|
{
|
|
float f_angle_out = asin(f_sin_out);
|
|
float f_sin2_m = Square( sin(f_angle_in - f_angle_out) );
|
|
float f_sin2_p = Square( sin(f_angle_in + f_angle_out) );
|
|
|
|
return f_sin2_m / f_sin2_p *
|
|
(1.0 + (1.0 - f_sin2_p) / (1.0 - f_sin2_m) ) *
|
|
0.5;
|
|
}
|
|
}
|
|
|
|
//**********************************************************************************************
|
|
//
|
|
// Global CMaterial definitions.
|
|
//
|
|
|
|
// Default material is same as matte material: no specular highlights.
|
|
const CMaterial matMATTE;
|
|
|
|
// Material with sharp white highlights.
|
|
const CMaterial matSHINY(1.0, 1.90, angwFromAngle(0.2)); //lint !e605
|
|
|
|
// Metal has dull highlights of material colour, and very little diffuse reflection.
|
|
const CMaterial matMETAL(0.2, 1.10, angwFromAngle(0.3)); //lint !e605
|
|
|
|
// Water has max diffuse reflection (because that is used to simulate transmission),
|
|
// maximum sharpness, and a refractive index.
|
|
const CMaterial matWATER(1.0, 1.80, angwZERO, true, true, 1.33); //lint !e605
|
|
|
|
// Mirrored surface has no diffuse reflection, sharp specular, and is reflective.
|
|
const CMaterial matMIRROR(0.0, 2.0, angwFromAngle(0), true);
|
|
|
|
const CMaterial matDEFAULT;
|
|
|