#include "pch.h"
const float maxDistance = 100;
class DebrisGeo : public Geo {
private:
class DebrisData {
public:
Vector m_position;
Color m_color;
float m_startTime;
};
typedef TList<DebrisData> DebrisDataList;
DebrisDataList m_listData;
TRef<Surface> m_psurface;
float m_time;
Vector m_positionLast;
Number* GetTime() { return Number::Cast(GetChild(0)); }
Viewport* GetViewport() { return Viewport::Cast(GetChild(1)); }
Camera* GetCamera() { return GetViewport()->GetCamera(); }
RectValue* GetViewRect() { return GetViewport()->GetViewRect(); }
public:
DebrisGeo(Modeler* pmodeler, Number* ptime, Viewport* pviewport) :
Geo(ptime, pviewport)
{
m_positionLast = GetCamera()->GetPosition();
m_psurface = pmodeler->LoadSurface("debris1bmp", true);
}
void Evaluate()
{
const Rect& rect = GetViewRect()->GetValue();
Camera* pcamera = GetCamera();
float focus = pcamera->GetFocus();
float rfocus = 1.0f / focus;
const Vector& position = pcamera->GetPosition();
Orientation orient = pcamera->GetOrientation();
Vector forward = orient.GetForward();
Vector up = orient.GetUp();
Vector right = orient.GetRight();
m_time = GetTime()->GetValue();
float length = (m_positionLast - position).Length();
if (length > 100) {
m_listData.SetEmpty();
length = 0;
} else {
DebrisDataList::Iterator iter(m_listData);
while (!iter.End()) {
DebrisData& data = iter.Value();
float time = m_time - data.m_startTime;
float bright = 1.0f - 0.25f * time * time;
float distance = (data.m_position - position).LengthSquared();
if (bright <= 0 || distance > 250000) {
iter.Remove();
} else {
iter.Next();
}
}
while (length > 0) {
m_listData.PushFront();
DebrisData& data = m_listData.GetFront();
float angle = random(0, 2 * pi);
float distance = maxDistance * sqrt(random(0, 1));
float radius = rfocus * distance * sqrt(random(0, 1));
float c = radius * cos(angle);
float s = radius * sin(angle);
data.m_position =
position
+ distance * forward
+ c * right
+ s * up;
data.m_color =
Color(
random(0.50f, 0.75f),
random(0.75f, 1.00f),
random(0.75f, 1.00f)
);
data.m_startTime = m_time;
length -= 1.0f;
}
}
m_positionLast = position;
}
void Render(Context* pcontext)
{
const Vector& position = GetCamera()->GetPosition();
float scale = 0.1f / GetCamera()->GetFocus();
DebrisDataList::Iterator iter(m_listData);
while (!iter.End()) {
DebrisData& data = iter.Value();
float time = m_time - data.m_startTime;
float bright = 1.0f - 0.25f * time * time;
float distance = (data.m_position - position).LengthSquared();
if (distance > 400) {
pcontext->DrawDecal(
m_psurface,
bright * data.m_color,
data.m_position,
Vector::GetZero(),
Vector::GetZero(),
scale,
0
);
}
iter.Next();
}
}
ZString GetFunctionName() { return "DebrisGeo"; }
};
TRef<Geo> CreateDebrisGeo(Modeler* pmodeler, Number* ptime, Viewport* pviewport)
{
return new DebrisGeo(pmodeler, ptime, pviewport);
}