#include "pch.h"
class MineFieldGeoImpl : public MineFieldGeo {
private:
TRef<Surface> m_psurface;
TVector<Vector> m_vposition;
float m_radius;
static int GetCount(float strength, float radius)
{
return 1 + int(strength * radius * radius / 100.0f);
}
public:
MineFieldGeoImpl(Surface* psurface, float strength, float radius) :
m_psurface(psurface),
m_vposition(GetCount(strength, radius)),
m_radius(radius)
{
for (int index = 0; index < m_vposition.GetCount(); index++) {
m_vposition.Set(index, Vector::RandomPosition(radius));
}
}
void SetStrength(float strength)
{
int count = GetCount(strength, m_radius);
ZAssert(count <= m_vposition.GetCount());
m_vposition.SetCount(count);
Changed();
}
float GetRadius(const Matrix& mat)
{
return m_radius;
}
void Render(Context* pcontext)
{
Vector vecEye = pcontext->TransformLocalToEye(Vector::GetZero());
float bright = bound(100.0f / vecEye.Length(), 0.0f, 1.0f);
if (bright > 0) {
Color color(bright, bright, bright);
int count = m_vposition.GetCount();
for (int index = 0; index < count; index++) {
pcontext->DrawDecal(
m_psurface,
color,
m_vposition[index],
Vector::GetZero(),
Vector::GetZero(),
1,
0,
BlendModeSource
);
}
}
}
};
TRef<MineFieldGeo> CreateMineFieldGeo(Surface* psurface, float strength, float radius)
{
return new MineFieldGeoImpl(psurface, strength, radius);
}
const float c_ratio = 1.1f;
class BuildEffectGeoImpl :
public BuildEffectGeo,
public IGeoCallback
{
private:
Color m_colorInner;
Color m_colorOuter;
Color m_colorComplement;
TRef<Geo> m_psphere;
float m_time;
float m_radius;
float m_fOuter;
Vector m_position;
public:
BuildEffectGeoImpl(
Modeler* pmodeler,
Number* ptime,
const Color& color
) :
BuildEffectGeo(ptime),
m_colorOuter(color)
{
float h;
float s;
float b;
m_colorOuter.GetHSB(h, s, b);
m_colorComplement.SetHSBA(h + 0.5f, s, b);
m_psphere = pmodeler->LoadGeo("build");
}
float GetRadius()
{
return m_radius;
}
void SetRadius(float f)
{
m_radius = f;
}
const Vector& GetPosition()
{
return m_position;
}
void SetPosition(const Vector& p)
{
m_position = p;
}
void SetColors(float aInner, float fInner, float fOuter)
{
m_colorInner = m_colorComplement * fInner;
m_colorInner.SetAlpha(aInner);
m_fOuter = fOuter;
}
void Evaluate()
{
m_time = GetTime()->GetValue();
}
void Render(Context* pcontext)
{
pcontext->DrawCallbackGeo(this, true);
}
void RenderCallback(Context* pcontext)
{
pcontext->SetBlendMode(BlendModeSourceAlpha);
pcontext->SetShadeMode(ShadeModeGlobalColor);
pcontext->Translate(m_position);
pcontext->Scale3(m_radius);
pcontext->PushState();
pcontext->Scale3(1.0f / c_ratio);
pcontext->Rotate(Vector(0, 0, 1), m_time / pi);
pcontext->Rotate(Vector(0, 1, 0), 0.5f * pi * m_time);
pcontext->SetGlobalColor(m_colorInner);
m_psphere->Render(pcontext);
pcontext->PopState();
pcontext->Rotate(Vector(1, 0, 0), m_time);
pcontext->Rotate(Vector(0, 1, 0), pi * m_time);
pcontext->SetBlendMode(BlendModeAdd);
pcontext->SetGlobalColor(m_colorOuter * m_fOuter);
m_psphere->Render(pcontext);
}
};
TRef<BuildEffectGeo> CreateBuildEffectGeo(
Modeler* pmodeler,
Number* ptime,
const Color& color
) {
return
new BuildEffectGeoImpl(
pmodeler,
ptime,
color
);
}
class WireSphereGeo : public Geo {
private:
TVector<VertexL> m_vertices;
TVector<MeshIndex> m_indices;
static const int vSegments;
public:
WireSphereGeo(float minDot, int hSegments) :
m_vertices(
hSegments * (vSegments - 1),
hSegments * (vSegments - 1)
),
m_indices(
2 * hSegments * (vSegments * 2 - 3),
2 * hSegments * (vSegments * 2 - 3)
)
{
int vIndex;
int hIndex;
for (vIndex = 0; vIndex < vSegments - 1; vIndex++)
{
float vSin = minDot - float(vIndex) * 0.05f;
float vCos = float(sqrt(1.0f - vSin * vSin));
for (hIndex = 0; hIndex < hSegments; hIndex++)
{
int index = vIndex * hSegments + hIndex;
float hAngle = (float)hIndex * 2.0f * pi / (float)hSegments;
float x = cos(hAngle) * vCos;
float y = sin(hAngle) * vCos;
float z = -vSin;
static const float intensity = 1.0f;
m_vertices.Set(
index,
VertexL(
1000 * x,
1000 * y,
1000 * z,
intensity,
intensity,
intensity,
1,
0,
0
)
);
}
}
int index = 0;
for (vIndex = 0; vIndex < vSegments - 2; vIndex++) {
for (hIndex = 0; hIndex < hSegments; hIndex++) {
m_indices.Set(index , vIndex * hSegments + hIndex);
m_indices.Set(index + 1, (vIndex + 1) * hSegments + hIndex);
ZAssert(m_indices[index ] >= 0 && m_indices[index ] < m_vertices.GetCount());
ZAssert(m_indices[index + 1] >= 0 && m_indices[index + 1] < m_vertices.GetCount());
index += 2;
}
}
for (vIndex = 0; vIndex < vSegments - 1; vIndex++) {
for (hIndex = 0; hIndex < hSegments; hIndex++) {
m_indices.Set(index, vIndex * hSegments + hIndex);
m_indices.Set(index + 1,
hIndex == hSegments - 1 ?
vIndex * hSegments
: vIndex * hSegments + hIndex + 1
);
ZAssert(m_indices[index ] >= 0 && m_indices[index ] < m_vertices.GetCount());
ZAssert(m_indices[index + 1] >= 0 && m_indices[index + 1] < m_vertices.GetCount());
index += 2;
}
}
ZAssert(index == m_indices.GetCount());
}
void Render(Context* pcontext)
{
pcontext->DrawLines(m_vertices, m_indices);
}
ZString GetFunctionName() { return "WireSphereGeo"; }
};
const int WireSphereGeo::vSegments = 3;
TRef<Geo> CreateWireSphereGeo(float minDot, int hSegments)
{
return new WireSphereGeo(minDot, hSegments);
}
class PulseGeoImpl :
public PulseGeo,
public IGeoCallback
{
protected:
class WhiteSurfaceSite : public SurfaceSite {
public:
void UpdateSurface(Surface* psurface)
{
psurface->FillSurface(Color::White());
}
};
struct PulseData : public IObject
{
float m_fRadius;
Vector m_vecPosition;
Color m_color;
float m_fStartTime;
float m_fExplodeTime;
PulseData (float fStartTime, float fExplodeTime, float fRadius, const Vector& vecPosition, const Color& color)
{
m_fRadius = fRadius;
m_vecPosition = vecPosition;
m_color = color;
m_fStartTime = fStartTime;
m_fExplodeTime = fExplodeTime;
}
};
TList<TRef<PulseData> > m_pulseData;
float m_fTime;
TRef<Geo> m_pSphere;
TRef<Surface> m_pTexture;
public:
PulseGeoImpl (Modeler* pmodeler, Number* ptime) : PulseGeo (ptime)
{
m_pSphere = pmodeler->LoadGeo("aleph_sphere");
m_pTexture = pmodeler->GetEngine ()->CreateSurface (WinPoint (16, 16), SurfaceType2D (), new WhiteSurfaceSite());
}
void AddPulse (float fExplodeTime, float fRadius, const Vector& vecPosition, const Color& color)
{
float fStartTime = GetTime()->GetValue();
m_pulseData.PushFront (new PulseData (fStartTime, fExplodeTime, (fRadius / m_pSphere->GetRadius (Matrix::GetIdentity ())) * 0.5f, vecPosition, color));
}
void Evaluate()
{
m_fTime = GetTime()->GetValue();
}
void Render(Context* pcontext)
{
pcontext->DrawCallbackGeo(this, true);
}
void RenderCallback(Context* pcontext)
{
TList<TRef<PulseData> >::Iterator iter (m_pulseData);
pcontext->SetBlendMode (BlendModeAdd);
pcontext->SetShadeMode (ShadeModeGlobalColor);
pcontext->SetTexture (m_pTexture);
while (! iter.End ())
{
TRef<PulseData> pGeo = iter.Value ();
float fTimeUntilExplosion = pGeo->m_fExplodeTime - (m_fTime - pGeo->m_fStartTime);
float fEffectDuration = 0.5f;
if (fTimeUntilExplosion <= fEffectDuration)
{
if (fTimeUntilExplosion <= -fEffectDuration)
{
iter.Remove ();
}
else
{
float fEffectScale = 1.0f - fabsf (fTimeUntilExplosion / fEffectDuration);
assert (fEffectScale <= 1.0f);
fEffectScale *= fEffectScale;
pcontext->Translate(pGeo->m_vecPosition);
pcontext->Scale3(pGeo->m_fRadius * fEffectScale);
pcontext->PushState ();
pcontext->SetGlobalColor (Color (pGeo->m_color.R () * fEffectScale, pGeo->m_color.G () * fEffectScale, pGeo->m_color.B () * fEffectScale, fEffectScale));
m_pSphere->Render (pcontext);
pcontext->PopState ();
iter.Next ();
}
}
else
{
iter.Next ();
}
}
}
};
TRef<PulseGeo> CreatePulseGeo(Modeler* pmodeler, Number* ptime)
{
return new PulseGeoImpl (pmodeler, ptime);
}
const int maxSections = 16;
const float timeDelta = 0.5f;
class TrailGeo : public Geo, public IGeoCallback {
private:
TVector<VertexL> m_vertices;
TVector<MeshIndex> m_indices;
int m_section;
float m_timeLast;
Color m_color;
TRef<Image> m_pimage;
Vector m_vecRightLast;
Number* GetTime() { return Number::Cast(GetChild(0)); }
VectorValue* GetPosition() { return VectorValue::Cast(GetChild(1)); }
VectorValue* GetRight() { return VectorValue::Cast(GetChild(2)); }
Boolean* GetMoving() { return Boolean::Cast(GetChild(3)); }
public:
TrailGeo(
Modeler* pmodeler,
const Color& color,
VectorValue* pvectorPosition,
VectorValue* pvectorRight,
Boolean* pbooleanMoving,
Number* ptime
) :
Geo(ptime, pvectorPosition, pvectorRight, pbooleanMoving),
m_color(color),
m_vecRightLast(pvectorRight->GetValue()),
m_vertices((maxSections + 1) * 2, (maxSections + 1) * 2),
m_indices(maxSections * 6, maxSections * 6),
m_section(0),
m_timeLast(-1)
{
m_pimage = pmodeler->LoadImage(AWF_EFFECT_TRAIL, true);
int index;
for(index = 0; index < maxSections; index ++) {
int indexVertex = index * 2;
int indexIndex = index * 6;
m_indices.Set(indexIndex + 0, indexVertex + 0);
m_indices.Set(indexIndex + 1, indexVertex + 2);
m_indices.Set(indexIndex + 2, indexVertex + 3);
m_indices.Set(indexIndex + 3, indexVertex + 0);
m_indices.Set(indexIndex + 4, indexVertex + 3);
m_indices.Set(indexIndex + 5, indexVertex + 1);
}
int count = m_vertices.GetCount();
for(index = 0; index < count; index += 2) {
m_vertices.Get(index ).SetTextureCoordinate(Point(0, 0));
m_vertices.Get(index + 1).SetTextureCoordinate(Point(1, 0));
}
}
void UpdateColors(float time)
{
if (m_section != 0) {
float dtime = (time - m_timeLast) / (timeDelta * maxSections);
float bright = 1 - dtime;
float dbright = 1.0f / maxSections;
for (int index = 0; index <= m_section; index++) {
Color color = m_color * bright;
bright -= dbright;
if (bright < 0) {
bright = 0;
}
m_vertices.Get(index * 2 ).SetColor(color);
m_vertices.Get(index * 2 + 1).SetColor(color);
}
}
}
void Evaluate()
{
float time = GetTime()->GetValue();
const Vector& vecPos = GetPosition()->GetValue();
const Vector& vecRight = GetRight()->GetValue();
bool bMoving = GetMoving()->GetValue();
if (vecPos != Vector(0, 0, 0)) {
if (m_timeLast == -1) {
m_timeLast = time;
} else {
if (time - m_timeLast > timeDelta) {
m_timeLast = time;
if (m_section < maxSections) {
m_section++;
}
for(int index = m_section * 2 + 1; index > 1; index--) {
m_vertices.Set(index, m_vertices[index - 2]);
}
}
}
if (bMoving) {
m_vecRightLast = vecRight;
}
m_vertices.Get(0).SetPosition(vecPos - m_vecRightLast);
m_vertices.Get(1).SetPosition(vecPos + m_vecRightLast);
UpdateColors(time);
}
}
void Render(Context* pcontext)
{
pcontext->DrawCallbackGeo(this, true);
}
void RenderCallback(Context* pcontext)
{
pcontext->SetMaterial(NULL);
pcontext->SetShadeMode(ShadeModeGouraud);
pcontext->SetCullMode(CullModeNone);
pcontext->SetBlendMode(BlendModeAdd);
pcontext->SetZWrite(false);
pcontext->SetTexture(m_pimage->GetSurface());
if (m_section != 0) {
pcontext->DrawTriangles(
&m_vertices[0],
(m_section + 1) * 2,
&m_indices[0],
m_section * 6
);
}
}
};
TRef<Geo> CreateTrailGeo(
Modeler* pmodeler,
const Color& color,
VectorValue* pvectorPosition,
VectorValue* pvectorRight,
Boolean* pbooleanMoving,
Number* ptime
) {
return new TrailGeo(pmodeler, color, pvectorPosition, pvectorRight, pbooleanMoving, ptime);
}
class HullHitGeoImpl : public HullHitGeo {
private:
class HullHitData {
public:
Vector m_position;
Vector m_normal;
Vector m_forward;
Vector m_right;
float m_timeStart;
};
typedef TList<HullHitData, DefaultNoEquals> HullHitDataList;
HullHitDataList m_listHullHitData;
TRef<Surface> m_psurfaceNormal;
TRef<Surface> m_psurfaceTangent;
Number* GetTime() { return Number::Cast(GetChild(0)); }
public:
HullHitGeoImpl(Modeler* pmodeler, Number* ptime) :
HullHitGeo(ptime)
{
m_psurfaceNormal = pmodeler->LoadSurface(AWF_EFFECT_NORMAL, true);
m_psurfaceTangent = pmodeler->LoadSurface(AWF_EFFECT_TANGENT, true);
}
void AddHullHit(const Vector& vecPosition, const Vector& vecNormal)
{
m_listHullHitData.PushFront();
HullHitData& data = m_listHullHitData.GetFront();
data.m_position = vecPosition;
data.m_normal = vecNormal;
if (vecNormal == Vector(0, 0, 1)) {
data.m_forward = CrossProduct(vecNormal, Vector(0, 1, 0)).Normalize();
} else {
data.m_forward = CrossProduct(vecNormal, Vector(0, 0, 1)).Normalize();
}
data.m_right = CrossProduct(data.m_forward, vecNormal);
data.m_timeStart = GetTime()->GetValue();
}
void Render(Context* pcontext)
{
float timeCurrent = GetTime()->GetValue();
HullHitDataList::Iterator iter(m_listHullHitData);
while (!iter.End()) {
HullHitData& data = iter.Value();
Color color(1, 1, 1);
float maxSize = 2.0f;
float scale = 0.1f + maxSize * 5.0f * (timeCurrent - data.m_timeStart);
if (scale > maxSize) {
scale = maxSize;
}
pcontext->DrawDecal(
m_psurfaceTangent,
color,
data.m_position,
scale * data.m_forward,
scale * data.m_right,
1,
0
);
pcontext->DrawDecal(
m_psurfaceNormal,
color,
data.m_position + scale * data.m_normal,
scale * data.m_normal,
Vector(0, 0, 0),
1,
0
);
if (scale == maxSize) {
iter.Remove();
} else {
iter.Next();
}
}
}
};
TRef<HullHitGeo> CreateHullHitGeo(Modeler* pmodeler, Number* ptime)
{
return new HullHitGeoImpl(pmodeler, ptime);
}
const int g_maxBits = 32;
const int g_bitsLifeTime = 2;
class BitsGeoImpl : public BitsGeo {
private:
typedef TRange<g_maxBits> BitsRange;
class BitsData {
public:
Vector m_vecPosition;
Vector m_dvecPosition;
Vector m_axis;
float m_speed;
float m_time;
float m_size;
};
BitsData m_data[g_maxBits];
TRef<Geo> m_pgeo;
BitsRange m_indexFirst;
BitsRange m_indexLast;
float m_time;
Number* GetTime() { return Number::Cast(GetChild(0)); }
public:
BitsGeoImpl(Modeler* pmodeler, Number* ptime) :
BitsGeo(ptime),
m_indexFirst(0),
m_indexLast(0),
m_time(ptime->GetValue())
{
m_pgeo = pmodeler->LoadGeo(AWF_EFFECT_BIT);
}
void AddBit(
const Vector& vecPosition,
const Vector& dvecPosition,
float size
) {
--m_indexFirst;
if (m_indexFirst == m_indexLast) {
--m_indexLast;
}
m_data[m_indexFirst].m_time = m_time;
m_data[m_indexFirst].m_vecPosition = vecPosition;
m_data[m_indexFirst].m_dvecPosition = dvecPosition;
m_data[m_indexFirst].m_axis = Vector::RandomDirection();
m_data[m_indexFirst].m_speed = random(pi /2, 2 * pi);
m_data[m_indexFirst].m_size = size;
}
void Evaluate()
{
m_time = GetTime()->GetValue();
}
void Render(Context* pcontext)
{
if (m_indexFirst != m_indexLast) {
for(BitsRange index = m_indexFirst; index != m_indexLast; ++index) {
BitsData& data = m_data[index];
float dtime = m_time - data.m_time;
if (dtime > g_bitsLifeTime) {
m_indexLast = index;
break;
}
Vector position = data.m_vecPosition + data.m_dvecPosition * dtime;
pcontext->PushState();
pcontext->Translate(position);
pcontext->Scale3(data.m_size);
pcontext->Rotate(data.m_axis, data.m_speed * dtime);
m_pgeo->Render(pcontext);
pcontext->PopState();
}
}
}
};
TRef<BitsGeo> CreateBitsGeo(Modeler* pmodeler, Number* ptime)
{
return new BitsGeoImpl(pmodeler, ptime);
}
class BoltGeo : public Geo, IGeoCallback {
private:
TRef<Surface> m_psurface;
float m_size;
VectorValue* GetStart() { return VectorValue::Cast(GetChild(0)); }
VectorValue* GetEnd() { return VectorValue::Cast(GetChild(1)); }
public:
BoltGeo(VectorValue* pstart, VectorValue* pend, float size, Surface* psurface) :
Geo(pstart, pend),
m_size(size),
m_psurface(psurface)
{
}
void Render(Context* pcontext)
{
pcontext->DrawCallbackGeo(this, false);
}
void RenderCallback(Context* pcontext)
{
int index;
Vector vecStart = GetStart()->GetValue();
Vector vecEnd = GetEnd()->GetValue();
Vector vecForward = vecEnd - vecStart;
Vector vecUp = vecForward.GetOrthogonalVector().Normalize();
Vector vecRight = CrossProduct(vecForward, vecUp).Normalize();
float length = vecForward.Length();
float size = m_size * length;
float lengthImage =
max(
pcontext->GetImageRadius(vecStart, size),
pcontext->GetImageRadius(vecEnd, size)
);
int lcount;
if (lengthImage > 128) {
lcount = 6;
} else if (lengthImage > 64) {
lcount = 5;
} else if (lengthImage > 32) {
lcount = 4;
} else {
lcount = 3;
}
float bright = min(1.0f, lengthImage / 128);
Color color(bright, bright, bright);
int vcount = (1 << lcount) + 1;
VertexL* pvertex = pcontext->GetVertexLBuffer(vcount);
for (index = 0; index < vcount; index++) {
pvertex[index].SetColor(color);
pvertex[index].SetTextureCoordinate(
Point(
(float)index / (float)(vcount - 1),
0
)
);
}
pvertex[0].SetPosition(vecStart);
pvertex[vcount - 1].SetPosition(vecEnd);
for (int level = lcount; level > 0 ; level--) {
int dbig = 1 << level;
int dsmall = dbig / 2;
for (index = dsmall; index < vcount; index += dbig) {
float angle = random(0, 2 * pi);
VertexL& vecFirst = pvertex[index - dsmall];
VertexL& vecLast = pvertex[index + dsmall];
Vector vecMiddle = 0.5f * (vecFirst.GetPosition() + vecLast.GetPosition());
Vector vecRandom = size * (cos(angle) * vecUp + sin(angle) * vecRight);
pvertex[index].SetPosition(vecMiddle + vecRandom);
}
size = size / 2;
}
pcontext->SetTexture(m_psurface);
pcontext->SetLineWidth(4);
pcontext->SetBlendMode(BlendModeAdd);
pcontext->DrawLineStrip(pvertex, vcount);
}
};
TRef<Geo> CreateBoltGeo(VectorValue* pstart, VectorValue* pend, float size, Surface* psurface)
{
return new BoltGeo(pstart, pend, size, psurface);
}
class CullGeo : public WrapGeo {
private:
float m_radius;
public:
CullGeo(Geo* pgeo) :
WrapGeo(pgeo)
{
m_radius = GetGeo()->GetRadius(Matrix::GetIdentity());
}
void Render(Context* pcontext)
{
bool bNoClipping;
if (!pcontext->IsCulled(Vector::GetZero(), m_radius, bNoClipping)) {
if (bNoClipping) {
pcontext->SetClipping(false);
}
GetGeo()->Render(pcontext);
if (bNoClipping) {
pcontext->SetClipping(true);
}
}
}
};
TRef<Geo> CreateCullGeo(Geo* pgeo)
{
return new CullGeo(pgeo);
}
class CopyModeGeo : public WrapGeo {
public:
CopyModeGeo(Geo* pgeo) :
WrapGeo(pgeo)
{
}
void Render(Context* pcontext)
{
#ifdef FixPermedia
pcontext->SetShadeMode(ShadeModeGlobalColor);
pcontext->SetGlobalColor(Color::White());
#else
pcontext->SetShadeMode(ShadeModeCopy);
#endif
GetGeo()->Render(pcontext);
}
};
TRef<Geo> CreateCopyModeGeo(Geo* pgeo)
{
return new CopyModeGeo(pgeo);
}