#include "pch.h"
#include "regkey.h"
template <class Type>
class ThingRef {
private:
Type* m_pt;
public:
ThingRef() :
m_pt(NULL)
{
}
ThingRef(const Type* pt)
{
m_pt = (Type*)pt;
if (m_pt)
m_pt->AddRef();
}
ThingRef(const ThingRef& tref) :
m_pt(tref.m_pt)
{
if (m_pt)
m_pt->AddRef();
}
ThingRef* Pointer()
{
return this;
}
Type** operator&()
{
if (m_pt) {
m_pt->Release();
m_pt = NULL;
}
return &m_pt;
}
ThingRef& operator=(const ThingRef& tref)
{
Type* ptOld = m_pt;
m_pt = tref.m_pt;
if (m_pt)
m_pt->AddRef();
if (ptOld)
ptOld->Release();
return *this;
}
ThingRef& operator=(const Type* ptNew)
{
Type* ptOld = m_pt;
m_pt = (Type*)ptNew;
if (m_pt)
m_pt->AddRef();
if (ptOld)
ptOld->Release();
return *this;
}
~ThingRef(void)
{
}
operator Type*(void) const { return m_pt; }
Type& operator*(void) const { return *m_pt; }
Type* operator->(void) const { return m_pt; }
friend bool operator==(const ThingRef& t1, Type* pt)
{
return t1.m_pt == pt;
}
friend bool operator!=(const ThingRef& t1, Type* pt)
{
return t1.m_pt != pt;
}
inline friend bool operator<(const ThingRef& t1, Type* pt) { return t1.m_pt < pt; };
inline friend bool operator>(const ThingRef& t1, Type* pt) { return t1.m_pt > pt; };
inline friend bool operator<=(const ThingRef& t1, Type* pt) { return t1.m_pt <= pt; };
inline friend bool operator>=(const ThingRef& t1, Type* pt) { return t1.m_pt >= pt; };
};
class ThingGeoImpl : public ThingGeo
{
private:
ThingRef<Modeler> m_pmodeler;
ThingRef<Number> m_ptime;
float m_timeLast;
float m_time;
float m_radius;
float m_radiusMesh;
float m_radiusCull;
ThingRef<FrameDataListValue> m_pframes;
Orientation m_orientation;
Vector m_vecPosition;
Vector m_vecPositionLast;
Vector m_vecVelocityLast;
ThingRef<Geo> m_pgeoTrail;
ThingRef<Geo> m_pgeoMesh;
ThingRef<HullHitGeo> m_phullHitGeo;
ThingRef<Geo> m_pgeoLights;
ThingRef<Geo> m_pgeoBounds;
bool m_bVisibleShip;
bool m_bVisible;
bool m_bClip;
bool m_bDrawLights;
bool m_bFlip;
bool m_bShadeAlways;
float m_scale;
ThingRef<Surface> m_psurfaceTexture;
ThingRef<ModifiableVectorValue> m_pvectorPositionTrail;
ThingRef<ModifiableVectorValue> m_pvectorRightTrail;
ThingRef<ModifiableBoolean> m_pbooleanMoving;
Vector m_vecTrailOffset;
float m_countFlares;
ThingRef<GroupGeo> m_pgroupFlares;
float m_fThrustPower;
Vector m_vecThrustDirection;
ThingRef<ParticleGeo> m_pParticleGeo;
ThingRef<BitsGeo> m_pbitsGeo;
ThingRef<Image> m_pimageGlow;
float m_scaleGlow;
Color m_colorGlow;
float m_sizeSmoke;
float m_fAfterburnerFireDuration;
float m_fAfterburnerSmokeDuration;
bool m_bUsePrivateAfterburners;
#define MAX_DAMAGE_FRAMES 10
FrameData* m_damageFrames;
int m_iLastDamageIndex;
bool m_bShowDamage;
#define TELEPORT_SPARK_COUNT 3
#define TIME_FOR_TELEPORT_SPARKS 3
float m_fTimeUntilRipcord;
float m_fTimeUntilAleph;
private:
DWORD LoadPreference(const ZString& szName, DWORD dwDefault)
{
HKEY hKey;
DWORD dwResult = dwDefault;
if (ERROR_SUCCESS == ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, ALLEGIANCE_REGISTRY_KEY_ROOT, 0, KEY_READ, &hKey))
{
DWORD dwSize = sizeof(dwResult);
DWORD dwType = REG_DWORD;
::RegQueryValueEx(hKey, szName, NULL, &dwType, (BYTE*)&dwResult, &dwSize);
::RegCloseKey(hKey);
if (dwType != REG_DWORD)
dwResult = dwDefault;
}
return dwResult;
}
public:
ThingGeoImpl(Modeler* pmodeler, Number* ptime) :
m_ptime(ptime),
m_pmodeler(pmodeler),
m_bVisible(true),
m_bVisibleShip(true),
m_countFlares(0),
m_sizeSmoke(4.0f),
m_fAfterburnerFireDuration(0.333f),
m_fAfterburnerSmokeDuration(2.0f),
m_scale(1),
m_bShadeAlways(false),
m_iLastDamageIndex(-1),
m_bShowDamage(true),
m_fTimeUntilRipcord(-1.0f),
m_fTimeUntilAleph(-1.0f)
{
m_damageFrames = new FrameData[MAX_DAMAGE_FRAMES];
m_time = m_ptime->GetValue();
m_vecPosition = Vector(0, 0, 0);
m_pgroupFlares = GroupGeo::Create();
m_phullHitGeo = CreateHullHitGeo(pmodeler, ptime);
m_pimageGlow = pmodeler->LoadImage(AWF_EFFECT_AFTERBURNER_GLOW, true);
SetAfterburnerThrust(Vector::GetZero(), 0.0f);
SetThrust(0);
m_timeLast = m_time;
m_vecPositionLast = m_vecPosition;
m_vecVelocityLast = m_vecPosition - m_vecPosition;
m_bUsePrivateAfterburners = LoadPreference ("PrivateAfterburners", 0) ? true : false;
if (m_bUsePrivateAfterburners)
{
m_sizeSmoke = 2.0f;
m_fAfterburnerFireDuration = 0.33f;
m_fAfterburnerSmokeDuration = 0.66f;
}
}
~ThingGeoImpl()
{
__try {
delete[] m_damageFrames;
m_pmodeler = NULL;
m_pframes = NULL;
m_pgeoTrail = NULL;
m_pgeoMesh = NULL;
m_phullHitGeo = NULL;
m_pgeoLights = NULL;
m_pgeoBounds = NULL;
m_psurfaceTexture = NULL;
m_pvectorPositionTrail = NULL;
m_pvectorRightTrail = NULL;
m_pbooleanMoving = NULL;
m_pgroupFlares = NULL;
m_pParticleGeo = NULL;
m_pbitsGeo = NULL;
m_pimageGlow = NULL;
} __except (true) {
}
}
bool IsValid()
{
return (m_pgeoMesh != NULL);
}
void SetBoundsGeo(Geo* pgeo)
{
m_pgeoBounds = pgeo;
}
class TargetGeo : public Geo {
private:
TRef<ThingGeoImpl> m_pthing;
public:
TargetGeo(ThingGeoImpl* pthing) :
m_pthing(pthing)
{
}
float GetRadius(const Matrix& mat)
{
return m_pthing->GetRadius(mat);
}
void Render(Context* pcontext)
{
pcontext->Scale3(1.0f / m_pthing->GetRadius());
m_pthing->RenderTargetGeo(pcontext);
}
void Evaluate()
{
m_pthing->Evaluate();
Changed();
}
};
Geo* GetTargetGeo()
{
return new TargetGeo(this);
}
class ThingGeoWrapper : public Geo {
public:
TRef<ThingGeoImpl> m_pthing;
ThingGeoWrapper(ThingGeoImpl* pthing) :
m_pthing(pthing)
{
}
float GetRadius(const Matrix& mat)
{
return m_pthing->GetRadius(mat);
}
void Render(Context* pcontext)
{
m_pthing->Render(pcontext);
}
void Evaluate()
{
m_pthing->Evaluate();
Changed();
}
};
Geo* GetGeo()
{
return new ThingGeoWrapper(this);
}
float m_thrust;
void UpdateGlow()
{
if (m_thrust != m_thrust) m_thrust = 0.0f; if (m_thrust < 0.0f) m_thrust = 0.0f;
if (m_thrust > 100.0f) m_thrust = 100.0f; float value = m_thrust;
if (m_fThrustPower != 0) {
value = 1.0f;
}
m_colorGlow =
HSBColor(
pow(value, 5) / 6.0f,
1 - 0.5f * (pow(value, 20)),
1 - pow(1 - value, 3)
);
m_scaleGlow = 1 - pow(1 - value, 2);
}
void SetAfterburnerThrust(const Vector& vecThrustDirection, float power)
{
m_vecThrustDirection = vecThrustDirection;
m_fThrustPower = power;
UpdateGlow();
}
void SetThrust(float value)
{
ZAssert(value >= 0.0f && value < 1.0001f);
if (value > 1.0f)
value = 1.0f;
m_thrust = value;
UpdateGlow();
}
void SetParticleGeo (ParticleGeo* pParticleGeo)
{
m_pParticleGeo = pParticleGeo;
}
void SetAfterburnerSmokeSize (float fSize)
{
m_sizeSmoke = fSize;
}
void SetAfterburnerFireDuration (float fDuration)
{
m_fAfterburnerFireDuration = fDuration;
}
void SetAfterburnerSmokeDuration (float fDuration)
{
m_fAfterburnerSmokeDuration = fDuration;
}
void AddFireAndSmoke (void)
{
if (m_bVisible && (s_iShowSmoke > 0) && (m_time != m_timeLast))
{
Vector vecDeltaLastPosition = m_vecPosition - m_vecPositionLast;
float fDeltaTime = m_time - m_timeLast;
Vector vecFrameVelocity = vecDeltaLastPosition / fDeltaTime;
Vector vecFrameAcceleration = (vecFrameVelocity - m_vecVelocityLast) / fDeltaTime;
AddThrustParticles (fDeltaTime, vecFrameVelocity, vecFrameAcceleration);
if (m_bVisibleShip)
{
AddDamageParticles (fDeltaTime, vecFrameVelocity, vecFrameAcceleration);
AddTeleportParticles (fDeltaTime, vecFrameVelocity, vecFrameAcceleration);
}
m_timeLast = m_time;
m_vecPositionLast = m_vecPosition;
m_vecVelocityLast = vecFrameVelocity;
}
}
void ComputeFireParticleParameters
(
float fTimeSinceCreation,
const Vector& vecFrameOrigin,
const Vector& vecFrameVelocity,
const Vector& vecFrameAcceleration,
const Vector& vecFrameThrustDirection,
float fEjectionVelocityMin,
float fEjectionVelocityMax,
float fEffectDuration,
float fStartPositionVariance,
float fEffectSize,
Vector& vecStartPosition,
Vector& vecStartVelocity
)
{
float fEjectionVelocity = random (fEjectionVelocityMin, fEjectionVelocityMax) * fEffectSize;
Vector vecThrust = fEjectionVelocity * vecFrameThrustDirection;
Vector vecTheoreticalStartPosition = vecFrameOrigin + (fTimeSinceCreation * vecThrust);
Vector vecTheoreticalEndPosition = vecTheoreticalStartPosition + (fEffectDuration * vecThrust);
vecStartPosition = vecTheoreticalStartPosition + ((fStartPositionVariance * fEffectSize) * Vector::RandomDirection ());
Vector vecRelativeVelocity = (vecTheoreticalEndPosition - vecStartPosition) / fEffectDuration;
vecStartVelocity = vecRelativeVelocity + vecFrameVelocity - (fTimeSinceCreation * vecFrameAcceleration);
}
void ComputeSmokeParticleParameters
(
float fTimeSinceCreation,
const Vector& vecFrameOrigin,
const Vector& vecFrameVelocity,
const Vector& vecFrameAcceleration,
const Vector& vecFrameThrustDirection,
float fEjectionVelocityMin,
float fEjectionVelocityMax,
float fStartPositionVariance,
float fEffectSize,
Vector& vecStartPosition,
Vector& vecStartVelocity
)
{
float fEjectionVelocity = random (fEjectionVelocityMin, fEjectionVelocityMax) * fEffectSize;
Vector vecThrust = fEjectionVelocity * vecFrameThrustDirection;
Vector vecTheoreticalStartPosition = vecFrameOrigin + (fTimeSinceCreation * vecThrust);
vecStartPosition = vecTheoreticalStartPosition + ((fStartPositionVariance * fEffectSize) * Vector::RandomDirection ());
vecStartVelocity = vecThrust + vecFrameVelocity - (fTimeSinceCreation * vecFrameAcceleration);
}
void AddTeleportParticles (float fDeltaTime, const Vector& vecFrameVelocity, const Vector& vecFrameAcceleration)
{
float fTimeUntilTeleport = m_fTimeUntilRipcord;
if ((fTimeUntilTeleport < 0.0f) || ((m_fTimeUntilAleph >= 0.0f) && (m_fTimeUntilAleph < m_fTimeUntilRipcord)))
fTimeUntilTeleport = m_fTimeUntilAleph;
if (fTimeUntilTeleport >= 0.0f)
{
Vector vecX;
Vector vecY;
Vector vecZ;
float fSpeedSquared = vecFrameVelocity.LengthSquared ();
if (fSpeedSquared > 0.0f)
{
vecZ = vecFrameVelocity / sqrtf (fSpeedSquared);
vecX = vecZ.GetOrthogonalVector ();
vecY = CrossProduct (vecX, vecZ);
}
else
{
vecZ = m_orientation.GetForward ();
vecX = m_orientation.GetRight ();
vecY = CrossProduct (vecX, vecZ);
}
float fTimeToShowSparks = 3.0f;
fTimeToShowSparks = fTimeUntilTeleport / fTimeToShowSparks;
if (fTimeToShowSparks > 1.0f)
fTimeToShowSparks = 1.0f;
if (fTimeToShowSparks < 0.0f)
fTimeToShowSparks = 0.0f;
float fCountUpToOne = 1.0f - fTimeToShowSparks;
float fScaledUpCount = ((fCountUpToOne * 2.0f) + 1.0f) * m_radius;
int iSparkCount = int (floorf (TELEPORT_SPARK_COUNT * float (s_iShowSmoke) * fCountUpToOne) + 0.5f);
float fSparkTimeDelta = fDeltaTime / float (iSparkCount);
for (int i = 0; i < iSparkCount; i++)
{
float fRadius = m_radius * random (0.75f, 1.25f);
Vector vecParticleVelocity = Vector::RandomDirection () * fRadius;
Vector vecParticleVelocityProjected = ((vecParticleVelocity * vecX) * vecX) + ((vecParticleVelocity * vecY) * vecY);
Vector vecDeltaProjection = vecParticleVelocityProjected - vecParticleVelocity;
vecParticleVelocity = (fTimeToShowSparks * vecDeltaProjection) + vecParticleVelocity;
ParticleData* pParticleData = m_pParticleGeo->AddSpark ();
float fRandomStartTime = random (float (i), float (i) + 1.0f) * fSparkTimeDelta;
pParticleData->m_vecVelocity = vecFrameVelocity + vecParticleVelocity;
pParticleData->m_vecAcceleration = -vecFrameVelocity;
pParticleData->m_vecPosition =
m_vecPositionLast
+ (fTimeToShowSparks * m_radius * 1.5f * vecZ)
+ (vecParticleVelocity * ((1.0f - fTimeToShowSparks) + fRandomStartTime))
+ (0.5f * fRandomStartTime * fRandomStartTime * pParticleData->m_vecAcceleration);
pParticleData->m_fSize = random (fScaledUpCount * 0.15f, fScaledUpCount * 0.3f);
pParticleData->m_fDuration = 0.5f;
}
}
m_fTimeUntilRipcord -= fDeltaTime;
m_fTimeUntilAleph -= fDeltaTime;
}
void AddDamageParticles (float fDeltaTime, const Vector& vecFrameVelocity, const Vector& vecFrameAcceleration)
{
if ((m_iLastDamageIndex >= 0) && m_pParticleGeo && m_bShowDamage)
{
float fDeltaLength = vecFrameVelocity.Length() * fDeltaTime;
int iParticlesPerFrame = int (fDeltaLength);
if (iParticlesPerFrame < 1)
iParticlesPerFrame = 1;
if (iParticlesPerFrame > s_iShowSmoke)
iParticlesPerFrame = s_iShowSmoke;
float fUniformTimeBetweenParticles = fDeltaTime / float (iParticlesPerFrame);
Vector vecStartPosition;
Vector vecStartVelocity;
for (int i = 0; i <= m_iLastDamageIndex; i++)
{
FrameData& frame = m_damageFrames[i];
Vector vecFrameOrigin = m_vecPositionLast + (frame.m_vecPosition * m_orientation);
Vector vecFrameForward = frame.m_vecForward * m_orientation;
for (int iParticleCount = 0; iParticleCount < iParticlesPerFrame; iParticleCount++)
{
float fEffectSize = random (1.5f, 2.5f);
float fJitteredTimeSinceCreation = fUniformTimeBetweenParticles * (float (iParticleCount) + random (0.0f, 1.0f));
ComputeFireParticleParameters (fJitteredTimeSinceCreation, vecFrameOrigin, vecFrameVelocity, vecFrameAcceleration, vecFrameForward, 4.0f, 6.0f, 0.35f, 0.05f, fEffectSize, vecStartPosition, vecStartVelocity);
ParticleData* pParticleData = m_pParticleGeo->AddSmoke ();
pParticleData->m_vecPosition = vecStartPosition;
pParticleData->m_vecVelocity = vecStartVelocity;
pParticleData->m_vecAcceleration = vecStartVelocity * -0.333f;
pParticleData->m_fSize = fEffectSize;
pParticleData->m_fDuration = 0.35f;
}
}
}
}
void AddThrustParticles (float fDeltaTime, const Vector& vecFrameVelocity, const Vector& vecFrameAcceleration)
{
if ((m_pframes != NULL) && m_pParticleGeo && (m_fThrustPower > 0))
{
float fDeltaLength = vecFrameVelocity.Length() * fDeltaTime;
int iParticlesPerFrame = int (2.0f * fDeltaLength / (m_sizeSmoke * m_fThrustPower));
if (iParticlesPerFrame < 1)
iParticlesPerFrame = 1;
else if (iParticlesPerFrame > s_iShowSmoke)
iParticlesPerFrame = s_iShowSmoke;
float fUniformTimeBetweenParticles = fDeltaTime / float (iParticlesPerFrame);
Vector vecStartPosition;
Vector vecStartVelocity;
FrameDataListValue::FrameList::Iterator iter(m_pframes->GetList ());
while (!iter.End ())
{
FrameData& frame = iter.Value ();
int iFrameType = 0;
if (frame.m_strName.Find ("rocket") != -1)
iFrameType = 1;
else if (frame.m_strName.Find ("smoke") != -1)
{
if (m_bUsePrivateAfterburners)
iFrameType = 1;
else
iFrameType = 2;
}
switch (iFrameType)
{
case 1: {
Vector vecFrameOrigin = m_vecPositionLast + TransformOffset (frame.m_vecPosition);
for (int iParticleCount = 0; iParticleCount < iParticlesPerFrame; iParticleCount++)
{
float fEffectSize = m_sizeSmoke * random (0.875f, 1.125f);
float fJitteredTimeSinceCreation = fUniformTimeBetweenParticles * (float (iParticleCount) + random (0.0f, 1.0f));
{
ComputeFireParticleParameters (
fJitteredTimeSinceCreation,
vecFrameOrigin,
vecFrameVelocity,
vecFrameAcceleration,
m_vecThrustDirection,
9.0f,
12.0f,
m_fAfterburnerFireDuration,
0.2f,
fEffectSize * m_fThrustPower,
vecStartPosition,
vecStartVelocity
);
ParticleData* pParticleData = m_pParticleGeo->AddFire ();
pParticleData->m_vecPosition = vecStartPosition;
pParticleData->m_vecVelocity = vecStartVelocity;
pParticleData->m_vecAcceleration = Vector::GetZero ();
pParticleData->m_fSize = fEffectSize;
pParticleData->m_fDuration = m_fAfterburnerFireDuration;
}
{
ComputeSmokeParticleParameters (
fJitteredTimeSinceCreation,
vecFrameOrigin,
vecFrameVelocity,
vecFrameAcceleration,
m_vecThrustDirection,
18.0f,
24.0f,
0.1f,
fEffectSize * m_fThrustPower,
vecStartPosition,
vecStartVelocity
);
ParticleData* pParticleData = m_pParticleGeo->AddSmoke ();
pParticleData->m_vecPosition = vecStartPosition;
pParticleData->m_vecVelocity = vecStartVelocity;
pParticleData->m_vecAcceleration = Vector::GetZero ();
pParticleData->m_fSize = fEffectSize;
pParticleData->m_fDuration = m_fAfterburnerSmokeDuration;
}
}
}
break;
case 2: {
Vector vecFrameOrigin = m_vecPositionLast + TransformOffset (frame.m_vecPosition);
for (int iParticleCount = 0; iParticleCount < iParticlesPerFrame; iParticleCount++)
{
float fEffectSize = m_sizeSmoke * random (0.875f, 1.125f);
float fRandomDelta = m_fThrustPower * fEffectSize;
Vector vecRandomModulation (random (-fRandomDelta, fRandomDelta), random (-fRandomDelta, fRandomDelta), random (-fRandomDelta, fRandomDelta));
float fJitteredTimeSinceCreation = fUniformTimeBetweenParticles * (float (iParticleCount) + random (0.0f, 1.0f));
{
ComputeSmokeParticleParameters (
fJitteredTimeSinceCreation,
vecFrameOrigin,
vecFrameVelocity,
Vector::GetZero(),
m_vecThrustDirection,
12.0f,
15.0f,
0.1f,
fEffectSize * m_fThrustPower,
vecStartPosition,
vecStartVelocity
);
ParticleData* pParticleData = m_pParticleGeo->AddAfterburner ();
pParticleData->m_vecPosition = vecStartPosition;
pParticleData->m_vecVelocity = vecStartVelocity + vecRandomModulation;
pParticleData->m_vecAcceleration = Vector::GetZero ();
pParticleData->m_fSize = fEffectSize;
pParticleData->m_fDuration = m_fAfterburnerSmokeDuration;
}
}
}
break;
}
iter.Next ();
}
}
}
void SetTrailColor(const Color& color)
{
if (m_pframes)
{
m_vecTrailOffset = Vector(0, 0, 0);
FrameDataListValue::FrameList::Iterator iter(m_pframes->GetList());
while (!iter.End())
{
FrameData& data = iter.Value();
if (data.m_strName == "trail") {
m_vecTrailOffset = data.m_vecPosition;
m_pgeoTrail =
CreateTrailGeo(
m_pmodeler,
Color (color.R() * 0.5f, color.G() * 0.5f, color.B() * 0.5f),
m_pvectorPositionTrail = new ModifiableVectorValue (Vector(0, 0, 0)),
m_pvectorRightTrail = new ModifiableVectorValue (Vector(0, 0, 0)),
m_pbooleanMoving = new ModifiableBoolean (false),
m_ptime
);
UpdateTrailPosition ();
break;
}
iter.Next();
}
}
}
void SetShadeAlways(bool bShadeAlways)
{
m_bShadeAlways = bShadeAlways;
}
TRef<INameSpace> LoadMDL(short options, const ZString& strName, Image* pimageTexture)
{
TRef<INameSpace> pns = m_pmodeler->GetNameSpace(strName);
if (pns) {
if (ZSucceeded(LoadMDL(options, pns, pimageTexture))) {
return pns;
}
}
return NULL;
}
HRESULT LoadMDL(short options, INameSpace* pns, Image* pimageTexture)
{
TRef<Geo> pgeo = Geo::Cast(pns->FindMember("object"));
if (pgeo) {
m_pgeoLights = Geo::Cast(pns->FindMember("lights"));
m_pframes = (FrameDataListValue*)pns->FindMember("frames");
HRESULT hr = Load(options, pgeo, pimageTexture);
if (ZFailed(hr)) {
return E_FAIL;
}
return S_OK;
}
return E_FAIL;
}
HRESULT Load(short options, Geo* pgeo, Image* pimageTexture)
{
m_pgeoMesh = pgeo;
m_bFlip = (options == 0);
if (pimageTexture) {
m_psurfaceTexture = pimageTexture->GetSurface();
} else {
pimageTexture = m_pgeoMesh->FindTexture();
if (pimageTexture) {
m_psurfaceTexture = pimageTexture->GetSurface();
} else {
m_psurfaceTexture = NULL;
}
}
m_pgeoMesh->Update();
m_radius =
m_radiusCull =
m_radiusMesh = m_pgeoMesh->GetRadius(Matrix::GetIdentity());
return S_OK;
}
Vector TransformOffset(const Vector& vecArg)
{
Vector vec = vecArg;
if (m_bFlip) {
vec = Vector(-vec.X(), vec.Y(), -vec.Z());
}
vec = vec * m_orientation;
vec *= m_scale;
return vec;
}
Vector TransformOffsetNoScale(const Vector& vecArg)
{
Vector vec = vecArg;
if (m_bFlip) {
vec = Vector(-vec.X(), vec.Y(), -vec.Z());
}
return vec * m_orientation;
}
bool GetChildModelOffset(const ZString& str, Vector& vec)
{
if (m_pframes) {
FrameDataListValue::FrameList::Iterator iter(m_pframes->GetList());
while (!iter.End()) {
FrameData& data = iter.Value();
if (data.m_strName == str) {
vec = data.m_vecPosition;
vec *= m_scale;
return true;
}
iter.Next();
}
}
return false;
}
bool GetChildOffset(const ZString& str, Vector& vec)
{
if (GetChildModelOffset(str, vec))
{
if (m_bFlip) {
vec = Vector(-vec.X(), vec.Y(), -vec.Z());
}
vec = vec * m_orientation;
return true;
}
return false;
}
float GetRadius()
{
return m_radius;
}
float GetRadius(const Matrix& mat)
{
return m_radius * mat.GetScale();
}
void SetVisible(bool bVisible)
{
m_bVisible = bVisible;
}
void SetVisibleShip(bool bVisible)
{
m_bVisibleShip = bVisible;
}
void UpdateTrailPosition()
{
if (m_pvectorPositionTrail) {
m_pvectorPositionTrail->SetValue(
m_vecPosition
+ TransformOffset(m_vecTrailOffset)
);
}
}
void SetPosition(const Vector& vec)
{
if (m_pbooleanMoving != NULL) {
m_pbooleanMoving->SetValue(m_vecPosition != vec);
}
if (m_vecPosition != vec) {
m_vecPosition = vec;
UpdateTrailPosition();
}
}
void SetOrientation(const Orientation& orient)
{
m_orientation = orient;
if (m_pvectorRightTrail) {
m_pvectorRightTrail->SetValue(orient.GetRight() * m_radius * 0.25);
}
UpdateTrailPosition();
}
float SetRadius(float radius)
{
m_scale = radius / m_radiusMesh;
if (m_radiusCull == m_radius) {
m_radiusCull = radius;
}
m_radius = radius;
UpdateTrailPosition();
return m_radius;
}
void SetTexture(Image* pimageTexture)
{
m_psurfaceTexture = pimageTexture->GetSurface();
}
float GetFlareCount(void) const
{
return m_countFlares;
}
class EventSink : public IEventSink {
public:
TRef<ThingGeoImpl> m_pthing;
EventSink(ThingGeoImpl* pthing) :
m_pthing(pthing)
{
}
bool OnEvent(IEventSource* pevent)
{
return m_pthing->OnEvent(pevent);
}
};
bool OnEvent(IEventSource* pevent)
{
ZAssert(m_countFlares > 0);
m_countFlares--;
if (m_countFlares == 0) {
m_radiusCull = m_radius;
}
return false;
}
void AddFlare(Geo* pgeo, Number* ptime, const Vector& vecDirection, const Vector* pvectorEllipse)
{
Vector vecLocal = m_orientation.TimesInverse(-vecDirection);
Orientation orient;
if (pvectorEllipse) {
Vector vector = vecLocal;
vector.x /= pvectorEllipse->x;
vector.y /= pvectorEllipse->y;
vector.z /= pvectorEllipse->z;
orient.Set(vector);
orient.Scale((*pvectorEllipse) / m_radius);
m_radiusCull = max(m_radiusCull, pvectorEllipse->X());
m_radiusCull = max(m_radiusCull, pvectorEllipse->Y());
m_radiusCull = max(m_radiusCull, pvectorEllipse->Z());
} else {
orient.Set(vecLocal);
}
Matrix mat(orient, Vector::GetZero(), m_radiusMesh);
TRef<TimedGeo> m_ptimedGeo =
new TimedGeo(
new TransformGeo(
CreateCopyModeGeo(
CreateBlendGeo(
pgeo,
BlendModeAdd
)
),
new MatrixTransform(mat)
),
ptime,
0.75f
);
m_pgroupFlares->AddGeo(m_ptimedGeo);
m_ptimedGeo->AddSink(new EventSink(this));
m_countFlares++;
}
void SetBitsGeo(BitsGeo* pbitsGeo)
{
m_pbitsGeo = pbitsGeo;
}
void AddHullHit(const Vector& vecPosition, const Vector& vecNormal)
{
m_phullHitGeo->AddHullHit(vecPosition, vecNormal);
float size = m_radius / 2;
float speed = 0.4f * size;
float spread = 0.25;
Vector vecWorldPosition = m_vecPosition + vecPosition;
for (int index = 0; index < 2; index++) {
m_pbitsGeo->AddBit(
vecWorldPosition,
speed * Vector(
vecNormal.X() + random(-spread, spread),
vecNormal.Y() + random(-spread, spread),
vecNormal.Z() + random(-spread, spread)
),
size
);
}
}
void SetShowDamage (bool bShowDamage)
{
m_bShowDamage = bShowDamage;
}
void AddDamage (const Vector& vecDamagePosition, float fDamageFraction)
{
int iDamageIndexOpen = int((1.0f - fDamageFraction) * MAX_DAMAGE_FRAMES) - 1;
int iDamageIndex = bound(iDamageIndexOpen, -1, MAX_DAMAGE_FRAMES - 1);
if (iDamageIndex != iDamageIndexOpen) {
s_trashCount++;
}
if (iDamageIndex > m_iLastDamageIndex)
{
Vector vecPosition = vecDamagePosition * 0.33f;
Vector vecFromDirection = vecPosition.Normalize ();
Vector vecForward = m_orientation.TimesInverse (vecFromDirection);
vecPosition = m_orientation.TimesInverse (vecPosition);
while (m_iLastDamageIndex <= iDamageIndex)
{
m_iLastDamageIndex++;
m_damageFrames[m_iLastDamageIndex].m_vecForward = vecForward;
m_damageFrames[m_iLastDamageIndex].m_vecPosition = vecPosition;
}
}
else
m_iLastDamageIndex = iDamageIndex;
}
void RemoveDamage (float fDamageFraction)
{
int iDamageIndexOpen = int((1.0f - fDamageFraction) * MAX_DAMAGE_FRAMES) - 1;
int iDamageIndex = bound(iDamageIndexOpen, -1, MAX_DAMAGE_FRAMES - 1);
if (iDamageIndex != iDamageIndexOpen) {
s_trashCount++;
}
if (iDamageIndex < m_iLastDamageIndex)
m_iLastDamageIndex = iDamageIndex;
}
void SetTimeUntilRipcord (float fTimeUntilTeleport)
{
m_fTimeUntilRipcord = fTimeUntilTeleport;
}
void SetTimeUntilAleph (float fTimeUntilTeleport)
{
m_fTimeUntilAleph = fTimeUntilTeleport;
}
void RenderGlow(Context* pcontext)
{
if ((m_scaleGlow > 0) && m_pframes)
{
FrameDataListValue::FrameList::Iterator iter(m_pframes->GetList());
while (!iter.End()) {
FrameData& data = iter.Value();
if (data.m_strName.Find("thrust") != -1) {
pcontext->DrawDecal(
m_pimageGlow->GetSurface(),
m_colorGlow,
m_vecPosition + TransformOffset(data.m_vecPosition),
Vector::GetZero(),
Vector::GetZero(),
m_scaleGlow * m_scale,
0
);
}
iter.Next();
}
}
}
class Callback : public IGeoCallback {
public:
TRef<ThingGeoImpl> m_pthing;
Callback(ThingGeoImpl* pthing) :
m_pthing(pthing)
{
}
void RenderCallback(Context* pcontext) {
m_pthing->RenderCallback(pcontext);
}
};
void RenderCallback(Context* pcontext)
{
#ifdef _DEBUG
pcontext->SetBlendMode(BlendModeAdd);
pcontext->SetShadeMode(ShadeModeGlobalColor);
if (s_bTransparentObjects)
{
pcontext->SetGlobalColor(Color(0.25f, 0.5f, 0.25f));
m_pgeoMesh->Render(pcontext);
}
else
{
if (m_pgeoBounds != NULL)
{
pcontext->SetGlobalColor(Color(0.5f, 0.25f, 0.25f));
pcontext->Rotate(Vector(0, 0, 1), pi);
m_pgeoBounds->Render(pcontext);
}
}
#endif
}
void RenderTargetGeo(Context* pcontext)
{
pcontext->Multiply(Matrix(m_orientation, Vector(0, 0, 0), 1));
pcontext->Scale3(m_scale);
if (m_pgroupFlares->GetList()->GetCount() != 0) {
pcontext->PushState();
m_pgroupFlares->Render(pcontext);
pcontext->PopState();
}
if (m_bFlip) {
pcontext->Rotate(Vector(0, 1, 0), pi);
}
if (m_bDrawLights && m_pgeoLights != NULL) {
m_pgeoLights->Render(pcontext);
}
bool bClipSave = pcontext->GetClipping();
pcontext->SetClipping(m_bClip);
if ((!pcontext->Has3DAcceleration()) && (!m_bShadeAlways)) {
pcontext->SetShadeMode(ShadeModeCopy);
}
bool bColorKey = pcontext->GetColorKey();
if (m_psurfaceTexture) {
pcontext->SetTexture(m_psurfaceTexture);
}
else
{
pcontext->SetBlendMode(BlendModeAdd);
pcontext->SetColorKey(false,true);
}
pcontext->DrawCallbackGeo(new Callback(this), false);
#ifdef _DEBUG
if (s_bShowBounds) {
pcontext->DrawCallbackGeo(new Callback(this), false);
if (s_bTransparentObjects) {
if (m_pgeoBounds) {
pcontext->Rotate(Vector(0, 0, 1), pi);
m_pgeoBounds->Render(pcontext);
}
} else {
m_pgeoMesh->Render(pcontext);
}
} else {
m_pgeoMesh->Render(pcontext);
}
#else
m_pgeoMesh->Render(pcontext);
#endif
pcontext->SetClipping(bClipSave);
pcontext->SetColorKey(bColorKey,true);
}
void RenderGeo(Context* pcontext)
{
pcontext->Translate(m_vecPosition);
if (s_bShowHullHits) {
m_phullHitGeo->Render(pcontext);
}
RenderTargetGeo(pcontext);
}
void Render(Context* pcontext)
{
if (m_pgeoTrail) {
m_pgeoTrail->Update();
}
if (m_pgeoLights) {
m_pgeoLights->Update();
}
m_phullHitGeo->Update();
m_pgroupFlares->Update();
m_pgeoMesh->Update();
if (m_bVisible) {
if (s_bShowTrails && m_pgeoTrail != NULL) {
m_pgeoTrail->Render(pcontext);
}
if (m_bVisibleShip) {
bool bNoClipping;
if (!pcontext->IsCulled(m_vecPosition, m_radiusCull, bNoClipping)) {
m_bClip = !bNoClipping;
float screenRadius = pcontext->GetImageRadius(m_vecPosition, m_radius);
pcontext->SetLOD(screenRadius * s_lodBias);
m_bDrawLights = screenRadius > 32 && s_bShowLights;
RenderGlow(pcontext);
RenderGeo(pcontext);
}
}
}
}
void Evaluate()
{
m_ptime->Update();
m_time = m_ptime->GetValue();
AddFireAndSmoke ();
}
ZString GetFunctionName() { return "ThingGeo"; }
};
float ThingGeo::s_lodBias = 1;
bool ThingGeo::s_bShowLights = true;
bool ThingGeo::s_bShowHullHits = true;
bool ThingGeo::s_bShowTrails = true;
bool ThingGeo::s_bShowBounds = false;
int ThingGeo::s_iShowSmoke = 1;
bool ThingGeo::s_bTransparentObjects = false;
int ThingGeo::s_crashCount = 0;
int ThingGeo::s_trashCount = 0;
void ThingGeo::SetTransparentObjects(bool bTransparentObjects)
{
s_bTransparentObjects = bTransparentObjects;
}
bool ThingGeo::GetTransparentObjects()
{
return s_bTransparentObjects;
}
void ThingGeo::SetShowTrails(bool bShowTrails)
{
s_bShowTrails = bShowTrails;
}
void ThingGeo::SetShowBounds(bool bShowBounds)
{
s_bShowBounds = bShowBounds;
}
int ThingGeo::GetShowSmoke()
{
return s_iShowSmoke;
}
void ThingGeo::SetShowSmoke(int iShowSmoke)
{
s_iShowSmoke = iShowSmoke;
}
bool ThingGeo::GetShowTrails()
{
return s_bShowTrails;
}
bool ThingGeo::GetShowBounds()
{
return s_bShowBounds;
}
void ThingGeo::SetShowLights(bool bShowLights)
{
s_bShowLights = bShowLights;
}
bool ThingGeo::GetShowLights()
{
return s_bShowLights;
}
void ThingGeo::SetShowHullHits(bool bShowHullHits)
{
s_bShowHullHits = bShowHullHits;
}
bool ThingGeo::GetShowHullHits()
{
return s_bShowHullHits;
}
void ThingGeo::SetLODBias(float lodBias)
{
ZAssert(lodBias >=0 && lodBias <=1);
ThingGeo::s_lodBias = lodBias;
}
int ThingGeo::GetCrashCount()
{
return ThingGeo::s_crashCount;
}
int ThingGeo::GetTrashCount()
{
return ThingGeo::s_trashCount;
}
TRef<ThingGeo> ThingGeo::Create(Modeler* pmodeler, Number* ptime)
{
return new ThingGeoImpl(pmodeler, ptime);
}