diff --git a/Engine/BasicTypes.h b/Engine/BasicTypes.h index cd87db912025e47f05f5668d597c66cc0e9050ed..1916c8157a62599fdbd147d1cbc3d32be47e3ff8 100644 --- a/Engine/BasicTypes.h +++ b/Engine/BasicTypes.h @@ -149,7 +149,9 @@ struct Vector friend Vector operator*(Vector obj, const float& obj2) { obj *= obj2; return obj; } friend Vector operator*(const float& obj, Vector obj2) { obj2 *= obj; return obj2; } - friend Vector operator/(const Vector& obj, const Vector& obj2) { return Vector(obj2.X / obj.X, obj2.Y / obj.Y, obj2.Z / obj.Z); } + friend Vector operator/(const Vector& obj, const Vector& obj2) { return Vector(obj.X / obj2.X, obj.Y / obj2.Y, obj.Z / obj2.Z); } + friend Vector operator/(const Vector& obj, const int obj2) { return Vector(obj.X / obj2, obj.Y / obj2, obj.Z / obj2); } + friend Vector operator/(const Vector& obj, const float obj2) { return Vector(obj.X / obj2, obj.Y / obj2, obj.Z / obj2); } friend bool operator==(const Vector& obj, const Vector& obj2) { return obj2.X == obj.X && obj2.Y == obj.Y && obj2.Z == obj.Z; } diff --git a/Engine/Core.h b/Engine/Core.h index 3f7d4f2274cce868dbc77066d9cb2cbca97162ec..14d0ad3b3d766cc0420e7e750c776553de600218 100644 --- a/Engine/Core.h +++ b/Engine/Core.h @@ -18,47 +18,48 @@ void Exit(); template <class T> class Ref : public RefHold { +protected: mutable T* Pointer; mutable Data* DataPtr; public: Ref() { Pointer = nullptr; DataPtr = nullptr; } Ref(T* ptr) : Pointer(ptr) { - DataPtr = dynamic_cast<Data*>(ptr); - if (DataPtr == nullptr) { - Pointer = nullptr; + this->DataPtr = dynamic_cast<Data*>(ptr); + if (this->DataPtr == nullptr) { + this->Pointer = nullptr; return; } ObjectManager::AddRef(GetRecord(), this); } - ~Ref() { + virtual ~Ref() { if(Pointer != nullptr) ObjectManager::RemoveRef(GetRecord(), this); NullThis(); } virtual const void NullThis() const { - Pointer = nullptr; - DataPtr = nullptr; + this->Pointer = nullptr; + this->DataPtr = nullptr; } virtual const RecordInt GetRecord() const override { - return DataPtr ? DataPtr->GetRecord() : RecordInt(0); + return this->DataPtr ? this->DataPtr->GetRecord() : RecordInt(0); } T* GetPointer() { return Pointer; } Ref(const Ref& old) { ObjectManager::AddRef(old->GetRecord(), this); - if (DataPtr != nullptr) ObjectManager::RemoveRef(GetRecord(), this); - Pointer = old.Pointer; - DataPtr = old.DataPtr; + if (this->DataPtr != nullptr) ObjectManager::RemoveRef(this->GetRecord(), this); + this->Pointer = old.Pointer; + this->DataPtr = old.DataPtr; } Ref& operator=(const Ref& old) { ObjectManager::AddRef(old->GetRecord(), this); - if (DataPtr != nullptr) ObjectManager::RemoveRef(GetRecord(), this); - Pointer = old.Pointer; - DataPtr = old.DataPtr; + if (this->DataPtr != nullptr) ObjectManager::RemoveRef(this->GetRecord(), this); + this->Pointer = old.Pointer; + this->DataPtr = old.DataPtr; return *this; } @@ -71,6 +72,31 @@ public: bool operator!=(const void* other) { return Pointer != other; } }; +template <class T> +class RefWeak : public Ref<T> +{ + const bool bWeak = true; + +public: + RefWeak(T* ptr) : Ref<T>(ptr) {} + RefWeak() : Ref<T>() {} + + RefWeak(const RefWeak& old) { + ObjectManager::AddRef(old->GetRecord(), this); + if (this->DataPtr != nullptr) ObjectManager::RemoveRef(this->GetRecord(), this); + this->Pointer = old.Pointer; + this->DataPtr = old.DataPtr; + } + + RefWeak& operator=(const RefWeak& old) { + ObjectManager::AddRef(old->GetRecord(), this); + if (this->DataPtr != nullptr) ObjectManager::RemoveRef(this->GetRecord(), this); + this->Pointer = old.Pointer; + this->DataPtr = old.DataPtr; + return *this; + } +}; + template <class T = BaseObject> T* SpawnObject() { diff --git a/Engine/GameLoop.cpp b/Engine/GameLoop.cpp index 26e385446bb8a54281446ecfe5457c015c28f9cd..28b62f8cabe90c161f67e8d3cd4136559b4a2d0e 100644 --- a/Engine/GameLoop.cpp +++ b/Engine/GameLoop.cpp @@ -6,6 +6,7 @@ #include "GameLoop.h" #include "WinConsole.h" #include "GarbageCollector.h" +#include "Physics.h" #include "Timer.h" using namespace std; @@ -96,7 +97,10 @@ int GameLoop::MainLoop() if (found) continue; t->Tick(duration.count()); } + Timer::UpdateTimers(duration.count()); + Physics::CheckCollisions(); + RI->Render(duration.count()); std::unique_lock<std::mutex> lock(TickListMutex); diff --git a/Engine/Gameplay/PlayerController.cpp b/Engine/Gameplay/PlayerController.cpp index ecb2fd9e8bcc60ac1118d3debe208f463d16535a..464bc92a5af005f5481c60d9985e91d7989d3921 100644 --- a/Engine/Gameplay/PlayerController.cpp +++ b/Engine/Gameplay/PlayerController.cpp @@ -3,6 +3,7 @@ Player::Player() : Actor() { PlayerCamera = RI->CreateCamera(); + PlayerCamera->SetLocation(Vector(0.f, 0.f, 1.5f)); RI->SetActiveCamera(PlayerCamera); } diff --git a/Engine/IRender.h b/Engine/IRender.h index 28921c6e549748ef0ef6c16f41326ed15b96968c..194911d8a94cab63812586c127d030db1bb203d2 100644 --- a/Engine/IRender.h +++ b/Engine/IRender.h @@ -10,6 +10,7 @@ class RenderObject; class VisibleObject; class LoadedMesh; struct LightData; +struct Vertex; class Camera { @@ -27,6 +28,9 @@ public: virtual void SetFov(float) = 0; virtual void SetPerspective(bool perspective) = 0; + void SetParent(VisibleObject* p) { Parent = p; } +protected: + VisibleObject* Parent; }; class IRender @@ -84,6 +88,15 @@ protected: double MouseY; }; +struct AABB +{ + AABB() {} + AABB(float f) { mins = -f; maxs = f; } + AABB(Vector min, Vector max) { mins = min; maxs = max; } + Vector mins; + Vector maxs; +}; + class RenderMesh { public: @@ -92,6 +105,10 @@ public: virtual void SetMaterial(uint section, Material* nextMat) = 0; virtual Material* GetMaterial(uint section) const = 0; virtual void SetInstances(int count, Transformation* dispArray) = 0; + AABB GetAABB() const { return bounds; } + void SetAABB(AABB bounds) { this->bounds = bounds.maxs.Length(); } +protected: + AABB bounds; }; class IMesh @@ -99,6 +116,7 @@ class IMesh public: virtual ~IMesh() {} virtual RenderMesh* LoadData(VisibleObject* parent, String name) = 0; + virtual RenderMesh* CreateProcedural(VisibleObject* parent, String name, std::vector<Vector>& positions, std::vector<Vector> UV, std::vector<Vector>& normal, std::vector<Vector>& tangent, std::vector<uint32>& indices) = 0; virtual void StartLoading() = 0; protected: diff --git a/Engine/ObjectManager.cpp b/Engine/ObjectManager.cpp index 6548498e1dc13f2e4f0af10472b20307dbf0bb39..2895c80cda9ca61ae817f36678348a6ad4a77edf 100644 --- a/Engine/ObjectManager.cpp +++ b/Engine/ObjectManager.cpp @@ -9,6 +9,9 @@ Record::~Record() for (const auto& p : pointerRefs) { p->NullThis(); } + for (const auto& p : weakRefs) { + p->NullThis(); + } auto t = dynamic_cast<Tickable*>(object); if (t != nullptr) Loop->RemoveFromTick(t); delete object; diff --git a/Engine/ObjectManager.h b/Engine/ObjectManager.h index 7ecda6ded3821d09ffb14c8f16ed01dfb81d332a..3db23a9a1423731f7e00ca0f3a67d7ce53476b17 100644 --- a/Engine/ObjectManager.h +++ b/Engine/ObjectManager.h @@ -10,6 +10,7 @@ struct Record Record(Data* o, short p) : object(o), protection(p), checkCount(0) {} ~Record(); std::list<const RefHold*> pointerRefs; + std::list<const RefHold*> weakRefs; short protection; short checkCount; Data* object; @@ -24,11 +25,13 @@ public: } static void AddRef(const uint64 record, const RefHold* obj) { - ObjectRecords[record]->pointerRefs.push_back(obj); + if (obj->bWeak) ObjectRecords[record]->weakRefs.push_back(obj); + else ObjectRecords[record]->pointerRefs.push_back(obj); } static void RemoveRef(const uint64 record, const RefHold* obj) { - ObjectRecords.find(record)->second->pointerRefs.remove(obj); + if (obj->bWeak) ObjectRecords.find(record)->second->weakRefs.remove(obj); + else ObjectRecords.find(record)->second->pointerRefs.remove(obj); } static void CreateRecord(Data* object, short protection = 0) { diff --git a/Engine/Objects/BaseObject.h b/Engine/Objects/BaseObject.h index 30ab13d709895c2311407a08178d91080c02fe5a..dee9e7d66acc6583ccbb242e685ff8ea304c5247 100644 --- a/Engine/Objects/BaseObject.h +++ b/Engine/Objects/BaseObject.h @@ -10,6 +10,8 @@ public: virtual const void NullThis() const = 0; virtual const RecordInt GetRecord() const = 0; protected: + friend class ObjectManager; + const bool bWeak = false; }; class Data diff --git a/Engine/Objects/MovementComponent.cpp b/Engine/Objects/MovementComponent.cpp index 50c666361c34bae51c14ee3a14a5cd8581b969c7..142ff32cd4ff97f218abdc835a57c1220b561130 100644 --- a/Engine/Objects/MovementComponent.cpp +++ b/Engine/Objects/MovementComponent.cpp @@ -1,20 +1,31 @@ -#include "Objects/Actor.h" +#include "Physics.h" +#include <Objects/Terrain.h> #include "Objects/MovementComponent.h" MovementComponent::MovementComponent() { mass = 1.f; - in_acceleration = 100.f; + in_acceleration = 600.f; max_speed = 10.f; isPhysics = false; isGravity = true; + inAir = true; direction_count = 0; + force_count = 0; drag = 1.f; - brake = 1000.f; + brake = 2000.f; + air_control = 0.05f; + Physics::AddMovable(this); +} + +void MovementComponent::OnDestroyed() +{ + Physics::RemoveMovable(this); } void MovementComponent::Tick(float time) { + OldState = DesiredState; if (Object == nullptr) return; switch (isPhysics) { @@ -23,9 +34,9 @@ void MovementComponent::Tick(float time) const Vector gravity(0.f, 0.f, -9.8f); Vector delta; - if (isGravity) acceleration += gravity * time; - velocity += acceleration * time; - delta = velocity * time; + if (isGravity) DesiredState.acceleration += gravity * time; + DesiredState.velocity += DesiredState.acceleration * time; + delta = DesiredState.velocity * time; Object->AddLocation(delta); } break; @@ -37,25 +48,67 @@ void MovementComponent::Tick(float time) delta_a += directions[i]; } delta_a = delta_a.Normalize(); - delta_a *= in_acceleration; + delta_a *= in_acceleration * (inAir ? air_control : 1.f); + + for (int i = 0; i < force_count; i++) { + delta_a += forces[i].Direction; + } - const Vector drag_a = velocity.Normalize() * brake * time; + const Vector drag_a = Vector(DesiredState.velocity.X, DesiredState.velocity.Y, 0.f).Normalize() * brake * time; const Vector total_a = delta_a - drag_a; - acceleration = total_a; + DesiredState.acceleration = total_a; - if (isGravity) { + DesiredState.velocity += DesiredState.acceleration * time; + Vector temp = DesiredState.velocity; + temp.Z = 0.f; + if (temp.Length() > max_speed) temp = temp.Normalize() * max_speed; + else if (temp.Length() < 0.01f) temp = Vector(0.f); + DesiredState.velocity.X = temp.X; + DesiredState.velocity.Y = temp.Y; + + if (isGravity && inAir) { const Vector gravity_const(0.f, 0.f, -9.8f); - gravity += gravity_const * time; + DesiredState.velocity += gravity_const * time; } - velocity = velocity + acceleration * time; - if (velocity.Length() > max_speed) velocity = velocity.Normalize() * max_speed; - else if (velocity.Length() < 0.1f) velocity = Vector(0.f); - Object->AddLocation((velocity + gravity) * time); + DesiredState.location = Object->GetLocation() + (DesiredState.velocity) * time; + + if (Terra != nullptr) { + float height = Terra->GetHeight(DesiredState.location.X, DesiredState.location.Y); + + if (DesiredState.location.Z < height) { + DesiredState.location.Z = height; + if (DesiredState.velocity.Z < 0.f) DesiredState.velocity.Z = 0.f; + if (DesiredState.acceleration.Z < 0.f) DesiredState.acceleration.Z = 0.f; + inAir = false; + } + else { + if (DesiredState.location.Z > height) + inAir = true; + } + } } break; } direction_count = 0; + force_count = 0; + +} + +void MovementComponent::SetTarget(Actor* t) +{ + Object = t; + Physics::RemoveStatic(t); +} + +void MovementComponent::SetGround(Terrain* t) +{ + Terra = t; +} + +void MovementComponent::ApplyMovement() +{ + Object->SetLocation(DesiredState.location); } diff --git a/Engine/Objects/MovementComponent.h b/Engine/Objects/MovementComponent.h index f7a052cef4a4d274d4858f751d574deda687975a..5e6261c4b9d24e4fe359db69ff2b0230c085d368 100644 --- a/Engine/Objects/MovementComponent.h +++ b/Engine/Objects/MovementComponent.h @@ -1,7 +1,8 @@ #pragma once #include <Core.h> +#include <Objects/Actor.h> -class Actor; +class Terrain; struct Force { @@ -10,41 +11,62 @@ struct Force float duration; }; +struct State +{ + Vector location; + Vector rotation; + Vector velocity; + Vector angular_v; + Vector acceleration; + Vector angular_a; +}; + class MovementComponent : public BaseObject, public Tickable { public: MovementComponent(); + virtual void OnDestroyed() override; + virtual void BeginPlay() override {} virtual void Tick(float) override; - void SetTarget(Actor* t) { Object = t; } + void SetTarget(Actor* t); + void SetGround(Terrain* t); + Actor* GetTarget() const { return Object; } void SetMass(float m) { mass = m; } void SetMaxSpeed(float speed) { max_speed = speed; } void SetPhysics(bool p) { isPhysics = p; } void SetGravity(bool g) { isGravity = g; } + void ApplyMovement(); void AddInput(const Vector dir) { directions[direction_count++] = dir; } + void AddImpulse(const Force& f) { forces[force_count++] = f; } + void AddImpulse(const Vector& d) { Force f; f.Direction = d; forces[force_count++] = f; } + bool IsInAir() { return inAir; } + + State DesiredState; + State OldState; private: Ref<Actor> Object; - - Vector velocity; - Vector acceleration; - Vector gravity; + RefWeak<Terrain> Terra; bool isPhysics; bool isGravity; + bool inAir; float mass; float in_acceleration; float max_speed; float drag; float brake; + float air_control; Force forces[16]; Vector directions[16]; int direction_count; + int force_count; }; diff --git a/Engine/Objects/Terrain.cpp b/Engine/Objects/Terrain.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2124f7c1a0bedbb732949dfcdd4f42f8958d7783 --- /dev/null +++ b/Engine/Objects/Terrain.cpp @@ -0,0 +1,331 @@ +#include "Terrain.h" + +namespace Noise { + /* coherent noise function over 1, 2 or 3 dimensions */ + /* (copyright Ken Perlin) */ + + #include <stdlib.h> + #include <stdio.h> + #include <math.h> + + #define B 0x100 + #define BM 0xff + + #define N 0x1000 + #define NP 12 /* 2^N */ + #define NM 0xfff + + static int p[B + B + 2]; + static float g3[B + B + 2][3]; + static float g2[B + B + 2][2]; + static float g1[B + B + 2]; + static int start = 1; + + static void init(void); + + #define s_curve(t) ( t * t * (3. - 2. * t) ) + + #define lerp(t, a, b) ( a + t * (b - a) ) + + #define setup(i,b0,b1,r0,r1)\ + t = vec[i] + N;\ + b0 = ((int)t) & BM;\ + b1 = (b0+1) & BM;\ + r0 = t - (int)t;\ + r1 = r0 - 1.; + + double noise1(double arg) + { + int bx0, bx1; + float rx0, rx1, sx, t, u, v, vec[1]; + + vec[0] = arg; + if (start) { + start = 0; + init(); + } + + setup(0, bx0, bx1, rx0, rx1); + + sx = s_curve(rx0); + + u = rx0 * g1[p[bx0]]; + v = rx1 * g1[p[bx1]]; + + return lerp(sx, u, v); + } + + float noise2(float vec[2]) + { + int bx0, bx1, by0, by1, b00, b10, b01, b11; + float rx0, rx1, ry0, ry1, * q, sx, sy, a, b, t, u, v; + register int i, j; + + if (start) { + start = 0; + init(); + } + + setup(0, bx0, bx1, rx0, rx1); + setup(1, by0, by1, ry0, ry1); + + i = p[bx0]; + j = p[bx1]; + + b00 = p[i + by0]; + b10 = p[j + by0]; + b01 = p[i + by1]; + b11 = p[j + by1]; + + sx = s_curve(rx0); + sy = s_curve(ry0); + + #define at2(rx,ry) ( rx * q[0] + ry * q[1] ) + + q = g2[b00]; u = at2(rx0, ry0); + q = g2[b10]; v = at2(rx1, ry0); + a = lerp(sx, u, v); + + q = g2[b01]; u = at2(rx0, ry1); + q = g2[b11]; v = at2(rx1, ry1); + b = lerp(sx, u, v); + + return lerp(sy, a, b); + } + + float noise3(float vec[3]) + { + int bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11; + float rx0, rx1, ry0, ry1, rz0, rz1, * q, sy, sz, a, b, c, d, t, u, v; + register int i, j; + + if (start) { + start = 0; + init(); + } + + setup(0, bx0, bx1, rx0, rx1); + setup(1, by0, by1, ry0, ry1); + setup(2, bz0, bz1, rz0, rz1); + + i = p[bx0]; + j = p[bx1]; + + b00 = p[i + by0]; + b10 = p[j + by0]; + b01 = p[i + by1]; + b11 = p[j + by1]; + + t = s_curve(rx0); + sy = s_curve(ry0); + sz = s_curve(rz0); + + #define at3(rx,ry,rz) ( rx * q[0] + ry * q[1] + rz * q[2] ) + + q = g3[b00 + bz0]; u = at3(rx0, ry0, rz0); + q = g3[b10 + bz0]; v = at3(rx1, ry0, rz0); + a = lerp(t, u, v); + + q = g3[b01 + bz0]; u = at3(rx0, ry1, rz0); + q = g3[b11 + bz0]; v = at3(rx1, ry1, rz0); + b = lerp(t, u, v); + + c = lerp(sy, a, b); + + q = g3[b00 + bz1]; u = at3(rx0, ry0, rz1); + q = g3[b10 + bz1]; v = at3(rx1, ry0, rz1); + a = lerp(t, u, v); + + q = g3[b01 + bz1]; u = at3(rx0, ry1, rz1); + q = g3[b11 + bz1]; v = at3(rx1, ry1, rz1); + b = lerp(t, u, v); + + d = lerp(sy, a, b); + + return lerp(sz, c, d); + } + + static void normalize2(float v[2]) + { + float s; + + s = sqrt(v[0] * v[0] + v[1] * v[1]); + v[0] = v[0] / s; + v[1] = v[1] / s; + } + + static void normalize3(float v[3]) + { + float s; + + s = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); + v[0] = v[0] / s; + v[1] = v[1] / s; + v[2] = v[2] / s; + } + + static void init(void) + { + int i, j, k; + + for (i = 0; i < B; i++) { + p[i] = i; + + g1[i] = (float)((rand() % (B + B)) - B) / B; + + for (j = 0; j < 2; j++) + g2[i][j] = (float)((rand() % (B + B)) - B) / B; + normalize2(g2[i]); + + for (j = 0; j < 3; j++) + g3[i][j] = (float)((rand() % (B + B)) - B) / B; + normalize3(g3[i]); + } + + while (--i) { + k = p[i]; + p[i] = p[j = rand() % B]; + p[j] = k; + } + + for (i = 0; i < B + 2; i++) { + p[B + i] = p[i]; + g1[B + i] = g1[i]; + for (j = 0; j < 2; j++) + g2[B + i][j] = g2[i][j]; + for (j = 0; j < 3; j++) + g3[B + i][j] = g3[i][j]; + } + } +} + +Terrain::Terrain() +{ + isTexture = false; + Heightmap = nullptr; + + resolution = 0; + noise_scale = 0.010; + amplitude = 10.0; + + Mesh = SpawnObject<VisibleObject>(); +} + +#pragma optimize("", off) +void Terrain::InitTerrain(int r, Vector scale) +{ + resolution = r; + Scale = scale / r; + + std::vector<Vector> pos; + std::vector<Vector> uvs; + std::vector<Vector> normals; + std::vector<Vector> tangents; + std::vector<uint32> inds; + + pos.reserve(r * r); + + Vector offset = scale / 2; + offset.Z = 0.f; + + for (int32 y = 0; y < r + 1; y++) { + for (int32 x = 0; x < r + 1; x++) { + pos.emplace_back(Vector(Scale.X * x, Scale.Y * y, GetHeight(Scale.X * x, Scale.Y * y))); + uvs.emplace_back(Vector((x - 1) / (float)r, (y - 1) / (float)r, 0.f)); + normals.emplace_back(Vector(0.f, 0.f, 1.f)); + if (y < r && x < r) { + inds.emplace_back(y * (r + 1) + x); + inds.emplace_back((y + 1) * (r + 1) + x); + inds.emplace_back((y + 1) * (r + 1) + x + 1); + + inds.emplace_back(y * (r + 1) + x); + inds.emplace_back((y + 1) * (r + 1) + x + 1); + inds.emplace_back(y * (r + 1) + x + 1); + } + } + } + + normals.resize(pos.size()); + tangents.resize(pos.size()); + + Mesh->SetModel(MI->CreateProcedural(Mesh, "Terrain_" + std::to_string(GetRecord()), pos, uvs, normals, tangents, inds)); + Mesh->GetModel()->SetMaterial(0, RI->LoadMaterialByName("Shaders/ground")); +} +// +//float fract(float p) +//{ +// return p - floor(p); +//} +// +//float hash1(float px, float py) +//{ +// px = 50.0 * fract(px * 0.3183099); +// py = 50.0 * fract(py * 0.3183099); +// return fract(px * py * (px + py)); +//} +// +//float clamp(float x, float lowerlimit, float upperlimit) { +// if (x < lowerlimit) +// x = lowerlimit; +// if (x > upperlimit) +// x = upperlimit; +// return x; +//} +// +//float smoothstep(float edge0, float edge1, float x) { +// x = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0); +// return x * x * (3 - 2 * x); +//} +// +//float noise(float x, float y) +//{ +// float px = floor(x); +// float py = floor(x); +// float wx = fract(x); +// float wy = fract(x); +// +// float ux = wx * wx * wx * (wx * (wx * 6.0 - 15.0) + 10.0); +// float uy = wy * wy * wy * (wy * (wy * 6.0 - 15.0) + 10.0); +// +// float a = hash1(px, py); +// +// float b = hash1(px + 1, py + 0); +// +// float c = hash1(px + 0, py + 1); +// +// float d = hash1(px + 1, py + 1); +// +// return -1.0 + 2.0 * (a + (b - a) * ux + (c - a) * uy + (a - b - c + d) * ux * uy); +//} +// +//float fbm_9(float x, float y) +//{ +// float f = 1.9; +// float s = 0.55; +// float a = 0.0; +// float b = 0.5; +// for (int i = 0; i < 9; i++) +// { +// float n = noise(x, y); +// a += b * n; +// } +// return a; +//} + +float Terrain::GetHeight(float x, float y) +{ + //const float sca = 0.010; + //const float amp = 10.0; + //x *= sca; + //y *= sca; + //float e = fbm_9( x + 1.0, y + -2.0); + //float a = 1.0 - smoothstep(0.12, 0.13, abs(e + 0.12)); // flag high-slope areas (-0.25, 0.0) + //e = e + 0.15 * smoothstep(-0.08, -0.01, e); + //e *= amp; + //return e; + x *= noise_scale; + y *= noise_scale; + float arr[] = { x, y }; + return Noise::noise2(arr) * amplitude; +} +#pragma optimize("", on) diff --git a/Engine/Objects/Terrain.h b/Engine/Objects/Terrain.h new file mode 100644 index 0000000000000000000000000000000000000000..87f66be35970fcf120ecbc7d7fa52b7be9368307 --- /dev/null +++ b/Engine/Objects/Terrain.h @@ -0,0 +1,27 @@ +#pragma once +#include <Core.h> +#include <Objects/VisibleObject.h> + +class Terrain : public BaseObject +{ +public: + Terrain(); + + virtual void BeginPlay() override {} + + void InitTerrain(int r, Vector scale = Vector(1.f)); + float GetHeight(float x, float y); +private: + + int resolution; + Vector Scale; + + float noise_scale = 0.010; + float amplitude = 10.0; + + bool isTexture; + Texture* Heightmap; + + Ref<VisibleObject> Mesh; +}; + diff --git a/Engine/Objects/VisibleObject.cpp b/Engine/Objects/VisibleObject.cpp index 59ef3355909b670a06bf5dac128505c3cef32d81..68162f64681d47ebb9587307bf9a416d81da9bce 100644 --- a/Engine/Objects/VisibleObject.cpp +++ b/Engine/Objects/VisibleObject.cpp @@ -1,4 +1,5 @@ #include "VisibleObject.h" +#include "Physics.h" VisibleObject::VisibleObject() : BaseObject() { @@ -6,6 +7,12 @@ VisibleObject::VisibleObject() : BaseObject() Rotation = Vector(0, 0, 0); Scale = Vector(1, 1, 1); RenderData = nullptr; + Physics::AddStatic(this); +} + +void VisibleObject::OnDestroyed() +{ + Physics::RemoveStatic(this); } void VisibleObject::SetLocation(Vector NewLocation) @@ -31,3 +38,9 @@ void VisibleObject::SetModel(std::string Name) RenderData = MI->LoadData(this, Name); if (RenderData != nullptr) RenderData->ApplyTransform(); } + +void VisibleObject::SetModel(RenderMesh* mesh) +{ + RenderData = mesh; + if (RenderData != nullptr) RenderData->ApplyTransform(); +} diff --git a/Engine/Objects/VisibleObject.h b/Engine/Objects/VisibleObject.h index 36cc007f61f6f8e9cbe598a1001a9cec5f73b00e..537012734d3bab629ca12b927f8951636048d797 100644 --- a/Engine/Objects/VisibleObject.h +++ b/Engine/Objects/VisibleObject.h @@ -8,6 +8,7 @@ class VisibleObject : public BaseObject public: VisibleObject(); virtual ~VisibleObject() { delete RenderData; } + virtual void OnDestroyed() override; void SetLocation(Vector NewLocation); void SetRotation(Vector NewRotation); @@ -19,6 +20,7 @@ public: const Vector GetScale() const { return Scale; } void SetModel(std::string Name); + void SetModel(RenderMesh* mesh); std::string GetModelName() const { return std::string(); } RenderMesh* GetModel() const { return RenderData; } diff --git a/Engine/OpenGLRenderer/Mesh.cpp b/Engine/OpenGLRenderer/Mesh.cpp index b0b15f9e826d8c81f9ffef40325922a3b43cbd84..a6d070c4ffbbf5b05b97c538213e12f842ace013 100644 --- a/Engine/OpenGLRenderer/Mesh.cpp +++ b/Engine/OpenGLRenderer/Mesh.cpp @@ -62,6 +62,7 @@ RenderObject::RenderObject(LoadedMesh* mesh) { mesh->Users++; Mesh = mesh; + float extent = 0.f; SectionCount = mesh->HolderCount; Sections = new Section[SectionCount](); @@ -92,7 +93,10 @@ RenderObject::RenderObject(LoadedMesh* mesh) glBindVertexArray(0); SetMaterial(i, mesh->Holders[i]->Instance); Sections[i].Parent = this; + + if (Sections[i].GetRadius() > extent) extent = Sections[i].GetRadius(); } + bounds = extent; } RenderObject::~RenderObject() diff --git a/Engine/OpenGLRenderer/Renderer.cpp b/Engine/OpenGLRenderer/Renderer.cpp index 70defaa7e5bda5be229d381c15020a924e677245..5f9b8514fae57f322bcf9fad3012162439c9df97 100644 --- a/Engine/OpenGLRenderer/Renderer.cpp +++ b/Engine/OpenGLRenderer/Renderer.cpp @@ -3,12 +3,11 @@ #include <GLFW/glfw3.h> #include "Core.h" #include "Renderer.h" -#include "Batcher.h" #include "Camera.h" #include "Mesh.h" #include "Texture.h" #include "RenderBuffer.h" -#include "../Objects/VisibleObject.h" +#include <Objects/VisibleObject.h> #include "LightData.h" #include "Settings.h" #include <filesystem> @@ -1205,7 +1204,7 @@ void processMesh(LoadedMesh* meshHolder, aiMesh* mesh) vertex.position.x = mesh->mVertices[i].x; vertex.position.y = mesh->mVertices[i].y; vertex.position.z = mesh->mVertices[i].z; - if (radius < mesh->mVertices[i].Length()) radius = mesh->mVertices[i].Length(); + if (radius < mesh->mVertices[i].x) radius = mesh->mVertices[i].x; vertex.normal.x = mesh->mNormals[i].x; vertex.normal.y = mesh->mNormals[i].y; @@ -1238,7 +1237,7 @@ void processMesh(LoadedMesh* meshHolder, aiMesh* mesh) } std::vector<uint> adjacent; MeshDataHolder* section = new MeshDataHolder(vertices.data(), vertices.size(), indices.data(), indices.size()); - section->Radius = radius * 1.5f; + section->Radius = radius; /*section->FaceCount = indices.size() / 3; section->VertexCount = vertices.size(); @@ -1388,6 +1387,41 @@ RenderMesh* GLMesh::LoadData(VisibleObject* parent, String name) } } +RenderMesh* GLMesh::CreateProcedural(VisibleObject* parent, String name, std::vector<Vector>& positions, std::vector<Vector> UV, std::vector<Vector>& normal, std::vector<Vector>& tangent, std::vector<uint32>& indices) +{ + LoadedMesh* mesh = new LoadedMesh(); + + std::vector<Vertex> verts; + verts.reserve(positions.size()); + + AABB bounds; + for (auto i = 0; i < positions.size(); i++) { + if (bounds.mins.X > positions[i].X) bounds.mins.X = positions[i].X; + else if (bounds.maxs.X < positions[i].X) bounds.maxs.X = positions[i].X; + if (bounds.mins.Y > positions[i].Y) bounds.mins.Y = positions[i].Y; + else if (bounds.maxs.Y < positions[i].Y) bounds.maxs.Y = positions[i].Y; + if (bounds.mins.Z > positions[i].Z) bounds.mins.Z = positions[i].Z; + else if (bounds.maxs.Z < positions[i].Z) bounds.maxs.Z = positions[i].Z; + + verts[i].position = glm::vec3(positions[i].X, positions[i].Z, positions[i].Y); + verts[i].uv = glm::vec3(UV[i].X, UV[i].Y, UV[i].Z); + verts[i].normal = glm::vec3(normal[i].X, normal[i].Z, normal[i].Y); + verts[i].tangent = glm::vec3(tangent[i].X, tangent[i].Z, tangent[i].Y); + } + + MeshDataHolder* section = new MeshDataHolder(verts.data(), positions.size(), indices.data(), indices.size()); + + mesh->HolderCount++; + mesh->Holders.push_back(section); + section->Radius = bounds.maxs.Length(); + + LoadedMeshes.emplace(name, mesh); + RenderObject* obj = new RenderObject(mesh); + obj->SetParent(parent); + obj->SetAABB(bounds); + return obj; +} + void GLMesh::StartLoading() { namespace fs = std::filesystem; diff --git a/Engine/OpenGLRenderer/include/Renderer.h b/Engine/OpenGLRenderer/include/Renderer.h index 94723ae0c513ecac0ca51bf4ac6d5342f60d656d..dbf0c6f37a46e5d6dbcc10bc32d672a246637698 100644 --- a/Engine/OpenGLRenderer/include/Renderer.h +++ b/Engine/OpenGLRenderer/include/Renderer.h @@ -3,7 +3,6 @@ class Shader; class Texture; -class RenderBatch; class GLCamera; class PreDepthBuffer; class PostBuffer; @@ -107,6 +106,7 @@ public: GLMesh(); virtual ~GLMesh(); virtual RenderMesh* LoadData(VisibleObject* parent, String name) override; + virtual RenderMesh* CreateProcedural(VisibleObject* parent, String name, std::vector<Vector>& positions, std::vector<Vector> UV, std::vector<Vector>& normal, std::vector<Vector>& tangent, std::vector<uint32>& indices) override; virtual void StartLoading() override; private: diff --git a/Engine/Physics.cpp b/Engine/Physics.cpp new file mode 100644 index 0000000000000000000000000000000000000000..15bbdbb3df5a7cfdb2d6a2010698784939d7d509 --- /dev/null +++ b/Engine/Physics.cpp @@ -0,0 +1,159 @@ +#include <Objects/VisibleObject.h> +#include <Objects/MovementComponent.h> +#include <limits> +#include "Physics.h" + +namespace Physics +{ + namespace + { + std::vector<RefWeak<VisibleObject>> Statics; + std::vector<RefWeak<MovementComponent>> Movables; + + bool intersect(AABB a, AABB b) { + return (a.mins.X <= b.maxs.X && a.maxs.X >= b.mins.X) && + (a.mins.Y <= b.maxs.Y && a.maxs.Y >= b.mins.Y) && + (a.mins.Z <= b.maxs.Z && a.maxs.Z >= b.mins.Z); + } + + float sign(float v) { + return v < 0.f ? -1.0f : 1.0f; + } + + struct Hit { + AABB collider; + bool collided; + Vector pos; + Vector delta; + Vector normal; + Vector time; + }; + + struct Sweep { + Hit hit; + Vector pos; + float time; + }; + /* + Hit intersectSegment(const AABB& box, Vector pos, Vector delta, Vector padding) + { + Vector scale = Vector(1.0) / delta; + Vector vsign = Vector(sign(scale.X), sign(scale.Y), sign(scale.Z)); + Vector nearTime = (box.position - vsign * (hwidth + padding) - pos) * scale; + Vector farTime = (position + vsign * (hwidth + padding) - pos) * scale; + if ((nearTime.X > farTime.Y) || (nearTime.X > farTime.Z) || (nearTime.Y > farTime.X) || (nearTime.Y > farTime.Z) || (nearTime.Z > farTime.X) || (nearTime.Z > farTime.Y)) + return Hit(false); + + float near = std::max(std::max(nearTime.X, nearTime.Y), nearTime.Z); + float far = std::min(std::min(farTime.X, farTime.Y), farTime.Z); + + if ((near >= 1) || (far <= 0)) + return Hit(false); + + Hit hit; + hit.time = near;//glm::clamp(near , 0.f, 1.f); + if ((nearTime.X > nearTime.Y) && (nearTime.X > nearTime.Z)) + { + hit.normal = Vector(-vsign.X, 0, 0); + } + else if ((nearTime.Y > nearTime.X) && (nearTime.Y > nearTime.Z)) + { + hit.normal = Vector(0, -vsign.Y, 0); + } + else if ((nearTime.Z > nearTime.Y) && (nearTime.Z > nearTime.X)) + { + hit.normal = Vector(0, 0, -vsign.Z); + } + + hit.delta = hit.time * delta; + + hit.pos = pos + hit.delta; + hit.time = nearTime; + + return hit; + + } + Sweep sweepAABB(AABB first, AABB box, Vector delta) + { + if (delta == Vector(0, 0, 0)) + { + Sweep s; + s.hit.collided = false; + s.time = 1; + return s; + } + Sweep sweep; + sweep.hit = intersectSegment(first, box.position, delta, box.hwidth); + + if (sweep.hit.collided) + { + sweep.time = glm::clamp(sweep.hit.time - EPSILON, 0.f, 1.f); + sweep.pos = box.position + (delta * sweep.time); + + Vector direction = delta.Normalize(); + + sweep.hit.pos += direction * box.hwidth; + sweep.item = box.getSimple(); + sweep.other = getSimple(); + } + else + { + sweep.pos = box.position + delta; + sweep.time = 1; + } + return sweep; + }*/ + } + +#pragma optimize("", off) + void CheckCollisions() + { + //printf("Colliding objects...\n"); + //printf("Statics: %u, Movables: %u\n", Statics.size(), Movables.size()); + + for (const auto& o : Movables) { + if (o->GetTarget() == nullptr || o->GetTarget()->GetModel() == nullptr) continue; + Vector& d_loc = o->DesiredState.location; + const Vector& o_loc = o->OldState.location; + const AABB& d_AABB = o->GetTarget()->GetModel()->GetAABB(); + + + + std::vector<VisibleObject*> Collisions; + for (const auto& s : Statics) { + const AABB& bounds = s->GetModel()->GetAABB(); + const Vector& loc = s->GetLocation(); + + } + } + + for (const auto& m : Movables) { + m->ApplyMovement(); + } + } +#pragma optimize("", on) + + void AddStatic(VisibleObject* obj) + { + Statics.push_back(obj); + } + + void RemoveStatic(VisibleObject* obj) + { + for (auto i = Statics.begin(); i != Statics.end(); i++) { + if (i->GetPointer() == obj) { Statics.erase(i); break; } + } + } + + void AddMovable(MovementComponent* obj) + { + Movables.push_back(obj); + } + + void RemoveMovable(MovementComponent* obj) + { + for (auto i = Movables.begin(); i != Movables.end(); i++) { + if (i->GetPointer() == obj) Movables.erase(i); + } + } +} diff --git a/Engine/Physics.h b/Engine/Physics.h new file mode 100644 index 0000000000000000000000000000000000000000..fafedc905ea75318f82df912ee3fa95d736cbdce --- /dev/null +++ b/Engine/Physics.h @@ -0,0 +1,15 @@ +#pragma once +#include <Core.h> + +class VisibleObject; +class MovementComponent; + +namespace Physics +{ + void CheckCollisions(); + void AddStatic(VisibleObject* obj); + void RemoveStatic(VisibleObject* obj); + void AddMovable(MovementComponent* obj); + void RemoveMovable(MovementComponent* obj); +}; + diff --git a/Eril.vcxproj b/Eril.vcxproj index 63807319ce8f3719e4344f57e822f91b05f757e4..65ae8a62d8dd74ab5eb2799f0a0359ceff148d17 100644 --- a/Eril.vcxproj +++ b/Eril.vcxproj @@ -215,6 +215,7 @@ <ClCompile Include="Engine\Objects\BaseObject.cpp" /> <ClCompile Include="Engine\Objects\InstancedObject.cpp" /> <ClCompile Include="Engine\Objects\MovementComponent.cpp" /> + <ClCompile Include="Engine\Objects\Terrain.cpp" /> <ClCompile Include="Engine\Objects\VisibleObject.cpp" /> <ClCompile Include="Engine\OpenGLRenderer\Batcher.cpp" /> <ClCompile Include="Engine\OpenGLRenderer\Camera.cpp" /> @@ -224,6 +225,7 @@ <ClCompile Include="Engine\OpenGLRenderer\RenderBuffer.cpp" /> <ClCompile Include="Engine\OpenGLRenderer\Renderer.cpp" /> <ClCompile Include="Engine\OpenGLRenderer\Texture.cpp" /> + <ClCompile Include="Engine\Physics.cpp" /> <ClCompile Include="Engine\Settings.cpp" /> <ClCompile Include="Engine\Timer.cpp" /> <ClCompile Include="Engine\WinConsole.cpp" /> @@ -244,6 +246,7 @@ <ClInclude Include="Engine\Objects\BaseObject.h" /> <ClInclude Include="Engine\Objects\InstancedObject.h" /> <ClInclude Include="Engine\Objects\MovementComponent.h" /> + <ClInclude Include="Engine\Objects\Terrain.h" /> <ClInclude Include="Engine\Objects\VisibleObject.h" /> <ClInclude Include="Engine\OpenGLRenderer\ext\glfw\deps\glad\gl.h" /> <ClInclude Include="Engine\OpenGLRenderer\include\Batcher.h" /> @@ -254,6 +257,7 @@ <ClInclude Include="Engine\OpenGLRenderer\include\RenderBuffer.h" /> <ClInclude Include="Engine\OpenGLRenderer\include\Renderer.h" /> <ClInclude Include="Engine\OpenGLRenderer\include\Texture.h" /> + <ClInclude Include="Engine\Physics.h" /> <ClInclude Include="Engine\Settings.h" /> <ClInclude Include="Engine\Timer.h" /> <ClInclude Include="Engine\WinConsole.h" /> diff --git a/Eril.vcxproj.filters b/Eril.vcxproj.filters index bbdb8b37704656a7e3b30c1daac746a50316c5b1..83df4967bedb9ee6dd60f32a249717235e1b7214 100644 --- a/Eril.vcxproj.filters +++ b/Eril.vcxproj.filters @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="16.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup> <ClCompile Include="$(SolutionDir)\Engine\OpenGLRenderer\ext\glfw\deps\glad_gl.c"> @@ -88,6 +88,12 @@ <ClCompile Include="Game\FallingCube.cpp"> <Filter>Game</Filter> </ClCompile> + <ClCompile Include="Engine\Physics.cpp"> + <Filter>Engine\Private</Filter> + </ClCompile> + <ClCompile Include="Engine\Objects\Terrain.cpp"> + <Filter>Engine\Objects</Filter> + </ClCompile> <ClCompile Include="Engine\Timer.cpp"> <Filter>Engine\Private</Filter> </ClCompile> @@ -203,6 +209,12 @@ <ClInclude Include="Game\FallingCube.h"> <Filter>Game</Filter> </ClInclude> + <ClInclude Include="Engine\Physics.h"> + <Filter>Engine\Public</Filter> + </ClInclude> + <ClInclude Include="Engine\Objects\Terrain.h"> + <Filter>Engine\Objects</Filter> + </ClInclude> <ClInclude Include="Engine\Timer.h"> <Filter>Engine\Public</Filter> </ClInclude> diff --git a/Game/TestPlayer.cpp b/Game/TestPlayer.cpp index b488d5f43826698810bfa0a6706e810e4ee4348b..5e62e46fc7fa185ec20eec38dd18430c77c793ab 100644 --- a/Game/TestPlayer.cpp +++ b/Game/TestPlayer.cpp @@ -3,6 +3,7 @@ #include "TestPlayer.h" #include "Objects/MovementComponent.h" #include "FallingCube.h" +#include "Objects/Terrain.h" #include "Timer.h" TestPlayer::TestPlayer() : Player() @@ -26,14 +27,23 @@ TestPlayer::TestPlayer() : Player() II->RegisterKeyInput(256, &TestPlayer::InputExit, this); II->RegisterMouseInput(0, &TestPlayer::MouseMoved, this); + SetModel("Cube"); + RenderData->SetAABB(AABB(Vector(-0.5f), Vector(0.5f))); + + terra = SpawnObject<Terrain>(); + terra->InitTerrain(1000, Vector(100.f, 100.f, 1.f)); + Movement = SpawnObject<MovementComponent>(); Movement->SetTarget(dynamic_cast<Actor*>(this)); - Movement->SetGravity(false); + Movement->SetGravity(true); + Movement->SetGround(terra); } void TestPlayer::RunInputW(float delta, bool KeyDown) { - Movement->AddInput(-GetCamera()->GetForwardVector()); + Vector dir = -GetCamera()->GetForwardVector(); + dir.Z = 0.f; + Movement->AddInput(dir.Normalize()); } void TestPlayer::RunInputA(float delta, bool KeyDown) @@ -48,12 +58,15 @@ void TestPlayer::RunInputD(float delta, bool KeyDown) void TestPlayer::RunInputS(float delta, bool KeyDown) { - Movement->AddInput(GetCamera()->GetForwardVector()); + Vector dir = GetCamera()->GetForwardVector(); + dir.Z = 0.f; + Movement->AddInput(dir.Normalize()); } void TestPlayer::RunInputSpace(float delta, bool KeyDown) { - + if (!Movement->IsInAir() && KeyDown) + Movement->AddImpulse(Vector(0.f, 0.f, 300.f)); } void TestPlayer::InputOne(float delta, bool KeyDown) @@ -105,10 +118,10 @@ void TestPlayer::MouseMoved(float X, float Y) void TestPlayer::Tick(float) { - GetCamera()->SetLocation(Location); + GetCamera()->SetLocation(Location + Vector(0.f, 0.f, 1.5f)); GetCamera()->SetRotation(Rotation); - if (spawnCounter++ == 30) { + /*if (spawnCounter++ == 40) { FallingCube* obj = SpawnObject<FallingCube>(); @@ -119,7 +132,7 @@ void TestPlayer::Tick(float) obj->SetLocation(Vector(rx * size, ry * size, 15.f)); spawnCounter = 0; - } + }*/ } #include "Objects/InstancedObject.h" @@ -142,14 +155,15 @@ void TestPlayer::BeginPlay() DirLight->Data.Rotation = Vector(45.0, 0.0, 0.0); LastSphere = SpawnObject<Actor>(); - LastSphere->SetModel("sphere"); - LastSphere->SetLocation(Vector(1.5f, 0.0f, 0.0f)); - LastSphere->GetModel()->SetMaterial(0, RI->LoadMaterialByName("Shaders/metal")); + LastSphere->SetModel("Cube"); + LastSphere->SetLocation(Vector(3.5f, 0.0f, 0.0f)); + LastSphere->SetScale(Vector(1.f, 1.f, 0.2f)); + LastSphere->GetModel()->SetMaterial(0, RI->LoadMaterialByName("Shaders/rocks")); - LastSphere2 = SpawnObject<Actor>(); + /*LastSphere2 = SpawnObject<Actor>(); LastSphere2->SetModel("sphere"); LastSphere2->SetLocation(Vector(3.f, 0.0f, 0.0f)); - LastSphere2->GetModel()->SetMaterial(0, RI->LoadMaterialByName("Shaders/metal")); + LastSphere2->GetModel()->SetMaterial(0, RI->LoadMaterialByName("Shaders/metal"));*/ Timer::CreateTimer(5.f, TimeFunction, false); @@ -183,20 +197,20 @@ void TestPlayer::BeginPlay() float size = 80.f; - for (int x = 0; x < 100; x++) { - VisibleObject* next = SpawnObject<VisibleObject>(); - next->SetModel("Cube"); - float rx = (float)rand() / (float)RAND_MAX - 0.5f; - float ry = (float)rand() / (float)RAND_MAX - 0.5f; - float rz = (float)rand() / (float)RAND_MAX; - //float scale = (float)rand() / (float)RAND_MAX * 2.f; - //next->SetScale(Vector(scale)); - next->GetModel()->SetMaterial(0, RI->LoadMaterialByName("Shaders/rocks")); - next->SetLocation(Vector(rx * size, ry * size, rz * size / 3)); - Spheres[x] = next; - } - - Spheres[10]->SetScale(2.f); + //for (int x = 0; x < 100; x++) { + // VisibleObject* next = SpawnObject<VisibleObject>(); + // next->SetModel("Cube"); + // float rx = (float)rand() / (float)RAND_MAX - 0.5f; + // float ry = (float)rand() / (float)RAND_MAX - 0.5f; + // float rz = (float)rand() / (float)RAND_MAX; + // //float scale = (float)rand() / (float)RAND_MAX * 2.f; + // //next->SetScale(Vector(scale)); + // next->GetModel()->SetMaterial(0, RI->LoadMaterialByName("Shaders/rocks")); + // next->SetLocation(Vector(rx * size, ry * size, rz * size / 3)); + // Spheres[x] = next; + //} + + //Spheres[10]->SetScale(2.f); /*for (int x = 0; x < 20; x++) { for (int y = 0; y < 20; y++) { diff --git a/Game/TestPlayer.h b/Game/TestPlayer.h index 6d8ada866dfc87750e2dc83761fbf2787add7488..630476b3d12a139e61911b6f283c1f6243323cc9 100644 --- a/Game/TestPlayer.h +++ b/Game/TestPlayer.h @@ -7,6 +7,7 @@ class Actor; class InstancedObject; class Light; class MovementComponent; +class Terrain; class TestPlayer : public Player { @@ -45,6 +46,7 @@ private: Ref<Actor> LastSphere; Ref<Actor> LastSphere2; + Ref<Terrain> terra; int spawnCounter; }; \ No newline at end of file