Browse Source

Add foundation of navmesh steering

master
C. J. Howard 7 years ago
parent
commit
17844309e2
25 changed files with 1913 additions and 245 deletions
  1. +10
    -6
      CMakeLists.txt
  2. +1
    -1
      data
  3. +1
    -1
      lib/emergent
  4. +0
    -21
      src/ant.cpp
  5. +0
    -84
      src/ant.hpp
  6. +10
    -0
      src/application.cpp
  7. +10
    -3
      src/application.hpp
  8. +157
    -0
      src/game/agent.cpp
  9. +193
    -0
      src/game/agent.hpp
  10. +138
    -0
      src/game/ant.cpp
  11. +165
    -0
      src/game/ant.hpp
  12. +546
    -0
      src/game/navmesh.cpp
  13. +264
    -0
      src/game/navmesh.hpp
  14. +0
    -0
      src/game/nest.cpp
  15. +0
    -0
      src/game/nest.hpp
  16. +0
    -0
      src/game/terrain.cpp
  17. +0
    -0
      src/game/terrain.hpp
  18. +10
    -5
      src/material-loader.cpp
  19. +1
    -0
      src/material-loader.hpp
  20. +210
    -82
      src/render-passes.cpp
  21. +54
    -7
      src/render-passes.hpp
  22. +2
    -2
      src/states/experiment-state.hpp
  23. +14
    -12
      src/states/splash-state.cpp
  24. +111
    -18
      src/states/title-state.cpp
  25. +16
    -3
      src/states/title-state.hpp

+ 10
- 6
CMakeLists.txt View File

@ -86,8 +86,8 @@ set(EXECUTABLE_SOURCES
${EXECUTABLE_SOURCE_DIR}/render-passes.hpp
${EXECUTABLE_SOURCE_DIR}/settings.cpp
${EXECUTABLE_SOURCE_DIR}/settings.hpp
${EXECUTABLE_SOURCE_DIR}/terrain.cpp
${EXECUTABLE_SOURCE_DIR}/terrain.hpp
${EXECUTABLE_SOURCE_DIR}/game/terrain.cpp
${EXECUTABLE_SOURCE_DIR}/game/terrain.hpp
${EXECUTABLE_SOURCE_DIR}/controls.hpp
${EXECUTABLE_SOURCE_DIR}/input.cpp
${EXECUTABLE_SOURCE_DIR}/input.hpp
@ -109,10 +109,14 @@ set(EXECUTABLE_SOURCES
${EXECUTABLE_SOURCE_DIR}/ui/tween.hpp
${EXECUTABLE_SOURCE_DIR}/ui/tween.cpp
${EXECUTABLE_SOURCE_DIR}/render-passes.cpp
${EXECUTABLE_SOURCE_DIR}/ant.hpp
${EXECUTABLE_SOURCE_DIR}/ant.cpp
${EXECUTABLE_SOURCE_DIR}/nest.hpp
${EXECUTABLE_SOURCE_DIR}/nest.cpp
${EXECUTABLE_SOURCE_DIR}/game/ant.hpp
${EXECUTABLE_SOURCE_DIR}/game/ant.cpp
${EXECUTABLE_SOURCE_DIR}/game/agent.hpp
${EXECUTABLE_SOURCE_DIR}/game/agent.cpp
${EXECUTABLE_SOURCE_DIR}/game/nest.hpp
${EXECUTABLE_SOURCE_DIR}/game/nest.cpp
${EXECUTABLE_SOURCE_DIR}/game/navmesh.hpp
${EXECUTABLE_SOURCE_DIR}/game/navmesh.cpp
${EXECUTABLE_SOURCE_DIR}/debug.hpp
${EXECUTABLE_SOURCE_DIR}/debug.cpp
${EXECUTABLE_SOURCE_DIR}/camera-controller.hpp

+ 1
- 1
data

@ -1 +1 @@
Subproject commit 3f2b504970e90fd476a682f4754d276bf162729c
Subproject commit 37456bb0933b6eed8444f2d6f1ce0819bac00b15

+ 1
- 1
lib/emergent

@ -1 +1 @@
Subproject commit 94f7940aa2ab6375c7672354f3a731a86542f51c
Subproject commit 341a6cd0b704379cd38c9382f9ea0ef4b33a382a

+ 0
- 21
src/ant.cpp View File

@ -1,21 +0,0 @@
/*
* Copyright (C) 2017 Christopher J. Howard
*
* This file is part of Antkeeper Source Code.
*
* Antkeeper Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper Source Code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ant.hpp"

+ 0
- 84
src/ant.hpp View File

@ -1,84 +0,0 @@
/*
* Copyright (C) 2017 Christopher J. Howard
*
* This file is part of Antkeeper Source Code.
*
* Antkeeper Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper Source Code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANT_HPP
#define ANT_HPP
#include <vector>
#include <emergent/emergent.hpp>
using namespace Emergent;
class Gait;
class Ant
{
public:
/**
* Named constants corresponding to leg indices.
*
* \_/
* L1 --| |-- R1
* L2 --| |-- R2
* L3 --|_|-- R3
*/
enum class LegIndex
{
L1,
L2,
L3,
R1,
R2,
R3
};
private:
Transform transform;
ModelInstance modelInstance;
Pose* skeletonPose;
};
class Colony
{
public:
Ant* spawn();
const Model* getAntModel() const;
Model* getAntModel();
const Skeleton* getAntSkeleton() const;
Skeleton* getSkeleton();
private:
// Rendering
Model* antModel;
Skeleton* antSkeleton;
// Locomotion
float walkSpeed;
float turnSpeed;
Gait* tripodGait;
Gait* rippleGait;
Gait* slowWaveGait;
std::vector<Ant*> ants;
};
#endif // ANT_HPP

+ 10
- 0
src/application.cpp View File

@ -340,6 +340,11 @@ Application::Application(int argc, char* argv[]):
gameControlProfile->registerControl("camera-zoom-out", &cameraZoomOut);
gameControlProfile->registerControl("camera-toggle-nest-view", &cameraToggleNestView);
gameControlProfile->registerControl("camera-toggle-overhead-view", &cameraToggleOverheadView);
gameControlProfile->registerControl("walk-forward", &walkForward);
gameControlProfile->registerControl("walk-back", &walkBack);
gameControlProfile->registerControl("turn-left", &turnLeft);
gameControlProfile->registerControl("turn-right", &turnRight);
cameraMoveForward.bindKey(keyboard, SDL_SCANCODE_W);
cameraMoveBack.bindKey(keyboard, SDL_SCANCODE_S);
cameraMoveLeft.bindKey(keyboard, SDL_SCANCODE_A);
@ -350,6 +355,10 @@ Application::Application(int argc, char* argv[]):
cameraZoomOut.bindKey(keyboard, SDL_SCANCODE_MINUS);
cameraToggleOverheadView.bindKey(keyboard, SDL_SCANCODE_R);
cameraToggleNestView.bindKey(keyboard, SDL_SCANCODE_F);
walkForward.bindKey(keyboard, SDL_SCANCODE_UP);
walkBack.bindKey(keyboard, SDL_SCANCODE_DOWN);
turnLeft.bindKey(keyboard, SDL_SCANCODE_LEFT);
turnRight.bindKey(keyboard, SDL_SCANCODE_RIGHT);
cameraOverheadView = true;
cameraNestView = false;
@ -364,6 +373,7 @@ Application::Application(int argc, char* argv[]):
SDL_GL_SwapWindow(window);
// Setup loaders
textureLoader = new TextureLoader();
materialLoader = new MaterialLoader();
modelLoader = new ModelLoader();
modelLoader->setMaterialLoader(materialLoader);

+ 10
- 3
src/application.hpp View File

@ -24,7 +24,7 @@
using namespace Emergent;
#include "mesh.hpp"
#include "terrain.hpp"
#include "game/terrain.hpp"
#include "input.hpp"
#include "controls.hpp"
#include "settings.hpp"
@ -102,7 +102,9 @@ public:
Camera camera;
Scene scene;
BillboardBatch particleBatch;
Arcball arcball;
TextureLoader* textureLoader;
MaterialLoader* materialLoader;
ModelLoader* modelLoader;
@ -158,6 +160,11 @@ public:
bool cameraOverheadView;
bool cameraNestView;
Control walkForward;
Control walkBack;
Control turnLeft;
Control turnRight;
// Misc
Timer frameTimer;
@ -167,8 +174,8 @@ public:
float fontSizePT;
float fontSizePX;
Font* menuFont;
Texture splashTexture;
Texture titleTexture;
Texture* splashTexture;
Texture* titleTexture;
Vector4 selectedColor;
Vector4 deselectedColor;
UIContainer* uiRootElement;

+ 157
- 0
src/game/agent.cpp View File

@ -0,0 +1,157 @@
/*
* Copyright (C) 2017 Christopher J. Howard
*
* This file is part of Antkeeper Source Code.
*
* Antkeeper Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper Source Code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "agent.hpp"
Agent::Agent():
navmeshTriangle(nullptr),
barycentricPosition(0.0f),
position(0.0f),
forward(0, 0, -1),
up(0, 1, 0),
right(1, 0, 0),
rotation(1, 0, 0, 0),
wanderDirection(0, 0, -1)
{}
void Agent::applyForce(const Vector3& force)
{
acceleration += force;
}
void Agent::updateVelocity()
{
// Limit acceleration
acceleration = limit(acceleration / mass, maxAcceleration);
// Add acceleration to velocity and limit
velocity = limit(velocity + acceleration, maxSpeed);
// Reset acceleration to zero
acceleration = Vector3(0.0f);
}
Vector3 Agent::wander(float dt)
{
// Calculate center of wander circle
Vector3 wanderCircleCenter = forward * wanderCircleDistance;
// Calculate wander force
Vector3 force = wanderCircleCenter + wanderDirection * wanderCircleRadius;
// Rotate wander direction by a random displacement angle
float displacement = frand(-wanderRate * 0.5f, wanderRate * 0.5f);
wanderDirection = glm::normalize(glm::angleAxis(displacement, up) * wanderDirection);
return force;
}
Vector3 Agent::seek(const Vector3& target) const
{
Vector3 desiredVelocity = glm::normalize(position - target) * maxSpeed;
return desiredVelocity - velocity;
}
Vector3 Agent::flee(const Vector3& target) const
{
Vector3 desiredVelocity = glm::normalize(target - position) * maxSpeed;
return desiredVelocity - velocity;
}
Vector3 Agent::containment(const Vector3& probe) const
{
std::vector<Navmesh::Step> traversal;
Navmesh::traverse(navmeshTriangle, barycentricPosition, probe, &traversal);
if (traversal.empty())
{
return Vector3(0.0f);
}
const Navmesh::Step& step = traversal.back();
// If not on edge or on connected edge
if (step.edge == nullptr || step.edge->symmetric != nullptr)
{
return Vector3(0.0f);
}
// Calculate edge normal
const Vector3& a = step.edge->vertex->position;
const Vector3& b = step.edge->next->vertex->position;
Vector3 ab = glm::normalize(b - a);
Vector3 edgeNormal = glm::cross(up, ab);
// Calculate reflection vector of forward vector and edge normal
Vector3 force = glm::reflect(forward, edgeNormal);
return force;
}
void Agent::setPosition(Navmesh::Triangle* triangle, const Vector3& position)
{
// Update navmesh triangle and position
navmeshTriangle = triangle;
barycentricPosition = position;
// Convert navmesh-space barycentric position to world-space cartesian position
const Vector3& a = triangle->edge->vertex->position;
const Vector3& b = triangle->edge->next->vertex->position;
const Vector3& c = triangle->edge->previous->vertex->position;
this->position = cartesian(position, a, b, c);
}
void Agent::setOrientation(const Vector3& newForward, const Vector3& newUp)
{
// Calculate alignment quaternion
Quaternion alignment = glm::rotation(up, newUp);
// Rebuild vector basis
forward = newForward;
right = glm::normalize(glm::cross(newUp, forward));
up = glm::cross(forward, right);
// Calculate rotation quaternion from vector basis
rotation = glm::normalize(glm::quat_cast(Matrix3(right, up, forward)));
// Align wander direction
wanderDirection = glm::normalize(project_on_plane(alignment * wanderDirection, Vector3(0.0f), up));
}
void Agent::setWanderCircleDistance(float distance)
{
wanderCircleDistance = distance;
}
void Agent::setWanderCircleRadius(float radius)
{
wanderCircleRadius = radius;
}
void Agent::setWanderRate(float angle)
{
wanderRate = angle;
}
/** EXAMPLE USAGE
Vector3 wanderForce = wander(dt) * wanderWeight;
Vector3 fleeForce = flee(mouse) * fleeWeight;
Vector3 steerForce = wanderForce + fleeForce;
steer(steerForce);
**/

+ 193
- 0
src/game/agent.hpp View File

@ -0,0 +1,193 @@
/*
* Copyright (C) 2017 Christopher J. Howard
*
* This file is part of Antkeeper Source Code.
*
* Antkeeper Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper Source Code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef AGENT_HPP
#define AGENT_HPP
#include "navmesh.hpp"
#include <vector>
#include <emergent/emergent.hpp>
using namespace Emergent;
class Obstacle;
/*************88
Ant is an agent.
Ant combines steering behaviors with different weights.
I.E.
seek pheromones * 0.5
separation * 0.1
alignment * 0.1
cohesion * 0.1
followWall * 0.2
*/
/**
* An agent which navigates on a navmesh.
*/
class Agent
{
public:
Agent();
/**
* Adds a force to the agent's acceleration vector.
*
* @param force Acceleration force
*/
void applyForce(const Vector3& force);
/**
* Calculates velocity based on current acceleration vector, then resets acceleration to zero.
*/
void updateVelocity();
/**
* Calculates steering force for the wander behavior.
*/
Vector3 wander(float dt);
/**
* Calculates steering force for the seek behavior.
*/
Vector3 seek(const Vector3& target) const;
/**
* Calculates steering force for the flee behavior.
*/
Vector3 flee(const Vector3& target) const;
Vector3 containment(const Vector3& probe) const;
void setMaxSpeed(float speed);
void setMaxAcceleration(float acceleration);
void setMass(float mass);
void setWanderCircleDistance(float distance);
void setWanderCircleRadius(float radius);
void setWanderRate(float angle);
/**
* Sets the position of the agent on a navmesh.
*
* @param triangle Navmesh triangle on which the agent resides
* @param position Barycentric position on the specified triangle
*/
void setPosition(Navmesh::Triangle* triangle, const Vector3& position);
/**
* Sets the orientation of the agent. This effectively updates the agent's vector basis and rotation quaternion.
*
* @param forward Normalized forward vector
* @param up Normalized up vector
*/
void setOrientation(const Vector3& newForward, const Vector3& newUp);
/*
Vector3 followWall();
// or
Vector3 followEdge();
Vector3 avoidObstacle(const Obstacle* obstacle);
Vector3 separation(const std::vector<const Agent*>* neighbors);
Vector3 alignment(const std::vector<const Agent*>* neighbors);
Vector3 cohesion(const std::vector<const Agent*>* neighbors);
*/
const Navmesh::Triangle* getNavmeshTriangle() const;
Navmesh::Triangle* getNavmeshTriangle();
const Vector3& getBarycentricPosition() const;
const Vector3& getPosition() const;
const Vector3& getForward() const;
const Vector3& getUp() const;
const Vector3& getRight() const;
const Quaternion& getRotation() const;
private:
Navmesh::Triangle* navmeshTriangle;
Vector3 barycentricPosition;
Vector3 position;
Vector3 forward;
Vector3 up;
Vector3 right;
Quaternion rotation;
// Limits
float maxSpeed;
float maxAcceleration;
// Steering forces
float mass;
Vector3 acceleration;
Vector3 velocity;
// Wander variables
float wanderCircleDistance;
float wanderCircleRadius;
float wanderRate;
Vector3 wanderDirection;
};
inline const Navmesh::Triangle* Agent::getNavmeshTriangle() const
{
return navmeshTriangle;
}
inline Navmesh::Triangle* Agent::getNavmeshTriangle()
{
return navmeshTriangle;
}
inline const Vector3& Agent::getBarycentricPosition() const
{
return barycentricPosition;
}
inline const Vector3& Agent::getPosition() const
{
return position;
}
inline const Vector3& Agent::getForward() const
{
return forward;
}
inline const Vector3& Agent::getUp() const
{
return up;
}
inline const Vector3& Agent::getRight() const
{
return right;
}
inline const Quaternion& Agent::getRotation() const
{
return rotation;
}
#endif // AGENT_HPP

+ 138
- 0
src/game/ant.cpp View File

@ -0,0 +1,138 @@
/*
* Copyright (C) 2017 Christopher J. Howard
*
* This file is part of Antkeeper Source Code.
*
* Antkeeper Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper Source Code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ant.hpp"
Ant::Ant(Colony* colony):
colony(colony),
state(Ant::State::IDLE),
transform(Transform::getIdentity()),
skeletonPose(nullptr)
{
modelInstance.setModel(colony->getAntModel());
}
void Ant::move(const Vector3& velocity)
{
std::vector<Navmesh::Step> traversal;
Navmesh::traverse(getNavmeshTriangle(), getBarycentricPosition(), velocity, &traversal);
if (!traversal.empty())
{
const Navmesh::Step& step = traversal.back();
if (step.start != step.end)
{
if (step.triangle != getNavmeshTriangle())
{
Quaternion alignment = glm::rotation(getNavmeshTriangle()->normal, step.triangle->normal);
Vector3 newForward = glm::normalize(project_on_plane(alignment * getForward(), Vector3(0.0f), step.triangle->normal));
setOrientation(newForward, step.triangle->normal);
}
}
setPosition(step.triangle, step.end);
}
}
void Ant::turn(float angle)
{
// Rotate forward vector
Vector3 newForward = glm::normalize(glm::angleAxis(angle, getUp()) * getForward());
setOrientation(newForward, getUp());
}
void Ant::update(float dt)
{
if (state == Ant::State::WANDER)
{
setWanderCircleDistance(3.0f);
setWanderCircleRadius(0.25f);
setWanderRate(glm::radians(90.0f));
// Calculate wander force
Vector3 wanderForce = wander(dt);
// Setup containment probes
float probeLateralOffset = 0.35f;
Vector3 forwardProbe = getForward() * 0.5f;
Vector3 leftProbe = getForward() * 0.1f - getRight() * probeLateralOffset;
Vector3 rightProbe = getForward() * 0.1f + getRight() * probeLateralOffset;
// Calculate containment force
Vector3 containmentForce = containment(forwardProbe)
+ containment(leftProbe)
+ containment(rightProbe);
// Calculate velocity
Vector3 velocity(0.0f);
velocity += wanderForce;
velocity += containmentForce;
velocity = limit(velocity, 0.025f);
setOrientation(glm::normalize(velocity), getUp());
// Move ant
move(velocity);
}
// Update transform
transform.translation = getPosition();
transform.rotation = getRotation();
// Update model instance
modelInstance.setTransform(transform);
}
void Ant::setState(Ant::State state)
{
this->state = state;
}
Colony::Colony():
antModel(nullptr)
{}
Ant* Colony::spawn(Navmesh* navmesh, Navmesh::Triangle* triangle, const Vector3& position)
{
// Allocate ant
Ant* ant = new Ant(this);
// Position it on the navmesh
ant->setPosition(triangle, position);
// Add ant to the colony
ants.push_back(ant);
return ant;
}
void Colony::update(float dt)
{
for (Ant* ant: ants)
{
ant->update(dt);
}
}
void Colony::setAntModel(Model* model)
{
this->antModel = model;
}

+ 165
- 0
src/game/ant.hpp View File

@ -0,0 +1,165 @@
/*
* Copyright (C) 2017 Christopher J. Howard
*
* This file is part of Antkeeper Source Code.
*
* Antkeeper Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper Source Code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANT_HPP
#define ANT_HPP
#include <vector>
#include "navmesh.hpp"
#include "agent.hpp"
#include <emergent/emergent.hpp>
using namespace Emergent;
class Colony;
class Gait;
/**
* An individual ant which belongs to a colony.
*/
class Ant: public Agent
{
public:
/**
* Named constants corresponding to leg indices.
*
* \_/
* L1 --| |-- R1
* L2 --| |-- R2
* L3 --|_|-- R3
*/
enum class LegIndex
{
L1,
L2,
L3,
R1,
R2,
R3
};
enum class State
{
IDLE,
WANDER
};
/**
* Creates an instance of Ant.
*/
Ant(Colony* colony);
void move(const Vector3& velocity);
void turn(float angle);
void update(float dt);
void setState(Ant::State state);
const Colony* getColony() const;
Colony* getColony();
const Transform& getTransform() const;
const ModelInstance* getModelInstance() const;
ModelInstance* getModelInstance();
private:
/**
* Calculates the surface normal averaged between the surface normals at each of the ant's grounded feet.
*/
Vector3 calculateAverageSurfaceNormal() const;
void updateTransform();
Colony* colony;
Ant::State state;
Transform transform;
ModelInstance modelInstance;
Pose* skeletonPose;
};
inline const Colony* Ant::getColony() const
{
return colony;
}
inline Colony* Ant::getColony()
{
return colony;
}
inline const Transform& Ant::getTransform() const
{
return transform;
}
inline const ModelInstance* Ant::getModelInstance() const
{
return &modelInstance;
}
inline ModelInstance* Ant::getModelInstance()
{
return &modelInstance;
}
/**
* A colony of ants.
*/
class Colony
{
public:
Colony();
Ant* spawn(Navmesh* navmesh, Navmesh::Triangle* triangle, const Vector3& position);
void update(float dt);
void setAntModel(Model* model);
const Model* getAntModel() const;
Model* getAntModel();
private:
// Rendering
Model* antModel;
// Locomotion
float walkSpeed;
float turnSpeed;
Gait* tripodGait;
Gait* rippleGait;
Gait* slowWaveGait;
std::vector<Ant*> ants;
};
inline const Model* Colony::getAntModel() const
{
return antModel;
}
inline Model* Colony::getAntModel()
{
return antModel;
}
#endif // ANT_HPP

+ 546
- 0
src/game/navmesh.cpp View File

@ -0,0 +1,546 @@
/*
* Copyright (C) 2017 Christopher J. Howard
*
* This file is part of Antkeeper Source Code.
*
* Antkeeper Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper Source Code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "navmesh.hpp"
#include <fstream>
#include <map>
#include <sstream>
#include <limits>
Navmesh::Navmesh()
{}
Navmesh::~Navmesh()
{
destroy();
}
bool Navmesh::create(const std::vector<Vector3>& vertices, const std::vector<std::size_t>& indices)
{
destroy();
if (indices.size() % 3 != 0)
{
std::cerr << "Navmesh::create(): index count is non multiple of 3\n";
return false;
}
// Copy vertices
this->vertices.resize(vertices.size());
for (std::size_t i = 0; i < vertices.size(); ++i)
{
Navmesh::Vertex* vertex = new Navmesh::Vertex();
vertex->edge = nullptr;
vertex->position = vertices[i];
vertex->flags = 0;
vertex->index = i;
this->vertices[i] = vertex;
}
// Allocate triangles
triangles.resize(indices.size() / 3, nullptr);
std::size_t currentTriangle = 0;
// Load triangles
std::map<std::pair<std::size_t, std::size_t>, Navmesh::Edge*> edgeMap;
for (std::size_t i = 0; i < indices.size(); i += 3)
{
std::size_t a = indices[i];
std::size_t b = indices[i + 1];
std::size_t c = indices[i + 2];
if (a >= vertices.size() || b >= vertices.size() || c >= vertices.size())
{
std::cerr << "Navmesh::create(): Mesh contains invalid index.\n";
destroy();
return false;
}
// Allocate three edges and a triangle
Navmesh::Edge* ab = new Navmesh::Edge();
Navmesh::Edge* bc = new Navmesh::Edge();
Navmesh::Edge* ca = new Navmesh::Edge();
Navmesh::Triangle* triangle = new Navmesh::Triangle();
// Zero triangle flags
triangle->flags = 0;
// Zero edge flags
ab->flags = 0;
bc->flags = 0;
ca->flags = 0;
// Set triangle start edge
triangle->edge = ab;
// For each edge in this triangle
std::size_t triangleIndices[] = {a, b, c};
Navmesh::Edge* triangleEdges[] = {ab, bc, ca};
for (std::size_t j = 0; j < 3; ++j)
{
// Set edge properties
Navmesh::Edge* edge = triangleEdges[j];
edge->triangle = triangle;
edge->vertex = this->vertices[triangleIndices[j]];
edge->previous = triangleEdges[(j + 2) % 3];
edge->next = triangleEdges[(j + 1) % 3];
edge->symmetric = nullptr;
// Point vertex to this edge
edge->vertex->edge = edge;
// Check for symmetry
std::pair<std::size_t, std::size_t> symmetricPair(triangleIndices[(j + 1) % 3], triangleIndices[j]);
std::map<std::pair<std::size_t, std::size_t>, Navmesh::Edge*>::iterator it = edgeMap.find(symmetricPair);
if (it == edgeMap.end())
{
// No symmetric edge found, insert this edge into the map
std::pair<std::size_t, std::size_t> pair(triangleIndices[j], triangleIndices[(j + 1) % 3]);
edgeMap[pair] = edge;
}
else
{
// Symmetric edge found, connect
edge->symmetric = it->second;
it->second->symmetric = edge;
}
}
// Set edge indices and add edges to the edge list
ab->index = edges.size();
edges.push_back(ab);
bc->index = edges.size();
edges.push_back(bc);
ca->index = edges.size();
edges.push_back(ca);
// Set triangle index and add triangle to the triangle list
triangle->index = currentTriangle;
triangles[currentTriangle++] = triangle;
}
calculateNormals();
return true;
}
void Navmesh::destroy()
{
for (std::size_t i = 0; i < vertices.size(); ++i)
delete vertices[i];
for (std::size_t i = 0; i < edges.size(); ++i)
delete edges[i];
for (std::size_t i = 0; i < triangles.size(); ++i)
delete triangles[i];
vertices.clear();
edges.clear();
triangles.clear();
}
bool Navmesh::loadOBJ(const std::string& filename)
{
// Open OBJ file
std::ifstream file(filename.c_str());
if (!file.is_open())
{
std::cerr << "Navmesh::loadOBJ(): Failed to open Wavefront OBJ file \"" << filename << "\"" << std::endl;
return false;
}
// Read OBJ file from file stream
if (!readOBJ(&file, filename))
{
std::cerr << "Navmesh::loadOBJ(): Failed to read Wavefront OBJ file \"" << filename << "\"" << std::endl;
file.close();
return false;
}
// Close OBJ file
file.close();
return true;
}
void Navmesh::traverse(Navmesh::Triangle* startTriangle, const Vector3& startPosition, const Vector3& startVelocity, std::vector<Navmesh::Step>* traversal)
{
// Form initial traversal step
Navmesh::Step step;
step.triangle = startTriangle;
step.start = normalize_barycentric(startPosition);
step.end = step.start;
step.edge = nullptr;
// Determine the maximum distance of the traversal
float maxDistance = glm::length(startVelocity);
// Set initial velocity
Vector3 velocity = startVelocity;
// Traverse navmesh
float distance = 0.0f;
while (distance < maxDistance)
{
// Grab triangle coordinates
const Vector3& a = step.triangle->edge->vertex->position;
const Vector3& b = step.triangle->edge->next->vertex->position;
const Vector3& c = step.triangle->edge->previous->vertex->position;
// Calculate target position
Vector3 cartesianStart = cartesian(step.start, a, b, c);
Vector3 target = cartesianStart + velocity;
// Find closest point on triangle to target position
closestPointOnTriangle(target, step.triangle, &step.end, &step.edge);
step.end = normalize_barycentric(step.end);
// Add step to the traversal
traversal->push_back(step);
// Determine distance traveled by the step
Vector3 cartesianEnd = cartesian(step.end, a, b, c);
distance += glm::length(cartesianEnd - cartesianStart);
// Check for no movement
if (cartesianEnd == cartesianStart)
{
/*
std::cout << "the same!\n";
if (step.edge == nullptr)
std::cout << "\tand no edge\n";
else if (step.edge->symmetric == nullptr)
{
std::cout << "\tand disconnected\n";
//step.edge = step.edge->previous;
}
//break;
*/
}
// Check if traversal is complete or edge is disconnected
if (step.edge == nullptr || step.edge->symmetric == nullptr)
{
break;
}
// Recalculate velocity
Quaternion rotation = glm::rotation(step.triangle->normal, step.edge->symmetric->triangle->normal);
velocity = glm::normalize(rotation * velocity) * (maxDistance - distance);
// Move to the next triangle
step.triangle = step.edge->symmetric->triangle;
// Calculate barycentric starting coordinates of the next step
step.start = normalize_barycentric(barycentric(cartesianEnd,
step.triangle->edge->vertex->position,
step.triangle->edge->next->vertex->position,
step.triangle->edge->previous->vertex->position));
step.end = step.start;
step.edge = nullptr;
}
/*
// Add triangle to visited list
visited->push_back(triangle);
// Grab triangle coordinates
const glm::vec3& a = triangle->edge->vertex->position;
const glm::vec3& b = triangle->edge->next->vertex->position;
const glm::vec3& c = triangle->edge->previous->vertex->position;
// Project target onto triangle
glm::vec3 closestPoint;
int edgeIndex = -1;
WingedEdge::Edge* closestEdge = nullptr;
project_on_triangle(target, a, b, c, &closestPoint, &edgeIndex);
*end = closestPoint;
// Determine if projected target is on an edge
switch (edgeIndex)
{
case -1:
// Projected target inside triangle
return;
case 0:
closestEdge = triangle->edge;
break;
case 1:
closestEdge = triangle->edge->next;
break;
case 2:
closestEdge = triangle->edge->previous;
break;
}
// If edge is not loose, repeat with connected triangle
if (closestEdge->symmetric != nullptr)
{
for (std::size_t i = 0; i < visited->size() - 1; ++i)
{
if ((*visited)[i] == closestEdge->symmetric->triangle)
return;
}
move(mesh, closestEdge->symmetric->triangle, closestPoint, target, visited, end);
}
*/
}
/*
if (steerCCW.isTriggered())
{
glm::quat rotation = glm::angleAxis(0.1f, navi.triangle->normal);
navi_forward = glm::normalize(rotation * navi_forward);
}
if (steerCW.isTriggered())
{
glm::quat rotation = glm::angleAxis(-0.1f, navi.triangle->normal);
navi_forward = glm::normalize(rotation * navi_forward);
}
if (navigate.isTriggered())
{
Mesh::Triangle* triangle = navi.triangle;
glm::vec3 start = navi.position;
glm::vec3 target = start + navi_forward * 0.02f;
std::vector<Mesh::Triangle*> visited;
glm::vec3 end;
move(&sceneManager.getTerrain()->mesh, triangle, start, target, &visited, &end);
Mesh::Triangle* end_triangle = visited[visited.size() - 1];
const glm::vec3& a = end_triangle->edge->vertex->position;
const glm::vec3& b = end_triangle->edge->next->vertex->position;
const glm::vec3& c = end_triangle->edge->previous->vertex->position;
glm::vec3 p = (a + b + c) / 3.0f;
const glm::vec3& n = end_triangle->normal;
glm::vec3 projected_start = project_on_plane(start, p, n);
// Calculate difference between positions
glm::vec3 difference = end - projected_start;
if (glm::dot(difference, difference) != 0.0f)
{
if (end_triangle != triangle)
{
glm::quat alignment = glm::rotation(triangle->normal, end_triangle->normal);
navi_forward = glm::normalize(alignment * navi_forward);
}
}
navi.position = end;
navi.triangle = visited[visited.size() - 1];
}
*/
void Navmesh::calculateNormals()
{
for (std::size_t i = 0; i < triangles.size(); ++i)
{
Navmesh::Triangle* triangle = triangles[i];
// Calculate surface normal
const Vector3& a = triangle->edge->vertex->position;
const Vector3& b = triangle->edge->next->vertex->position;
const Vector3& c = triangle->edge->previous->vertex->position;
Vector3 ba = b - a;
Vector3 ca = c - a;
triangle->normal = glm::normalize(glm::cross(ba, ca));
}
}
bool Navmesh::readOBJ(std::istream* stream, const std::string& filename)
{
std::string line;
std::vector<Vector3> vertices;
std::vector<std::size_t> indices;
while (stream->good() && std::getline(*stream, line))
{
// Tokenize line
std::vector<std::string> tokens;
std::string token;
std::istringstream linestream(line);
while (linestream >> token)
tokens.push_back(token);
// Skip empty lines and comments
if (tokens.empty() || tokens[0][0] == '#')
continue;
if (tokens[0] == "v")
{
if (tokens.size() != 4)
{
std::cerr << "Navmesh::readOBJ(): Invalid line \"" << line << "\" in file \"" << filename << "\"" << std::endl;
return false;
}
Vector3 vertex;
std::stringstream(tokens[1]) >> vertex.x;
std::stringstream(tokens[2]) >> vertex.y;
std::stringstream(tokens[3]) >> vertex.z;
vertices.push_back(vertex);
}
else if (tokens[0] == "f")
{
if (tokens.size() != 4)
{
std::cerr << "Navmesh::readOBJ(): Invalid line \"" << line << "\" in file \"" << filename << "\"" << std::endl;
return false;
}
std::size_t a, b, c;
std::stringstream(tokens[1]) >> a;
std::stringstream(tokens[2]) >> b;
std::stringstream(tokens[3]) >> c;
a -= 1;
b -= 1;
c -= 1;
indices.push_back(a);
indices.push_back(b);
indices.push_back(c);
}
}
return create(vertices, indices);
}
Vector3 Navmesh::barycentric(const Vector3& p, const Vector3& a, const Vector3& b, const Vector3& c)
{
Vector3 v0 = b - a;
Vector3 v1 = c - a;
Vector3 v2 = p - a;
float d00 = glm::dot(v0, v0);
float d01 = glm::dot(v0, v1);
float d11 = glm::dot(v1, v1);
float d20 = glm::dot(v2, v0);
float d21 = glm::dot(v2, v1);
float denom = d00 * d11 - d01 * d01;
Vector3 result;
result.y = (d11 * d20 - d01 * d21) / denom; // v
result.z = (d00 * d21 - d01 * d20) / denom; // w
result.x = 1.0f - result.y - result.z; // u
return result;
}
Vector3 Navmesh::cartesian(const Vector3& p, const Vector3& a, const Vector3& b, const Vector3& c)
{
return a * p.x + b * p.y + c * p.z;
}
// code taken from Detour's dtClosestPtPointTriangle
// @see https://github.com/recastnavigation/recastnavigation/blob/master/Detour/Source/DetourCommon.cpp
// (zlib license)
void Navmesh::closestPointOnTriangle(const Vector3& p, const Navmesh::Triangle* triangle, Vector3* closestPoint, Navmesh::Edge** closestEdge)
{
// Grab triangle coordinates
const Vector3& a = triangle->edge->vertex->position;
const Vector3& b = triangle->edge->next->vertex->position;
const Vector3& c = triangle->edge->previous->vertex->position;
// Check if P in vertex region outside A
Vector3 ab = b - a;
Vector3 ac = c - a;
Vector3 ap = p - a;
float d1 = glm::dot(ab, ap);
float d2 = glm::dot(ac, ap);
if (d1 <= 0.0f && d2 <= 0.0f)
{
// Barycentric coordinates (1, 0, 0)
*closestPoint = Vector3(1.0f, 0.0f, 0.0f);
*closestEdge = triangle->edge;
return;
}
// Check if P in vertex region outside B
Vector3 bp = p - b;
float d3 = glm::dot(ab, bp);
float d4 = glm::dot(ac, bp);
if (d3 >= 0.0f && d4 <= d3)
{
// Barycentric coordinates (0, 1, 0)
*closestPoint = Vector3(0.0f, 1.0f, 0.0f);
*closestEdge = triangle->edge->next;
return;
}
// Check if P in edge region of AB, if so return projection of P onto AB
float vc = d1 * d4 - d3 * d2;
if (vc <= 0.0f && d1 >= 0.0f && d3 <= 0.0f)
{
// barycentric coordinates (1-v,v,0)
float v = d1 / (d1 - d3);
*closestPoint = Vector3(1.0f - v, v, 0.0f);
*closestEdge = triangle->edge;
return;
}
// Check if P in vertex region outside C
Vector3 cp = p - c;
float d5 = glm::dot(ab, cp);
float d6 = glm::dot(ac, cp);
if (d6 >= 0.0f && d5 <= d6)
{
// Barycentric coordinates (0, 0, 1)
*closestPoint = Vector3(0.0f, 0.0f, 1.0f);
*closestEdge = triangle->edge->previous;
return;
}
// Check if P in edge region of AC, if so return projection of P onto AC
float vb = d5 * d2 - d1 * d6;
if (vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f)
{
// Barycentric coordinates (1 - w, 0, w)
float w = d2 / (d2 - d6);
*closestPoint = Vector3(1.0f - w, 0.0f, w);
*closestEdge = triangle->edge->previous;
return;
}
// Check if P in edge region of BC, if so return projection of P onto BC
float va = d3 * d6 - d5 * d4;
if (va <= 0.0f && (d4 - d3) >= 0.0f && (d5 - d6) >= 0.0f)
{
// Barycentric coordinates (0, 1 - w, w)
float w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
*closestPoint = Vector3(0.0f, 1.0f - w, w);
*closestEdge = triangle->edge->next;
return;
}
// P inside face region. Compute Q through its barycentric coordinates (u, v, w)
float denom = 1.0f / (va + vb + vc);
float v = vb * denom;
float w = vc * denom;
*closestPoint = Vector3(1.0f - v - w, v, w);
*closestEdge = nullptr;
}

+ 264
- 0
src/game/navmesh.hpp View File

@ -0,0 +1,264 @@
/*
* Copyright (C) 2017 Christopher J. Howard
*
* This file is part of Antkeeper Source Code.
*
* Antkeeper Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper Source Code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef NAVMESH_HPP
#define NAVMESH_HPP
#include <iostream>
#include <string>
#include <vector>
#include <emergent/emergent.hpp>
using namespace Emergent;
/**
* Navigation mesh represented by a half-edge structure.
*
* @ingroup geometry
*/
class Navmesh
{
public:
struct Vertex;
struct Edge;
struct Triangle;
struct Step;
/**
* Creates an instance of Navmesh.
*/
Navmesh();
/**
* Destroys an instance of Navmesh.
*/
~Navmesh();
/**
* Forms a navmesh from a list of vertices and indices.
*
* @param vertices Specifies a list of vertices.
* @param indices Specifies a list of indices.
* @return `true` if the navmesh was successfully created, `false` otherwise.
*/
bool create(const std::vector<Vector3>& vertices, const std::vector<std::size_t>& indices);
/**
* Destroys the navmesh.
*/
void destroy();
/**
* Loads this navmesh from a triangulated Wavefront OBJ file. This method only supported **triangulated** Wavefront OBJ files. The supported commands are `v`, `f` and comment lines beginning with `#`.
*
* @param filename Path to the Wavefront OBJ file.
* @return `true` if the navmesh was successfully loaded from the OBJ file, `false` otherwise.
*/
bool loadOBJ(const std::string& filename);
/**
* Traverses the navmesh.
*
* @param[in] startTriangle Initial triangle
* @param[in] startPosition Initial barycentric coordinates on the start triangle
* @param[in] startVelocity Initial cartesian velocity vector
* @param[out] traversal Traversal information
*/
static void traverse(Navmesh::Triangle* startTriangle, const Vector3& startPosition, const Vector3& startVelocity, std::vector<Navmesh::Step>* traversal);
/// Returns a pointer to the navmesh vertices
const std::vector<Navmesh::Vertex*>* getVertices() const;
/// Returns a pointer to the navmesh edges
const std::vector<Navmesh::Edge*>* getEdges() const;
/// Returns a pointer to the navmesh triangles
const std::vector<Navmesh::Triangle*>* getTriangles() const;
/// @copydoc Navmesh::getVertices() const
std::vector<Navmesh::Vertex*>* getVertices();
/// @copydoc Navmesh::getEdges() const
std::vector<Navmesh::Edge*>* getEdges();
/// @copydoc Navmesh::getTriangles() const
std::vector<Navmesh::Triangle*>* getTriangles();
/**
* Half-edge vertex which contains a pointer to its parent edge, a position vector, and an index.
*/
struct Vertex
{
/// Pointer to the edge to which this vertex belongs
Navmesh::Edge* edge;
/// Vertex position vector
Vector3 position;
/// Vertex flags
unsigned char flags;
/// Index of this vertex
std::size_t index;
};
/**
* Half-edge edge which contains pointers to its starting vertex, parent triangle, and related edges.
*/
struct Edge
{
/// Pointer to the vertex at which the edge starts
Navmesh::Vertex* vertex;
/// Pointer to the triangle to which this edge belongs
Navmesh::Triangle* triangle;
/// Pointer to the previous edge in the parent triangle
Navmesh::Edge* previous;
/// Pointer to the next edge in the parent triangle
Navmesh::Edge* next;
/// Pointer to the symmetric edge
Navmesh::Edge* symmetric;
/// Edge flags
unsigned char flags;
/// Index of this edge
std::size_t index;
};
/**
* Half-edge triangle which contains a pointer to its first edge and its normal vector.
*/
struct Triangle
{
/// Pointer to the first edge in this triangle
Navmesh::Edge* edge;
/// Faceted surface normal
Vector3 normal;
/// Triangle flags
unsigned char flags;
/// Index of this triangle
std::size_t index;
};
/**
* Contains informations about a single step in a navmesh traversal operation.
*/
struct Step
{
/// Pointer to the triangle on which the step occured
Triangle* triangle;
/// Barycentric coordinates of the step's starting position
Vector3 start;
/// Barycentric coordinates of the step's ending position
Vector3 end;
/// Pointer to the edge on which the step exited the triangle, or `nullptr` if the step is within the triangle
Edge* edge;
};
private:
/**
* Calculates the faceted surface normals for each triangle.
*/
void calculateNormals();
/**
* Reads Wavefront OBJ data from an input stream
*
* @param stream Input stream containing OBJ data
* @param filename Path to the OBJ file
* @return `true` if OBJ data was successfully read from the file.
*/
bool readOBJ(std::istream* stream, const std::string& filename);
/**
* Calculates barycentric coordinates from cartesian coordinates.
*
* @param p Cartesian point
* @param a First vertex in triangle
* @param b Second vertex in triangle
* @param c Third vertex in triangle
*/
static Vector3 barycentric(const Vector3& p, const Vector3& a, const Vector3& b, const Vector3& c);
/**
* Calculates cartesian coordinates from barycentric coordinates.
*
* @param p Barycentric point
* @param a First vertex in triangle
* @param b Second vertex in triangle
* @param c Third vertex in triangle
*/
static Vector3 cartesian(const Vector3& p, const Vector3& a, const Vector3& b, const Vector3& c);
/**
* Finds the closest point on a triangle.
*
* @param[in] p Point to project
* @param[in] triangle Triangle on which to find the closest point
* @param[out] closest Closest point on triangle
* @param[out] edge Edge on which the closest point is located, or `nullptr` if the closest point is not on an edge.
*/
static void closestPointOnTriangle(const Vector3& p, const Navmesh::Triangle* triangle, Vector3* closestPoint, Navmesh::Edge** closestEdge);
std::vector<Navmesh::Vertex*> vertices;
std::vector<Navmesh::Edge*> edges;
std::vector<Navmesh::Triangle*> triangles;
};
inline const std::vector<Navmesh::Vertex*>* Navmesh::getVertices() const
{
return &vertices;
}
inline const std::vector<Navmesh::Edge*>* Navmesh::getEdges() const
{
return &edges;
}
inline const std::vector<Navmesh::Triangle*>* Navmesh::getTriangles() const
{
return &triangles;
}
inline std::vector<Navmesh::Vertex*>* Navmesh::getVertices()
{
return &vertices;
}
inline std::vector<Navmesh::Edge*>* Navmesh::getEdges()
{
return &edges;
}
inline std::vector<Navmesh::Triangle*>* Navmesh::getTriangles()
{
return &triangles;
}
#endif // NAVMESH_HPP

src/nest.cpp → src/game/nest.cpp View File


src/nest.hpp → src/game/nest.hpp View File


src/terrain.cpp → src/game/terrain.cpp View File


src/terrain.hpp → src/game/terrain.hpp View File


+ 10
- 5
src/material-loader.cpp View File

@ -23,7 +23,12 @@
#include <vector>
MaterialLoader::MaterialLoader()
{}
{
textureLoader.setGamma(1.0f);
textureLoader.setCubemap(false);
textureLoader.setMipmapChain(false);
textureLoader.setMaxAnisotropy(16.0f);
}
MaterialLoader::~MaterialLoader()
{
@ -166,13 +171,13 @@ Texture* MaterialLoader::loadTexture(const std::string& filename)
return it->second;
}
// Load texture
std::string fullFilename = std::string("data/textures/") + filename;
Texture* texture = new Texture();
if (!texture->load(fullFilename))
// Load texture
Texture* texture = textureLoader.load(fullFilename);
if (!texture)
{
std::cerr << "MaterialLoader::loadTexture(): Failed to load texture file \"" << fullFilename << "\"" << std::endl;
delete texture;
return nullptr;
}

+ 1
- 0
src/material-loader.hpp View File

@ -36,6 +36,7 @@ private:
Texture* loadTexture(const std::string& filename);
std::map<std::string, Texture*> textureCache;
std::map<std::string, PhysicalMaterial*> materialCache;
TextureLoader textureLoader;
};
#endif // MATERIAL_LOADER_HPP

+ 210
- 82
src/render-passes.cpp View File

@ -23,11 +23,13 @@
ShadowMapRenderPass::ShadowMapRenderPass():
depthShader(nullptr)
{}
{
modelViewProjectionParam = parameterSet.addParameter("modelViewProjection", ShaderParameter::Type::MATRIX_4, 1);
}
bool ShadowMapRenderPass::load(const RenderContext* renderContext)
{
depthShader = shaderLoader.load("data/shaders/depth-pass.glsl");
depthShader = shaderLoader.load("data/shaders/depth-pass.glsl", &parameterSet);
if (!depthShader)
{
return false;
@ -66,7 +68,6 @@ void ShadowMapRenderPass::render(const RenderContext* renderContext)
// Bind shader
depthShader->bind();
ShaderParameterSet* parameters = depthShader->getParameters();
const Camera& camera = *(renderContext->camera);
const std::list<RenderOperation>* operations = renderContext->queue->getOperations();
@ -89,8 +90,8 @@ void ShadowMapRenderPass::render(const RenderContext* renderContext)
}
const Matrix4& modelMatrix = operation.transform;
Matrix4 modelViewProjectionMatrix = camera.getViewProjection() * modelMatrix;
parameters->setValue(ShaderParameter::MODEL_VIEW_PROJECTION_MATRIX, modelViewProjectionMatrix);
Matrix4 modelViewProjectionMatrix = camera.getViewProjection() * modelMatrix;
depthShader->setParameter(modelViewProjectionParam, modelViewProjectionMatrix);
glBindVertexArray(operation.vao);
glDrawElementsBaseVertex(GL_TRIANGLES, operation.triangleCount * 3, GL_UNSIGNED_INT, (void*)0, operation.indexOffset);
@ -101,14 +102,18 @@ void ShadowMapRenderPass::render(const RenderContext* renderContext)
ClippingRenderPass::ClippingRenderPass():
shader(nullptr)
{}
{
clippingPlanesParam = parameterSet.addParameter("clippingPlanes", ShaderParameter::Type::VECTOR_4, 1);
modelParam = parameterSet.addParameter("modelMatrix", ShaderParameter::Type::MATRIX_4, 1);
modelViewProjectionParam = parameterSet.addParameter("modelViewProjectionMatrix", ShaderParameter::Type::MATRIX_4, 1);
}
bool ClippingRenderPass::load(const RenderContext* renderContext)
{
shaderLoader.undefine();
shaderLoader.define("CLIPPING_PLANE_COUNT", 1);
shader = shaderLoader.load("data/shaders/clip.glsl");
shader = shaderLoader.load("data/shaders/clip.glsl", &parameterSet);
if (!shader)
{
return false;
@ -133,10 +138,9 @@ void ClippingRenderPass::render(const RenderContext* renderContext)
// Bind shader
shader->bind();
ShaderParameterSet* parameters = shader->getParameters();
// Pass clipping planes to shader
parameters->setValue(ShaderParameter::CLIPPING_PLANES, clippingPlane);
shader->setParameter(clippingPlanesParam, clippingPlane);
// Grab render context parameters
const Camera& camera = *(renderContext->camera);
@ -163,8 +167,8 @@ void ClippingRenderPass::render(const RenderContext* renderContext)
{
const Matrix4& modelMatrix = operation.transform;
Matrix4 modelViewProjectionMatrix = camera.getViewProjection() * modelMatrix;
parameters->setValue(ShaderParameter::MODEL_MATRIX, modelMatrix);
parameters->setValue(ShaderParameter::MODEL_VIEW_PROJECTION_MATRIX, modelViewProjectionMatrix);
shader->setParameter(modelParam, modelMatrix);
shader->setParameter(modelViewProjectionParam, modelViewProjectionMatrix);
glBindVertexArray(operation.vao);
glDrawElementsBaseVertex(GL_TRIANGLES, operation.triangleCount * 3, GL_UNSIGNED_INT, (void*)0, operation.indexOffset);
@ -185,23 +189,28 @@ void ClippingRenderPass::setClippingPlane(const Plane& plane)
CappingRenderPass::CappingRenderPass():
shader(nullptr)
{}
{
horizonTexturesParam = parameterSet.addParameter("horizonTextures", ShaderParameter::Type::INT, 4);
modelParam = parameterSet.addParameter("modelMatrix", ShaderParameter::Type::MATRIX_4, 1);
modelViewProjectionParam = parameterSet.addParameter("modelViewProjectionMatrix", ShaderParameter::Type::MATRIX_4, 1);
}
bool CappingRenderPass::load(const RenderContext* renderContext)
{
shaderLoader.undefine();
shaderLoader.define("TEXTURE_COUNT", 4);
shader = shaderLoader.load("data/shaders/soil-profile.glsl");
shader = shaderLoader.load("data/shaders/soil-profile.glsl", &parameterSet);
if (!shader)
{
return false;
}
horizonOTexture.load("data/textures/horizon-o.png");
horizonATexture.load("data/textures/horizon-a.png");
horizonBTexture.load("data/textures/horizon-b.png");
horizonCTexture.load("data/textures/horizon-c.png");
TextureLoader textureLoader;
horizonOTexture = textureLoader.load("data/textures/horizon-o.png");
horizonATexture = textureLoader.load("data/textures/horizon-a.png");
horizonBTexture = textureLoader.load("data/textures/horizon-b.png");
horizonCTexture = textureLoader.load("data/textures/horizon-c.png");
return true;
}
@ -211,10 +220,15 @@ void CappingRenderPass::unload()
delete shader;
shader = nullptr;
horizonOTexture.destroy();
horizonATexture.destroy();
horizonBTexture.destroy();
horizonCTexture.destroy();
delete horizonOTexture;
delete horizonATexture;
delete horizonBTexture;
delete horizonCTexture;
horizonOTexture = nullptr;
horizonATexture = nullptr;
horizonBTexture = nullptr;
horizonCTexture = nullptr;
}
void CappingRenderPass::render(const RenderContext* renderContext)
@ -225,22 +239,21 @@ void CappingRenderPass::render(const RenderContext* renderContext)
// Bind shader
shader->bind();
ShaderParameterSet* parameters = shader->getParameters();
// Bind texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, horizonOTexture.getTextureID());
glBindTexture(GL_TEXTURE_2D, horizonOTexture->getTextureID());
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, horizonATexture.getTextureID());
glBindTexture(GL_TEXTURE_2D, horizonATexture->getTextureID());
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, horizonBTexture.getTextureID());
glBindTexture(GL_TEXTURE_2D, horizonBTexture->getTextureID());
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, horizonCTexture.getTextureID());
glBindTexture(GL_TEXTURE_2D, horizonCTexture->getTextureID());
// Pass texture units to shader
int textureUnits[] = {0, 1, 2, 3};
parameters->setValue(ShaderParameter::MATERIAL_TEXTURE, 0, &textureUnits[0], 4);
shader->setParameter(horizonTexturesParam, 0, &textureUnits[0], 4);
const Camera& camera = *(renderContext->camera);
const std::list<RenderOperation>* operations = renderContext->queue->getOperations();
@ -249,8 +262,8 @@ void CappingRenderPass::render(const RenderContext* renderContext)
{
const Matrix4& modelMatrix = operation.transform;
Matrix4 modelViewProjectionMatrix = camera.getViewProjection() * modelMatrix;
parameters->setValue(ShaderParameter::MODEL_MATRIX, modelMatrix);
parameters->setValue(ShaderParameter::MODEL_VIEW_PROJECTION_MATRIX, modelViewProjectionMatrix);
shader->setParameter(modelParam, modelMatrix);
shader->setParameter(modelViewProjectionParam, modelViewProjectionMatrix);
glBindVertexArray(operation.vao);
glDrawElementsBaseVertex(GL_TRIANGLES, operation.triangleCount * 3, GL_UNSIGNED_INT, (void*)0, operation.indexOffset);
@ -262,7 +275,10 @@ void CappingRenderPass::render(const RenderContext* renderContext)
LightingRenderPass::LightingRenderPass():
shadowMap(0),
shadowCamera(nullptr)
shadowCamera(nullptr),
modelLoader(nullptr),
treeShadow(nullptr),
diffuseCubemap(nullptr)
{
// Initialize bias matrix for calculating the model-view-projection-bias matrix (used for shadow map texture coordinate calculation)
biasMatrix = Matrix4(
@ -271,11 +287,24 @@ LightingRenderPass::LightingRenderPass():
0.0f, 0.0f, 0.5f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f);
modelLoader.setMaterialLoader(&materialLoader);
modelParam = parameterSet.addParameter("modelMatrix", ShaderParameter::Type::MATRIX_4, 1);
modelViewParam = parameterSet.addParameter("modelViewMatrix", ShaderParameter::Type::MATRIX_4, 1);
modelViewProjectionParam = parameterSet.addParameter("modelViewProjectionMatrix", ShaderParameter::Type::MATRIX_4, 1);
normalModelViewParam = parameterSet.addParameter("normalModelViewMatrix", ShaderParameter::Type::MATRIX_3, 1);
normalModelParam = parameterSet.addParameter("normalModelMatrix", ShaderParameter::Type::MATRIX_3, 1);
cameraPositionParam = parameterSet.addParameter("cameraPosition", ShaderParameter::Type::VECTOR_3, 1);
directionalLightCountParam = parameterSet.addParameter("directionalLightCount", ShaderParameter::Type::INT, 1);
directionalLightColorsParam = parameterSet.addParameter("directionalLightColors", ShaderParameter::Type::VECTOR_3, 1);
directionalLightDirectionsParam = parameterSet.addParameter("directionalLightDirections", ShaderParameter::Type::VECTOR_3, 1);
albedoOpacityMapParam = parameterSet.addParameter("albedoOpacityMap", ShaderParameter::Type::INT, 1);
metalnessRoughnessMapParam = parameterSet.addParameter("metalnessRoughness", ShaderParameter::Type::INT, 1);
normalOcclusionMapParam = parameterSet.addParameter("normalOcclusionMap", ShaderParameter::Type::INT, 1);
diffuseCubemapParam = parameterSet.addParameter("diffuseCubemap", ShaderParameter::Type::INT, 1);
specularCubemapParam = parameterSet.addParameter("specularCubemap", ShaderParameter::Type::INT, 1);
}
bool LightingRenderPass::load(const RenderContext* renderContext)
{
{
// For each render operation
/*
if (renderContext != nullptr)
@ -289,13 +318,32 @@ bool LightingRenderPass::load(const RenderContext* renderContext)
*/
// Load tree shadow
if (!treeShadow.load("data/textures/tree-shadow-0.png"))
TextureLoader textureLoader;
treeShadow = textureLoader.load("data/textures/tree-shadow-0.png");
if (!treeShadow)
{
std::cerr << "Failed to load tree shadow" << std::endl;
}
// Load cubemap
textureLoader.setCubemap(true);
textureLoader.setMipmapChain(false);
diffuseCubemap = textureLoader.load("data/textures/galileo-diffuse.png");
if (!diffuseCubemap)
{
std::cerr << "Failed to load cubemap" << std::endl;
}
textureLoader.setCubemap(true);
textureLoader.setMipmapChain(true);
specularCubemap = textureLoader.load("data/textures/galileo-specular_m%02d.png");
if (!specularCubemap)
{
std::cerr << "Failed to load cubemap" << std::endl;
}
// Load unit plane
unitPlaneModel = modelLoader.load("data/models/unit-plane.mdl");
unitPlaneModel = modelLoader->load("data/models/unit-plane.mdl");
if (!unitPlaneModel)
{
std::cout << "Failed to load unit plane" << std::endl;
@ -327,15 +375,16 @@ bool LightingRenderPass::load(const RenderContext* renderContext)
shaderLoader.define("TEXTURE_COUNT", 0);
shaderLoader.define("VERTEX_POSITION", EMERGENT_VERTEX_POSITION);
shaderLoader.define("VERTEX_NORMAL", EMERGENT_VERTEX_NORMAL);
shaderLoader.define("VERTEX_TEXCOORD", EMERGENT_VERTEX_TEXCOORD);
lightingShader = shaderLoader.load("data/shaders/lit-object.glsl");
lightingShader = shaderLoader.load("data/shaders/lit-object.glsl", &parameterSet);
if (!lightingShader)
{
return false;
}
time = 0.0f;
return true;
}
@ -352,6 +401,15 @@ void LightingRenderPass::unload()
delete it->second;
}
shaderCache.clear();
delete treeShadow;
treeShadow = nullptr;
delete diffuseCubemap;
diffuseCubemap = nullptr;
delete specularCubemap;
specularCubemap = nullptr;
}
void LightingRenderPass::render(const RenderContext* renderContext)
@ -757,30 +815,96 @@ void LightingRenderPass::render(const RenderContext* renderContext)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Bind shader
lightingShader->bind();
ShaderParameterSet* parameters = lightingShader->getParameters();
Shader* shader = lightingShader;
shader->bind();
// Pass texture units to shader
shader->setParameter(albedoOpacityMapParam, 0);
shader->setParameter(normalOcclusionMapParam, 2);
int directionalLightCount = 1;
Vector3 directionalLightColor[3];
Vector3 directionalLightDirection[3];
directionalLightColor[0] = Vector3(1);
directionalLightDirection[0] = glm::normalize(Vector3(camera.getView() * -Vector4(0, 0, -1, 0)));
parameters->setValue(ShaderParameter::DIRECTIONAL_LIGHT_COLOR, 0, &directionalLightColor[0], directionalLightCount);
parameters->setValue(ShaderParameter::DIRECTIONAL_LIGHT_DIRECTION, 0, &directionalLightDirection[0], directionalLightCount);
Vector3 directionalLightColors[3];
Vector3 directionalLightDirections[3];
directionalLightColors[0] = Vector3(1);
directionalLightDirections[0] = glm::normalize(Vector3(camera.getView() * -Vector4(0, 0, -1, 0)));
shader->setParameter(directionalLightCountParam, directionalLightCount);
shader->setParameter(directionalLightColorsParam, 0, &directionalLightColors[0], directionalLightCount);
shader->setParameter(directionalLightDirectionsParam, 0, &directionalLightDirections[0], directionalLightCount);
shader->setParameter(cameraPositionParam, camera.getTranslation());
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_CUBE_MAP, diffuseCubemap->getTextureID());
shader->setParameter(diffuseCubemapParam, 3);
glActiveTexture(GL_TEXTURE4);
glBindTexture(GL_TEXTURE_CUBE_MAP, specularCubemap->getTextureID());
shader->setParameter(specularCubemapParam, 4);
Texture* albedoOpacityMap = nullptr;
Texture* metalnessRoughnessMap = nullptr;
Texture* normalOcclusionMap = nullptr;
// Render operations
for (const RenderOperation& operation: *operations)
{
{
// Skip render operations with unsupported materials
if (operation.material->getMaterialFormatID() != static_cast<unsigned int>(MaterialFormat::PHYSICAL))
{
continue;
}
const PhysicalMaterial* material = static_cast<const PhysicalMaterial*>(operation.material);
// Skip render operations with unsupported vertex formats
// Bind albedo-opacity map
if (material->albedoOpacityMap != albedoOpacityMap)
{
albedoOpacityMap = material->albedoOpacityMap;
if (albedoOpacityMap != nullptr)
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, albedoOpacityMap->getTextureID());
}
}
// Bind metalness-roughness map
if (material->metalnessRoughnessMap != metalnessRoughnessMap)
{
metalnessRoughnessMap = material->metalnessRoughnessMap;
if (metalnessRoughnessMap != nullptr)
{
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, metalnessRoughnessMap->getTextureID());
}
}
// Bind normal-occlusion map
if (material->normalOcclusionMap != normalOcclusionMap)
{
normalOcclusionMap = material->normalOcclusionMap;
if (normalOcclusionMap != nullptr)
{
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, normalOcclusionMap->getTextureID());
}
}
const Matrix4& modelMatrix = operation.transform;
Matrix4 modelViewMatrix = camera.getView() * modelMatrix;
Matrix4 modelViewProjectionMatrix = camera.getViewProjection() * modelMatrix;
Matrix3 normalModelViewMatrix = glm::transpose(glm::inverse(Matrix3(modelViewMatrix)));
Matrix3 normalModelMatrix = glm::transpose(glm::inverse(Matrix3(modelMatrix)));
parameters->setValue(ShaderParameter::MODEL_VIEW_MATRIX, modelViewMatrix);
parameters->setValue(ShaderParameter::MODEL_VIEW_PROJECTION_MATRIX, modelViewProjectionMatrix);
parameters->setValue(ShaderParameter::NORMAL_MODEL_VIEW_MATRIX, normalModelViewMatrix);
shader->setParameter(modelParam, modelMatrix);
shader->setParameter(modelViewParam, modelViewMatrix);
shader->setParameter(modelViewProjectionParam, modelViewProjectionMatrix);
shader->setParameter(normalModelViewParam, normalModelViewMatrix);
shader->setParameter(normalModelParam, normalModelMatrix);
glBindVertexArray(operation.vao);
glDrawElementsBaseVertex(GL_TRIANGLES, operation.triangleCount * 3, GL_UNSIGNED_INT, (void*)0, operation.indexOffset);
@ -853,9 +977,14 @@ bool LightingRenderPass::loadShader(const RenderOperation& operation)
return false;
}
DebugRenderPass::DebugRenderPass()
{
modelViewProjectionParam = parameterSet.addParameter("modelViewProjectionMatrix", ShaderParameter::Type::MATRIX_4, 1);
}
bool DebugRenderPass::load(const RenderContext* renderContext)
{
unlitSolidShader = shaderLoader.load("data/shaders/unlit-solid.glsl");
unlitSolidShader = shaderLoader.load("data/shaders/unlit-solid.glsl", &parameterSet);
if (!unlitSolidShader)
{
return false;
@ -934,7 +1063,6 @@ void DebugRenderPass::render(const RenderContext* renderContext)
// Bind unlit solid shader
unlitSolidShader->bind();
ShaderParameterSet* parameters = unlitSolidShader->getParameters();
// Bind AABB geometry
glBindVertexArray(aabbVAO);
@ -955,12 +1083,20 @@ void DebugRenderPass::render(const RenderContext* renderContext)
Matrix4 modelMatrix = glm::translate(translation) * glm::scale(scale);
Matrix4 modelViewProjectionMatrix = camera.getViewProjection() * modelMatrix;
parameters->setValue(ShaderParameter::MODEL_VIEW_PROJECTION_MATRIX, modelViewProjectionMatrix);
unlitSolidShader->setParameter(modelViewProjectionParam, modelViewProjectionMatrix);
glDrawElements(GL_LINES, aabbIndexCount, GL_UNSIGNED_INT, (void*)0);
}
}
UIRenderPass::UIRenderPass()
{
modelViewProjectionParam = parameterSet.addParameter("modelViewProjectionMatrix", ShaderParameter::Type::MATRIX_4, 1);
textureParam = parameterSet.addParameter("tex", ShaderParameter::Type::INT, 1);
texcoordOffsetParam = parameterSet.addParameter("texcoordOffset", ShaderParameter::Type::VECTOR_2, 1);
texcoordScaleParam = parameterSet.addParameter("texcoordScale", ShaderParameter::Type::VECTOR_2, 1);
}
bool UIRenderPass::load(const RenderContext* renderContext)
{
shaderLoader.define("VERTEX_POSITION", EMERGENT_VERTEX_POSITION);
@ -969,14 +1105,14 @@ bool UIRenderPass::load(const RenderContext* renderContext)
shaderLoader.define("GAMMA_CORRECT");
shaderLoader.define("TEXTURE_COUNT", 1);
texturedUIShader = shaderLoader.load("data/shaders/ui.glsl");
texturedUIShader = shaderLoader.load("data/shaders/ui.glsl", &parameterSet);
shaderLoader.undefine();
shaderLoader.define("VERTEX_POSITION", EMERGENT_VERTEX_POSITION);
shaderLoader.define("VERTEX_COLOR", EMERGENT_VERTEX_COLOR);
shaderLoader.define("GAMMA_CORRECT");
untexturedUIShader = shaderLoader.load("data/shaders/ui.glsl");
untexturedUIShader = shaderLoader.load("data/shaders/ui.glsl", &parameterSet);
if (!texturedUIShader || !untexturedUIShader)
{
@ -1017,8 +1153,8 @@ void UIRenderPass::render(const RenderContext* renderContext)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glActiveTexture(GL_TEXTURE0);
ShaderParameterSet* parameters;
Shader* shader = nullptr;
// Render operations
const std::list<RenderOperation>* operations = renderContext->queue->getOperations();
@ -1034,34 +1170,25 @@ void UIRenderPass::render(const RenderContext* renderContext)
if (material->texture != nullptr)
{
// Bind textured UI shader
texturedUIShader->bind();
parameters = texturedUIShader->getParameters();
parameters->setValue(ShaderParameter::MATERIAL_TEXTURE, 0);
shader = texturedUIShader;
shader->bind();
shader->setParameter(textureParam, 0);
shader->setParameter(texcoordOffsetParam, Vector2(0.0f));
shader->setParameter(texcoordScaleParam, Vector2(1.0f));
glBindTexture(GL_TEXTURE_2D, material->texture->getTextureID());
//parameters->setValue(ShaderParameter::TEXCOORD_OFFSET, Vector2(texture->getCoordinateOffset()));
//parameters->setValue(ShaderParameter::TEXCOORD_SCALE, Vector2(texture->getCoordinateScale()));
parameters->setValue(ShaderParameter::TEXCOORD_OFFSET, Vector2(0.0f));
parameters->setValue(ShaderParameter::TEXCOORD_SCALE, Vector2(1.0f));
}
else
{
// Bind untextured UI shader
untexturedUIShader->bind();
parameters = untexturedUIShader->getParameters();
shader = untexturedUIShader;
shader->bind();
}
parameters->setValue(ShaderParameter::MATERIAL_DIFFUSE_COLOR, Vector3(1.0f));
parameters->setValue(ShaderParameter::MATERIAL_OPACITY, 1.0f);
const Matrix4& modelMatrix = operation.transform;
Matrix4 modelViewProjectionMatrix = camera.getViewProjection() * modelMatrix;
// Pass matrix parameters
parameters->setValue(ShaderParameter::MODEL_VIEW_PROJECTION_MATRIX, modelViewProjectionMatrix);
shader->setParameter(modelViewProjectionParam, modelViewProjectionMatrix);
// Draw geometry
glBindVertexArray(operation.vao);
@ -1071,7 +1198,10 @@ void UIRenderPass::render(const RenderContext* renderContext)
VignetteRenderPass::VignetteRenderPass():
shader(nullptr)
{}
{
bayerTextureParam = parameterSet.addParameter("bayerTexture", ShaderParameter::Type::INT, 1);
modelViewProjectionParam = parameterSet.addParameter("modelViewProjectionMatrix", ShaderParameter::Type::MATRIX_4, 1);
}
bool VignetteRenderPass::load(const RenderContext* renderContext)
{
@ -1080,7 +1210,7 @@ bool VignetteRenderPass::load(const RenderContext* renderContext)
shaderLoader.define("VERTEX_COLOR", EMERGENT_VERTEX_COLOR);
shaderLoader.define("TEXTURE_COUNT", 1);
shader = shaderLoader.load("data/shaders/vignette.glsl");
shader = shaderLoader.load("data/shaders/vignette.glsl", &parameterSet);
if (!shader)
{
return false;
@ -1106,7 +1236,6 @@ bool VignetteRenderPass::load(const RenderContext* renderContext)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, 8, 8, 0, GL_RED, GL_UNSIGNED_BYTE, pattern);
return true;
}
@ -1126,14 +1255,13 @@ void VignetteRenderPass::render(const RenderContext* renderContext)
// Bind shader
shader->bind();
ShaderParameterSet* parameters = shader->getParameters();
// Bind texture
glActiveTexture(0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, bayerTextureID);
// Pass texture unit to shader
parameters->setValue(ShaderParameter::MATERIAL_TEXTURE, 0);
shader->setParameter(bayerTextureParam, 0);
const Camera& camera = *(renderContext->camera);
const std::list<RenderOperation>* operations = renderContext->queue->getOperations();
@ -1145,7 +1273,7 @@ void VignetteRenderPass::render(const RenderContext* renderContext)
const Matrix4& modelMatrix = operation.transform;
Matrix4 modelViewProjectionMatrix = camera.getViewProjection() * modelMatrix;
parameters->setValue(ShaderParameter::MODEL_VIEW_PROJECTION_MATRIX, modelViewProjectionMatrix);
shader->setParameter(modelViewProjectionParam, modelViewProjectionMatrix);
glBindVertexArray(operation.vao);
glDrawElementsBaseVertex(GL_TRIANGLES, operation.triangleCount * 3, GL_UNSIGNED_INT, (void*)0, operation.indexOffset);

+ 54
- 7
src/render-passes.hpp View File

@ -39,6 +39,9 @@ public:
virtual void render(const RenderContext* renderContext);
private:
ShaderParameterSet parameterSet;
const ShaderParameter* modelViewProjectionParam;
ShaderLoader shaderLoader;
Shader* depthShader;
};
@ -57,6 +60,11 @@ public:
void setClippingPlane(const Plane& plane);
private:
ShaderParameterSet parameterSet;
const ShaderParameter* modelParam;
const ShaderParameter* modelViewProjectionParam;
const ShaderParameter* clippingPlanesParam;
ShaderLoader shaderLoader;
Shader* shader;
Vector4 clippingPlane;
@ -74,13 +82,18 @@ public:
virtual void render(const RenderContext* renderContext);
private:
ShaderParameterSet parameterSet;
const ShaderParameter* modelParam;
const ShaderParameter* modelViewProjectionParam;
const ShaderParameter* horizonTexturesParam;
ShaderLoader shaderLoader;
Shader* shader;
Texture horizonOTexture;
Texture horizonATexture;
Texture horizonBTexture;
Texture horizonCTexture;
Texture* horizonOTexture;
Texture* horizonATexture;
Texture* horizonBTexture;
Texture* horizonCTexture;
};
@ -98,6 +111,7 @@ public:
inline void setShadowMap(GLuint shadowMap) { this->shadowMap = shadowMap; }
inline void setShadowCamera(const Camera* camera) { this->shadowCamera = camera; }
inline void setModelLoader(ModelLoader* modelLoader) { this->modelLoader = modelLoader; }
inline void setClippingPlanes(const Plane* planes)
{
@ -109,13 +123,32 @@ public:
private:
bool loadShader(const RenderOperation& operation);
ShaderParameterSet parameterSet;
const ShaderParameter* modelParam;
const ShaderParameter* modelViewParam;
const ShaderParameter* modelViewProjectionParam;
const ShaderParameter* normalModelViewParam;
const ShaderParameter* normalModelParam;
const ShaderParameter* cameraPositionParam;
const ShaderParameter* directionalLightCountParam;
const ShaderParameter* directionalLightColorsParam;
const ShaderParameter* directionalLightDirectionsParam;
const ShaderParameter* albedoOpacityMapParam;
const ShaderParameter* metalnessRoughnessMapParam;
const ShaderParameter* normalOcclusionMapParam;
const ShaderParameter* diffuseCubemapParam;
const ShaderParameter* specularCubemapParam;
ShaderLoader shaderLoader;
std::map<std::size_t, Shader*> shaderCache;
Shader* lightingShader;
Matrix4 biasMatrix;
GLuint shadowMap;
Texture treeShadow;
Texture* treeShadow;
Texture* diffuseCubemap;
Texture* specularCubemap;
const Camera* shadowCamera;
float time;
@ -128,8 +161,7 @@ private:
ClippingRenderPass clippingRenderPass;
CappingRenderPass cappingRenderPass;
MaterialLoader materialLoader;
ModelLoader modelLoader;
ModelLoader* modelLoader;
};
/**
@ -138,11 +170,15 @@ private:
class DebugRenderPass: public RenderPass
{
public:
DebugRenderPass();
virtual bool load(const RenderContext* renderContext);
virtual void unload();
virtual void render(const RenderContext* renderContext);
private:
ShaderParameterSet parameterSet;
const ShaderParameter* modelViewProjectionParam;
ShaderLoader shaderLoader;
Shader* unlitSolidShader;
@ -159,11 +195,18 @@ private:
class UIRenderPass: public RenderPass
{
public:
UIRenderPass();
virtual bool load(const RenderContext* renderContext);
virtual void unload();
virtual void render(const RenderContext* renderContext);
private:
ShaderParameterSet parameterSet;
const ShaderParameter* modelViewProjectionParam;
const ShaderParameter* textureParam;
const ShaderParameter* texcoordOffsetParam;
const ShaderParameter* texcoordScaleParam;
ShaderLoader shaderLoader;
Shader* texturedUIShader;
Shader* untexturedUIShader;
@ -181,6 +224,10 @@ public:
virtual void render(const RenderContext* renderContext);
private:
ShaderParameterSet parameterSet;
const ShaderParameter* modelViewProjectionParam;
const ShaderParameter* bayerTextureParam;
ShaderLoader shaderLoader;
Shader* shader;
GLuint bayerTextureID;

+ 2
- 2
src/states/experiment-state.hpp View File

@ -23,8 +23,8 @@
#include "../application-state.hpp"
#include "../input.hpp"
#include "../debug.hpp"
#include "../nest.hpp"
#include "../terrain.hpp"
#include "../game/nest.hpp"
#include "../game/terrain.hpp"
#include <emergent/emergent.hpp>
using namespace Emergent;

+ 14
- 12
src/states/splash-state.cpp View File

@ -63,11 +63,13 @@ void SplashState::enter()
}
delete fontLoader;
// Load splash texture
application->splashTexture.load("data/textures/splash.png");
// Load title texture
application->titleTexture.load("data/textures/title.png");
// Load splash & title textures
application->textureLoader->setGamma(1.0f);
application->textureLoader->setCubemap(false);
application->textureLoader->setMipmapChain(false);
application->textureLoader->setMaxAnisotropy(1.0f);
application->splashTexture = application->textureLoader->load("data/textures/galileo_cross.hdr");
application->titleTexture = application->textureLoader->load("data/textures/title.png");
// Get UI strings
std::string pressAnyKeyString;
@ -121,16 +123,16 @@ void SplashState::enter()
application->splashImage = new UIImage();
application->splashImage->setAnchor(Anchor::CENTER);
application->splashImage->setDimensions(Vector2(application->splashTexture.getWidth(), application->splashTexture.getHeight()));
application->splashImage->setTexture(&application->splashTexture);
application->splashImage->setDimensions(Vector2(application->splashTexture->getWidth(), application->splashTexture->getHeight()));
application->splashImage->setTexture(application->splashTexture);
application->splashImage->setVisible(false);
application->uiRootElement->addChild(application->splashImage);
application->titleImage = new UIImage();
application->titleImage->setAnchor(Vector2(0.5f, 0.0f));
application->titleImage->setDimensions(Vector2(application->titleTexture.getWidth(), application->titleTexture.getHeight()));
application->titleImage->setTranslation(Vector2(0.0f, (int)(application->height * (1.0f / 3.0f) - application->titleTexture.getHeight())));
application->titleImage->setTexture(&application->titleTexture);
application->titleImage->setDimensions(Vector2(application->titleTexture->getWidth(), application->titleTexture->getHeight()));
application->titleImage->setTranslation(Vector2(0.0f, (int)(application->height * (1.0f / 3.0f) - application->titleTexture->getHeight())));
application->titleImage->setTexture(application->titleTexture);
application->titleImage->setVisible(false);
application->uiRootElement->addChild(application->titleImage);
@ -490,8 +492,8 @@ void SplashState::enter()
application->selectMenuItem(application->selectedMenuItemIndex);
// Models
application->displayModel = application->modelLoader->load("data/models/monkey.mdl");
application->antModel = application->modelLoader->load("data/models/worker-ant.mdl");
application->displayModel = application->modelLoader->load("data/models/icosphere.mdl");
application->antModel = application->modelLoader->load("data/models/agent.mdl");
// Model instances
application->displayModelInstance = new ModelInstance();

+ 111
- 18
src/states/title-state.cpp View File

@ -20,6 +20,7 @@
#include "title-state.hpp"
#include "experiment-state.hpp"
#include "../application.hpp"
#include "../camera-controller.hpp"
#include <iostream>
#include <SDL.h>
@ -45,7 +46,7 @@ void TitleState::enter()
// Setup screen fade-in transition
fadeIn = false;
fadeOut = false;
/*
// Load tunnel texture
GLuint tunnelTexture;
@ -81,6 +82,7 @@ void TitleState::enter()
application->lightingPass.setRenderTarget(&application->defaultRenderTarget);
application->lightingPass.setShadowMap(0);
application->lightingPass.setShadowCamera(&application->camera);
application->lightingPass.setModelLoader(application->modelLoader);
application->defaultCompositor.addPass(&application->lightingPass);
application->camera.lookAt(
@ -111,7 +113,6 @@ void TitleState::enter()
application->scene.getLayer(0)->addObject(lightC);
application->scene.getLayer(0)->addObject(application->displayModelInstance);
application->scene.getLayer(0)->addObject(application->antModelInstance);
application->scene.getLayer(0)->addObject(&application->camera);
// Load compositor
@ -130,8 +131,43 @@ void TitleState::enter()
application->fadeInTween->start();
application->inputManager->addWindowObserver(this);
application->mouse->addMouseButtonObserver(this);
windowResized(application->width, application->height);
// Setup camera controller
application->surfaceCam->setCamera(&application->camera);
application->surfaceCam->setFocalPoint(Vector3(0.0f));
application->surfaceCam->setFocalDistance(10.0f);
application->surfaceCam->setElevation(0.0f);
application->surfaceCam->setAzimuth(0.0f);
application->surfaceCam->setTargetFocalPoint(application->surfaceCam->getFocalPoint());
application->surfaceCam->setTargetFocalDistance(application->surfaceCam->getFocalDistance());
application->surfaceCam->setTargetElevation(application->surfaceCam->getElevation());
application->surfaceCam->setTargetAzimuth(application->surfaceCam->getAzimuth());
application->surfaceCam->update(0.0f);
// Setup arcball
dragging = false;
wasDragging = dragging;
application->arcball.setCenter(Vector2(application->width * 0.5f, application->height * 0.5f));
application->arcball.setRadius(application->height * 0.5f);
// Load navmesh
navmesh.loadOBJ("data/textures/icosphere.obj");
// Setup colony
colony.setAntModel(application->antModel);
for (int i = 0; i < 20; ++i)
{
ant = colony.spawn(&navmesh, (*navmesh.getTriangles())[0], normalize_barycentric(Vector3(0.5f)));
application->scene.getLayer(0)->addObject(ant->getModelInstance());
ant->setState(Ant::State::WANDER);
}
ant->setState(Ant::State::IDLE);
// Start timer
stateTime = 0.0f;
application->frameTimer.reset();
@ -148,6 +184,13 @@ void TitleState::execute()
// Add dt to state time
stateTime += dt;
// Update menu controls
application->menuControlProfile->update();
application->gameControlProfile->update();
// Update input
application->inputManager->update();
if (substate == 0 || substate == 1)
{
if (stateTime >= titleDelay && !application->titleImage->isVisible())
@ -242,24 +285,27 @@ void TitleState::execute()
fadeIn = true;
}
// Update display model
Transform transform = application->displayModelInstance->getTransform();
transform.translation = Vector3(0, 0.0f, 0);
transform.scale = Vector3(0.75f);
transform.rotation = glm::angleAxis(stateTime * glm::radians(360.0f) / 60.0f, glm::vec3(0, 1, 0));
application->displayModelInstance->setTransform(transform);
glm::ivec2 mousePosition = application->mouse->getCurrentPosition();
mousePosition.y = application->height - mousePosition.y;
if (dragging && !wasDragging)
{
dragStart = application->arcball.project(Vector2(mousePosition.x, mousePosition.y));
dragStartRotation = application->displayModelInstance->getTransform().rotation;
}
else if (dragging && wasDragging)
{
Vector3 dragEnd = application->arcball.project(Vector2(mousePosition.x, mousePosition.y));
Quaternion rotation = glm::normalize(glm::rotation(dragStart, dragEnd));
// Update display model
Transform transform = application->displayModelInstance->getTransform();
transform.rotation = glm::normalize(rotation * dragStartRotation);
application->displayModelInstance->setTransform(transform);
}
wasDragging = dragging;
transform.scale = Vector3(0.25f);
transform.translation.y = 0.0f;
//application->antModelInstance->setTransform(transform);
// Update menu controls
application->menuControlProfile->update();
// Update input
application->inputManager->update();
// Check if application was closed
if (application->inputManager->wasClosed() || application->escape.isTriggered())
{
@ -272,6 +318,19 @@ void TitleState::execute()
{
application->changeFullscreen();
}
float rotationSpeed = glm::radians(3.0f) * dt / (1.0f / 60.0f);
if (application->cameraRotateCW.isTriggered())
application->surfaceCam->rotate(-rotationSpeed);
if (application->cameraRotateCCW.isTriggered())
application->surfaceCam->rotate(rotationSpeed);
// Zoom camera
float zoomFactor = application->surfaceCam->getFocalDistance() / 20.0f * dt / (1.0f / 60.0f);
if (application->cameraZoomIn.isTriggered())
application->surfaceCam->zoom(zoomFactor * application->cameraZoomIn.getCurrentValue());
if (application->cameraZoomOut.isTriggered())
application->surfaceCam->zoom(-zoomFactor * application->cameraZoomOut.getCurrentValue());
application->surfaceCam->update(dt);
// Navigate menu
if (application->menuDown.isTriggered() && !application->menuDown.wasTriggered())
@ -312,6 +371,30 @@ void TitleState::execute()
Vector2(container->getPosition().x - application->menuSelectorLabel->getDimensions().x * 1.5f,
container->getPosition().y + lineHeight * 0.5f - application->menuSelectorLabel->getDimensions().y * 0.5f + lineHeight * application->selectedMenuItemIndex));
float walkSpeed = 3.0f * dt;
float turnSpeed = 4.0f * dt;
Vector3 antVelocity = ant->getForward() * walkSpeed;
if (application->walkForward.isTriggered())
{
ant->move(antVelocity);
}
if (application->walkBack.isTriggered())
{
ant->move(-antVelocity);
}
if (application->turnLeft.isTriggered())
{
ant->turn(turnSpeed);
}
if (application->turnRight.isTriggered())
{
ant->turn(-turnSpeed);
}
colony.update(dt);
// Perform tweening
application->tweener->update(dt);
@ -378,4 +461,14 @@ void TitleState::windowResized(int width, int height)
(float)application->width / (float)application->height,
0.1f,
1000.0f);
}
}
void TitleState::mouseButtonPressed(int button, int x, int y)
{
dragging = true;
}
void TitleState::mouseButtonReleased(int button, int x, int y)
{
dragging = false;
}

+ 16
- 3
src/states/title-state.hpp View File

@ -23,13 +23,15 @@
#include "../application-state.hpp"
#include "../ui/ui.hpp"
#include "../game/ant.hpp"
#include <emergent/emergent.hpp>
using namespace Emergent;
/**
* Displays the title screen.
*/
class TitleState: public ApplicationState, public WindowObserver
class TitleState: public ApplicationState, public WindowObserver, public MouseButtonObserver
{
public:
TitleState(Application* application);
@ -39,14 +41,25 @@ public:
virtual void execute();
virtual void exit();
void windowClosed();
void windowResized(int width, int height);
virtual void windowClosed();
virtual void windowResized(int width, int height);
virtual void mouseButtonPressed(int button, int x, int y);
virtual void mouseButtonReleased(int button, int x, int y);
private:
float stateTime;
bool fadeIn;
bool fadeOut;
bool dragging;
bool wasDragging;
Vector3 dragStart;
Quaternion dragStartRotation;
int substate;
Colony colony;
Ant* ant;
Navmesh navmesh;
};
#endif // TITLE_STATE_HPP

Loading…
Cancel
Save