Merge remote-tracking branch 'origin/master' into Forward+
This commit is contained in:
@@ -9,8 +9,8 @@ find_package(ZLIB REQUIRED)
|
||||
find_package(PNG REQUIRED)
|
||||
find_package(Xerces REQUIRED)
|
||||
# Because FindOpenAL is retarded
|
||||
#set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} "${CMAKE_SOURCE_DIR}/deps/include/AL")
|
||||
#find_package(OpenAL REQUIRED)
|
||||
set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} "${CMAKE_SOURCE_DIR}/deps/include/OpenAL")
|
||||
find_package(OpenAL REQUIRED)
|
||||
if(UNIX)
|
||||
find_package(X11 REQUIRED)
|
||||
endif()
|
||||
@@ -52,6 +52,12 @@ file(GLOB SOURCE_FILES_Network
|
||||
)
|
||||
source_group(Network FILES ${SOURCE_FILES_Network})
|
||||
|
||||
file(GLOB SOURCE_FILES_Sound
|
||||
"${INCLUDE_PATH}/Sound/*.h"
|
||||
"Sound/*.cpp"
|
||||
)
|
||||
source_group(Sound FILES ${SOURCE_FILES_Sound})
|
||||
|
||||
file(GLOB SOURCE_FILES_Rendering
|
||||
"${INCLUDE_PATH}/Rendering/*.h"
|
||||
"Rendering/*.cpp"
|
||||
@@ -86,6 +92,7 @@ set(SOURCE_FILES
|
||||
${SOURCE_FILES_Core_Util}
|
||||
${SOURCE_FILES_Input}
|
||||
${SOURCE_FILES_Network}
|
||||
${SOURCE_FILES_Sound}
|
||||
${SOURCE_FILES_GUI}
|
||||
${SOURCE_FILES_Rendering}
|
||||
${SOURCE_FILES_Rendering_Util}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
#include "Collision/CollidableOctreeSystem.h"
|
||||
|
||||
void CollidableOctreeSystem::Update(World* world, double dt)
|
||||
{
|
||||
m_Octree->ClearDynamicObjects();
|
||||
}
|
||||
|
||||
void CollidableOctreeSystem::UpdateComponent(World* world, EntityWrapper& entity, ComponentWrapper& component, double dt)
|
||||
{
|
||||
if (entity.HasComponent("AABB")) {
|
||||
boost::optional<AABB> absoluteAABB = Collision::EntityAbsoluteAABB(entity);
|
||||
if (absoluteAABB) {
|
||||
m_Octree->AddDynamicObject(*absoluteAABB);
|
||||
}
|
||||
} else if (entity.HasComponent("Model")) {
|
||||
// TODO: Derive AABB from model
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ bool RayAABBIntr(const Ray& ray, const AABB& box)
|
||||
{
|
||||
glm::vec3 w = 75.0f * ray.Direction();
|
||||
glm::vec3 v = glm::abs(w);
|
||||
glm::vec3 c = ray.Origin() - box.Center() + w;
|
||||
glm::vec3 c = ray.Origin() - box.Origin() + w;
|
||||
glm::vec3 half = box.HalfSize();
|
||||
|
||||
if (abs(c.x) > v.x + half.x) {
|
||||
@@ -68,8 +68,8 @@ bool RayVsAABB(const Ray& ray, const AABB& box, float& outDistance)
|
||||
|
||||
bool AABBVsAABB(const AABB& a, const AABB& b)
|
||||
{
|
||||
const glm::vec3& aCenter = a.Center();
|
||||
const glm::vec3& bCenter = b.Center();
|
||||
const glm::vec3& aCenter = a.Origin();
|
||||
const glm::vec3& bCenter = b.Origin();
|
||||
const glm::vec3& aHSize = a.HalfSize();
|
||||
const glm::vec3& bHSize = b.HalfSize();
|
||||
//Test will probably exit because of the X and Z axes more often, so test them first.
|
||||
@@ -199,11 +199,51 @@ bool RayVsModel(const Ray& ray,
|
||||
return hit;
|
||||
}
|
||||
|
||||
bool AABBvsTriangles(const AABB& box, const std::vector<RawModel::Vertex>& modelVertices, const std::vector<unsigned int>& modelIndices, const glm::mat4& modelMatrix, glm::vec3& outResolutionVector)
|
||||
{
|
||||
bool hit = false;
|
||||
|
||||
const glm::vec3& origin = box.Origin();
|
||||
const glm::vec3& min = box.MinCorner();
|
||||
const glm::vec3& max = box.MaxCorner();
|
||||
|
||||
outResolutionVector.x = INFINITY;
|
||||
|
||||
for (int i = 0; i < modelIndices.size(); ++i) {
|
||||
glm::vec3 p = modelVertices[i].Position;
|
||||
p = glm::vec3(modelMatrix * glm::vec4(p.x, p.y, p.z, 1));
|
||||
|
||||
float distFromOrigin = glm::abs(origin.x - p.x);
|
||||
float penetration = box.HalfSize().x - distFromOrigin;
|
||||
if (penetration > 0 && penetration < glm::abs(outResolutionVector.x)) {
|
||||
if (p.x > origin.x) {
|
||||
outResolutionVector.x = -penetration;
|
||||
} else {
|
||||
outResolutionVector.x = penetration;
|
||||
}
|
||||
hit = true;
|
||||
}
|
||||
//glm::vec3 pLocal = origin - p;
|
||||
//for (int axis = 0; axis < 3; ++axis) {
|
||||
// if (p[axis] < min[axis] || p[axis] > max[axis]) {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// if (glm::abs(pLocal[axis]) < box.HalfSize()[axis]) {
|
||||
// outResolutionVector[axis] = (glm::sign(pLocal[axis]) * box.HalfSize()[axis]) - pLocal[axis];
|
||||
// hit = true;
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
return hit;
|
||||
}
|
||||
|
||||
bool IsSameBoxProbably(const AABB& first, const AABB& second, const float epsilon)
|
||||
{
|
||||
const glm::vec3& ma1 = first.MaxCorner();
|
||||
const glm::vec3& ma2 = first.MaxCorner();
|
||||
const glm::vec3& mi1 = second.MinCorner();
|
||||
const glm::vec3& ma2 = second.MaxCorner();
|
||||
const glm::vec3& mi1 = first.MinCorner();
|
||||
const glm::vec3& mi2 = second.MinCorner();
|
||||
return (std::abs(ma1.x - ma2.x) < epsilon) &&
|
||||
(std::abs(mi1.x - mi2.x) < epsilon) &&
|
||||
@@ -225,11 +265,11 @@ bool attachAABBComponentFromModel(World* world, EntityID id)
|
||||
return false;
|
||||
}
|
||||
|
||||
glm::mat4 modelMatrix = modelRes->m_Matrix;
|
||||
glm::mat4 modelMatrix = modelRes->Matrix();
|
||||
|
||||
glm::vec3 mini = glm::vec3(INFINITY, INFINITY, INFINITY);
|
||||
glm::vec3 maxi = glm::vec3(-INFINITY, -INFINITY, -INFINITY);
|
||||
for (const auto& v : modelRes->m_Vertices) {
|
||||
for (const auto& v : modelRes->Vertices()) {
|
||||
const auto& wPos = modelMatrix * glm::vec4(v.Position.x, v.Position.y, v.Position.z, 1);
|
||||
maxi.x = std::max(wPos.x, maxi.x);
|
||||
maxi.y = std::max(wPos.y, maxi.y);
|
||||
@@ -238,45 +278,23 @@ bool attachAABBComponentFromModel(World* world, EntityID id)
|
||||
mini.y = std::min(wPos.y, mini.y);
|
||||
mini.z = std::min(wPos.z, mini.z);
|
||||
}
|
||||
collision["BoxCenter"] = 0.5f * (maxi + mini);
|
||||
collision["BoxSize"] = maxi - mini;
|
||||
collision["Origin"] = 0.5f * (maxi + mini);
|
||||
collision["Size"] = maxi - mini;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetEntityBox(World* world, ComponentWrapper& AABBComponent, AABB& outBox)
|
||||
boost::optional<AABB> EntityAbsoluteAABB(EntityWrapper& entity)
|
||||
{
|
||||
ComponentWrapper& cTrans = world->GetComponent(AABBComponent.EntityID, "Transform");
|
||||
ComponentWrapper model = world->GetComponent(AABBComponent.EntityID, "Model");
|
||||
Model* modelRes = ResourceManager::Load<Model>(model["Resource"]);
|
||||
outBox.CreateFromCenter(AABBComponent["BoxCenter"], AABBComponent["BoxSize"]);
|
||||
glm::vec3 mini = outBox.MinCorner();
|
||||
glm::vec3 maxi = outBox.MaxCorner();
|
||||
|
||||
if (modelRes == nullptr) {
|
||||
return false;
|
||||
}
|
||||
glm::mat4 modelMatrix = modelRes->m_Matrix *
|
||||
glm::translate(glm::mat4(), (glm::vec3)cTrans["Position"]) *
|
||||
glm::scale((glm::vec3)cTrans["Scale"]);
|
||||
|
||||
outBox = AABB(modelMatrix * glm::vec4(mini.x, mini.y, mini.z, 1),
|
||||
modelMatrix * glm::vec4(maxi.x, maxi.y, maxi.z, 1));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetEntityBox(World* world, EntityID entity, AABB& outBox, bool forceBoxFromModel)
|
||||
{
|
||||
if (!world->HasComponent(entity, "AABB")) {
|
||||
if (forceBoxFromModel) {
|
||||
if (!attachAABBComponentFromModel(world, entity))
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
if (!entity.HasComponent("AABB")) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
ComponentWrapper& cBox = world->GetComponent(entity, "AABB");
|
||||
return GetEntityBox(world, cBox, outBox);
|
||||
ComponentWrapper& cAABB = entity["AABB"];
|
||||
glm::vec3 absPosition = Transform::AbsolutePosition(entity.World, entity.ID);
|
||||
glm::vec3 absScale = Transform::AbsoluteScale(entity.World, entity.ID);
|
||||
glm::vec3 origin = absPosition + (glm::vec3)cAABB["Origin"];
|
||||
glm::vec3 size = (glm::vec3)cAABB["Size"] * absScale;
|
||||
return AABB::FromOriginSize(origin, size);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,33 +2,60 @@
|
||||
#include "Collision/CollisionSystem.h"
|
||||
#include "Core/AABB.h"
|
||||
|
||||
void CollisionSystem::UpdateComponent(World * world, ComponentWrapper & cAABB, double dt)
|
||||
void CollisionSystem::UpdateComponent(World* world, EntityWrapper& entity, ComponentWrapper& component, double dt)
|
||||
{
|
||||
//Right now, cAABB is a component attached to any entity that should be collideable.
|
||||
AABB thisBox;
|
||||
if (!Collision::GetEntityBox(world, cAABB, thisBox)) {
|
||||
if (!entity.HasComponent("Physics")) {
|
||||
return;
|
||||
}
|
||||
ComponentWrapper& cPhysics = entity["Physics"];
|
||||
|
||||
boost::optional<AABB> boundingBox = Collision::EntityAbsoluteAABB(entity);
|
||||
if (!boundingBox) {
|
||||
return;
|
||||
}
|
||||
ComponentWrapper& cTransform = entity["Transform"];
|
||||
AABB& boxA = *boundingBox;
|
||||
|
||||
//Press 'Z' to enable/disable collision.
|
||||
if (zPress) {
|
||||
return;
|
||||
}
|
||||
//Here, mover should be an object that moves, currently only players.
|
||||
for (auto& mover : *world->GetComponents("Player")) {
|
||||
if (cAABB.EntityID == mover.EntityID) {
|
||||
|
||||
// Collide against octree
|
||||
std::vector<AABB> octreeResult;
|
||||
m_Octree->BoxesInSameRegion(*boundingBox, octreeResult);
|
||||
for (auto& boxB : octreeResult) {
|
||||
glm::vec3 resolutionVector;
|
||||
if (Collision::IsSameBoxProbably(boxA, boxB)) {
|
||||
continue;
|
||||
}
|
||||
AABB otherBox;
|
||||
if (!Collision::GetEntityBox(world, mover.EntityID, otherBox)) {
|
||||
continue;
|
||||
}
|
||||
glm::vec3 resolveTranslation;
|
||||
if (Collision::AABBVsAABB(otherBox, thisBox, resolveTranslation)) {
|
||||
ComponentWrapper& trans = world->GetComponent(mover.EntityID, "Transform");
|
||||
//TODO: Special treatment if both are movers.
|
||||
trans["Position"] = (glm::vec3)trans["Position"] + resolveTranslation;
|
||||
if (Collision::AABBVsAABB(boxA, boxB, resolutionVector)) {
|
||||
(glm::vec3&)cTransform["Position"] += resolutionVector;
|
||||
cPhysics["Velocity"] = glm::vec3(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// HACK: Temporarily collide against all collidable models since they're not in the octree yet
|
||||
//auto otherCollidables = world->GetComponents("Model");
|
||||
//for (auto& cModel : *otherCollidables) {
|
||||
// if (cModel.EntityID == entity) {
|
||||
// continue;
|
||||
// }
|
||||
// if (!world->HasComponent(cModel.EntityID, "Collidable")) {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// auto absPosition = RenderQueueFactory::AbsolutePosition(world, cModel.EntityID);
|
||||
// auto absOrientation = RenderQueueFactory::AbsoluteOrientation(world, cModel.EntityID);
|
||||
// auto absScale = RenderQueueFactory::AbsoluteScale(world, cModel.EntityID);
|
||||
// glm::mat4 modelMatrix = glm::translate(absPosition); // *glm::toMat4(absOrientation) * glm::scale(absScale);
|
||||
|
||||
// auto model = ResourceManager::Load<Model>(cModel["Resource"]);
|
||||
// glm::vec3 resolutionVector;
|
||||
// if (Collision::AABBvsTriangles(boxA, model->m_Vertices, model->m_Indices, modelMatrix, resolutionVector)) {
|
||||
// (glm::vec3&)cTransform["Position"] += resolutionVector;
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
bool CollisionSystem::OnKeyUp(const Events::KeyUp & event)
|
||||
|
||||
@@ -3,27 +3,27 @@
|
||||
#include "Core/AABB.h"
|
||||
#include "Rendering/Model.h"
|
||||
|
||||
void TriggerSystem::UpdateComponent(World* world, ComponentWrapper& trigger, double dt)
|
||||
void TriggerSystem::UpdateComponent(World* world, EntityWrapper& entity, ComponentWrapper& component, double dt)
|
||||
{
|
||||
//Currently only players can trigger things.
|
||||
auto players = world->GetComponents("Player");
|
||||
if (players == nullptr) {
|
||||
return;
|
||||
}
|
||||
EntityID tId = trigger.EntityID;
|
||||
AABB triggerBox;
|
||||
EntityID tId = component.EntityID;
|
||||
boost::optional<AABB> triggerBox = Collision::EntityAbsoluteAABB(entity);
|
||||
//The trigger *should* have a bounding box, or something, to test against so it can be triggered.
|
||||
if (!Collision::GetEntityBox(world, tId, triggerBox, true)) {
|
||||
if (!triggerBox) {
|
||||
return;
|
||||
}
|
||||
for (auto& pc : *players) {
|
||||
EntityID pId = pc.EntityID;
|
||||
AABB playerBox;
|
||||
boost::optional<AABB> playerBox = Collision::EntityAbsoluteAABB(EntityWrapper(world, pId));
|
||||
//The player can't trigger anything without an AABB.
|
||||
if (!Collision::GetEntityBox(world, pId, playerBox, true)) {
|
||||
if (!playerBox) {
|
||||
continue;
|
||||
}
|
||||
if (!Collision::AABBVsAABB(triggerBox, playerBox)) {
|
||||
if (!Collision::AABBVsAABB(*triggerBox, *playerBox)) {
|
||||
//Entity is not touching the trigger,
|
||||
//Throw event if it was previously.
|
||||
if (throwLeaveIfWasInTrigger(m_EntitiesTouchingTrigger[tId], pId, tId)) {
|
||||
@@ -34,10 +34,9 @@ void TriggerSystem::UpdateComponent(World* world, ComponentWrapper& trigger, dou
|
||||
throwLeaveIfWasInTrigger(m_EntitiesCompletelyInTrigger[tId], pId, tId);
|
||||
} else {
|
||||
//Entity is at least touching the trigger.
|
||||
AABB completelyInsideBox;
|
||||
completelyInsideBox.CreateFromCenter(triggerBox.Center(), triggerBox.Size() - 2.0f * playerBox.Size());
|
||||
if (Collision::AABBVsAABB(completelyInsideBox, playerBox) &&
|
||||
glm::all(glm::greaterThan(triggerBox.Size(), playerBox.Size()))) {
|
||||
AABB completelyInsideBox = AABB::FromOriginSize((*triggerBox).Origin(), (*triggerBox).Size() - 2.0f * (*playerBox).Size());
|
||||
if (Collision::AABBVsAABB(completelyInsideBox, *playerBox) &&
|
||||
glm::all(glm::greaterThan((*triggerBox).Size(), (*playerBox).Size()))) {
|
||||
//Entity is completely inside the trigger.
|
||||
//If it was only touching before, it is erased.
|
||||
m_EntitiesTouchingTrigger[tId].erase(pId);
|
||||
@@ -79,3 +78,20 @@ bool TriggerSystem::throwLeaveIfWasInTrigger(std::unordered_set<EntityID>& trigg
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TriggerSystem::OnTouch(const Events::TriggerTouch &event)
|
||||
{
|
||||
LOG_INFO("Player entity %i touched trigger entity %i.", event.Entity, event.Trigger);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TriggerSystem::OnEnter(const Events::TriggerEnter &event)
|
||||
{
|
||||
LOG_INFO("Player entity %i entered trigger entity %i.", event.Entity, event.Trigger);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TriggerSystem::OnLeave(const Events::TriggerLeave &event)
|
||||
{
|
||||
LOG_INFO("Player entity %i left trigger entity %i.", event.Entity, event.Trigger);
|
||||
return true;
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
AABB::AABB(const glm::vec3& minPos, const glm::vec3& maxPos)
|
||||
: m_MinCorner(minPos)
|
||||
, m_MaxCorner(maxPos)
|
||||
, m_Center(0.5f * (maxPos + minPos))
|
||||
, m_Origin(0.5f * (maxPos + minPos))
|
||||
, m_HalfSize(0.5f * (maxPos - minPos))
|
||||
{
|
||||
DEBUG_IF(glm::any(glm::lessThan(m_MaxCorner, m_MinCorner))) {
|
||||
@@ -20,15 +20,12 @@ AABB::AABB(const glm::vec3& minPos, const glm::vec3& maxPos)
|
||||
|
||||
AABB::AABB(const glm::vec4& minPos, const glm::vec4& maxPos)
|
||||
: AABB(glm::vec3(minPos), glm::vec3(maxPos))
|
||||
{}
|
||||
{ }
|
||||
|
||||
void AABB::CreateFromCenter(const glm::vec3& center, const glm::vec3& size)
|
||||
AABB AABB::FromOriginSize(const glm::vec3& origin, const glm::vec3& size)
|
||||
{
|
||||
m_Center = center;
|
||||
m_HalfSize = 0.5f * size;
|
||||
m_MinCorner = m_Center - m_HalfSize;
|
||||
m_MaxCorner = m_Center + m_HalfSize;
|
||||
return AABB(origin - (size/2.f), origin + (size/2.f));
|
||||
}
|
||||
|
||||
AABB::~AABB()
|
||||
{}
|
||||
{ }
|
||||
|
||||
@@ -21,14 +21,24 @@ void EntityFile::Parse(const EntityFileHandler* handler) const
|
||||
using namespace xercesc;
|
||||
|
||||
EntityFileSAXHandler saxHandler(handler, nullptr);
|
||||
m_SAX2XMLReader->setFeature(XMLUni::fgXercesCacheGrammarFromParse, true);
|
||||
m_SAX2XMLReader->setFeature(XMLUni::fgXercesUseCachedGrammarInParse, true);
|
||||
setReaderFeatures(m_SAX2XMLReader);
|
||||
m_SAX2XMLReader->setContentHandler(&saxHandler);
|
||||
m_SAX2XMLReader->setErrorHandler(&saxHandler);
|
||||
m_SAX2XMLReader->setDeclarationHandler(&saxHandler);
|
||||
m_SAX2XMLReader->parse(m_FilePath.string().c_str());
|
||||
}
|
||||
|
||||
void EntityFile::setReaderFeatures(xercesc::SAX2XMLReader* reader)
|
||||
{
|
||||
using namespace xercesc;
|
||||
reader->setFeature(XMLUni::fgXercesCacheGrammarFromParse, true);
|
||||
reader->setFeature(XMLUni::fgXercesUseCachedGrammarInParse, true);
|
||||
reader->setFeature(XMLUni::fgSAX2CoreValidation, true);
|
||||
reader->setFeature(XMLUni::fgSAX2CoreNameSpaces, true);
|
||||
reader->setFeature(XMLUni::fgXercesSchema, true);
|
||||
reader->setFeature(XMLUni::fgXercesSchemaFullChecking, true);
|
||||
}
|
||||
|
||||
std::size_t EntityFile::GetTypeStride(std::string typeName)
|
||||
{
|
||||
std::map<std::string, size_t> typeStrides{
|
||||
@@ -37,6 +47,7 @@ std::size_t EntityFile::GetTypeStride(std::string typeName)
|
||||
{ "float", sizeof(float) },
|
||||
{ "double", sizeof(double) },
|
||||
{ "string", sizeof(std::string) },
|
||||
{ "enum", sizeof(int) },
|
||||
{ "Vector", sizeof(glm::vec3) },
|
||||
{ "Quaternion", sizeof(glm::quat) },
|
||||
{ "Color", sizeof(glm::vec4) }
|
||||
@@ -75,7 +86,7 @@ void EntityFile::WriteAttributeData(char* outData, const ComponentInfo::Field_t&
|
||||
|
||||
void EntityFile::WriteValueData(char* outData, const ComponentInfo::Field_t& field, const char* valueData)
|
||||
{
|
||||
if (field.Type == "int") {
|
||||
if (field.Type == "int" || field.Type == "enum") {
|
||||
int value = boost::lexical_cast<int>(valueData);
|
||||
memcpy(outData, reinterpret_cast<char*>(&value), field.Stride);
|
||||
} else if (field.Type == "float") {
|
||||
@@ -93,3 +104,171 @@ void EntityFile::WriteValueData(char* outData, const ComponentInfo::Field_t& fie
|
||||
LOG_WARNING("Unknown value data type: %s", field.Type.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
EntityFileSAXHandler::EntityFileSAXHandler(const EntityFileHandler* handler, xercesc::SAX2XMLReader* reader) : m_Handler(handler)
|
||||
, m_Reader(reader)
|
||||
{
|
||||
// 0 is imaginary base parent
|
||||
m_EntityStack.push(0);
|
||||
m_StateStack.push(State::Unknown);
|
||||
}
|
||||
|
||||
void EntityFileSAXHandler::startElement(const XMLCh* const _uri, const XMLCh* const _localName, const XMLCh* const _qname, const xercesc::Attributes& attrs)
|
||||
{
|
||||
std::string name = XS::ToString(_localName);
|
||||
|
||||
if (m_StateStack.top() == State::Unknown || m_StateStack.top() == State::Entity) {
|
||||
if (name == "Entity") {
|
||||
m_StateStack.push(State::Entity);
|
||||
onStartEntity(attrs);
|
||||
return;
|
||||
}
|
||||
if (name == "EntityRef") {
|
||||
onStartEntityRef(attrs);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::string uri = XS::ToString(_uri);
|
||||
if (m_StateStack.top() == State::Entity) {
|
||||
if (uri == "components") {
|
||||
m_StateStack.push(State::Component);
|
||||
onStartComponent(name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_StateStack.top() == State::Component) {
|
||||
m_StateStack.push(State::ComponentField);
|
||||
onStartComponentField(name, attrs);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void EntityFileSAXHandler::endElement(const XMLCh* const _uri, const XMLCh* const _localName, const XMLCh* const _qname)
|
||||
{
|
||||
std::string name = XS::ToString(_localName);
|
||||
if (m_StateStack.top() == State::Entity) {
|
||||
if (name == "Entity") {
|
||||
m_StateStack.pop();
|
||||
onEndEntity();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::string uri = XS::ToString(_uri);
|
||||
if (m_StateStack.top() == State::Component) {
|
||||
//if (uri == "components") {
|
||||
m_StateStack.pop();
|
||||
onEndComponent(name);
|
||||
return;
|
||||
//}
|
||||
}
|
||||
|
||||
if (m_StateStack.top() == State::ComponentField) {
|
||||
m_StateStack.pop();
|
||||
onEndComponentField(name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void EntityFileSAXHandler::characters(const XMLCh* const chars, const XMLSize_t length)
|
||||
{
|
||||
if (m_StateStack.top() == State::ComponentField) {
|
||||
char* transcoded = xercesc::XMLString::transcode(chars);
|
||||
onFieldData(transcoded);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityFileSAXHandler::fatalError(const xercesc::SAXParseException& e)
|
||||
{
|
||||
XS::ToString s(e.getMessage());
|
||||
LOG_ERROR("SAXParseException: %s", ((std::string)s).c_str());
|
||||
//throw e;
|
||||
}
|
||||
|
||||
void EntityFileSAXHandler::error(const xercesc::SAXParseException& e)
|
||||
{
|
||||
XS::ToString s(e.getMessage());
|
||||
LOG_ERROR("SAXParseException: %s", ((std::string)s).c_str());
|
||||
}
|
||||
|
||||
void EntityFileSAXHandler::warning(const xercesc::SAXParseException& e)
|
||||
{
|
||||
XS::ToString s(e.getMessage());
|
||||
LOG_ERROR("SAXParseException: %s", ((std::string)s).c_str());
|
||||
}
|
||||
|
||||
void EntityFileSAXHandler::onStartEntity(const xercesc::Attributes& attrs)
|
||||
{
|
||||
EntityID parent = m_EntityStack.top();
|
||||
|
||||
if (m_Handler->m_OnStartEntityCallback) {
|
||||
std::string name;
|
||||
auto xName = attrs.getValue(XS::ToXMLCh("name"));
|
||||
if (xName != nullptr) {
|
||||
name = XS::ToString(xName);
|
||||
}
|
||||
m_Handler->m_OnStartEntityCallback(m_NextEntityID, parent, name);
|
||||
}
|
||||
|
||||
m_EntityStack.push(m_NextEntityID);
|
||||
m_NextEntityID++;
|
||||
}
|
||||
|
||||
void EntityFileSAXHandler::onEndEntity()
|
||||
{
|
||||
m_EntityStack.pop();
|
||||
}
|
||||
|
||||
void EntityFileSAXHandler::onStartEntityRef(const xercesc::Attributes& attrs)
|
||||
{
|
||||
std::string path = XS::ToString(attrs.getValue(XS::ToXMLCh("file")));
|
||||
|
||||
xercesc::SAX2XMLReader* reader = xercesc::XMLReaderFactory::createXMLReader();
|
||||
EntityFile::setReaderFeatures(reader);
|
||||
reader->setContentHandler(this);
|
||||
reader->setErrorHandler(this);
|
||||
reader->parse(path.c_str());
|
||||
delete reader;
|
||||
}
|
||||
|
||||
void EntityFileSAXHandler::onStartComponentField(const std::string& field, const xercesc::Attributes& attrs)
|
||||
{
|
||||
//LOG_DEBUG(" Field: %s", field.c_str());
|
||||
m_CurrentField = field;
|
||||
m_CurrentAttributes.clear();
|
||||
for (int i = 0; i < attrs.getLength(); i++) {
|
||||
auto name = attrs.getQName(i);
|
||||
auto value = attrs.getValue(name);
|
||||
//LOG_DEBUG(" %s = %s", (char*)XS::ToString(name), (char*)XS::ToString(value));
|
||||
m_CurrentAttributes[XS::ToString(name).operator std::string()] = XS::ToString(value).operator std::string();
|
||||
}
|
||||
|
||||
if (m_Handler->m_OnStartFieldCallback) {
|
||||
m_Handler->m_OnStartFieldCallback(m_EntityStack.top(), m_CurrentComponent, field, m_CurrentAttributes);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityFileSAXHandler::onEndComponent(const std::string& name) { }
|
||||
|
||||
void EntityFileSAXHandler::onStartComponent(const std::string& name)
|
||||
{
|
||||
//LOG_DEBUG(" Component: %s", name.c_str());
|
||||
m_CurrentComponent = name;
|
||||
if (m_Handler->m_OnStartComponentCallback) {
|
||||
m_Handler->m_OnStartComponentCallback(m_EntityStack.top(), name);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityFileSAXHandler::onEndComponentField(const std::string& field) { }
|
||||
|
||||
void EntityFileSAXHandler::onFieldData(char* data)
|
||||
{
|
||||
//LOG_DEBUG(" Data: %s", data);
|
||||
if (m_Handler->m_OnStartFieldDataCallback) {
|
||||
m_Handler->m_OnStartFieldDataCallback(m_EntityStack.top(), m_CurrentComponent, m_CurrentField, data);
|
||||
}
|
||||
|
||||
xercesc::XMLString::release(&data);
|
||||
}
|
||||
|
||||
@@ -9,17 +9,21 @@ EntityFileParser::EntityFileParser(const EntityFile* entityFile)
|
||||
m_Handler.SetStartFieldDataCallback(std::bind(&EntityFileParser::onFieldData, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
|
||||
}
|
||||
|
||||
void EntityFileParser::MergeEntities(World* world)
|
||||
EntityID EntityFileParser::MergeEntities(World* world, EntityID baseParent /*= EntityID_Invalid */)
|
||||
{
|
||||
m_World = world;
|
||||
m_EntityIDMapper[0] = 0;
|
||||
m_EntityIDMapper[0] = baseParent;
|
||||
m_EntityFile->Parse(&m_Handler);
|
||||
return m_FirstEntity;
|
||||
}
|
||||
|
||||
void EntityFileParser::onStartEntity(EntityID entity, EntityID parent, const std::string& name)
|
||||
{
|
||||
EntityID realParent = m_EntityIDMapper.at(parent);
|
||||
EntityID realEntity = m_World->CreateEntity(realParent);
|
||||
if (m_FirstEntity == EntityID_Invalid) {
|
||||
m_FirstEntity = realEntity;
|
||||
}
|
||||
if (!name.empty()) {
|
||||
m_World->SetName(realEntity, name);
|
||||
}
|
||||
@@ -38,7 +42,12 @@ void EntityFileParser::onStartComponentField(EntityID entity, const std::string&
|
||||
{
|
||||
EntityID realEntity = m_EntityIDMapper.at(entity);
|
||||
ComponentWrapper component = m_World->GetComponent(realEntity, componentType);
|
||||
auto& field = component.Info.Fields.at(fieldName);
|
||||
auto fieldIt = component.Info.Fields.find(fieldName);
|
||||
if (fieldIt == component.Info.Fields.end()) {
|
||||
LOG_ERROR("Tried to set unknown field \"%s\" of component type \"%s\"! Ignoring.", fieldName.c_str(), componentType.c_str());
|
||||
return;
|
||||
}
|
||||
auto& field = fieldIt->second;
|
||||
|
||||
LOG_DEBUG("Field \"%s\" type \"%s\"", fieldName.c_str(), field.Type.c_str());
|
||||
LOG_DEBUG("Attributes:");
|
||||
@@ -54,7 +63,11 @@ void EntityFileParser::onFieldData(EntityID entity, const std::string& component
|
||||
{
|
||||
EntityID realEntity = m_EntityIDMapper.at(entity);
|
||||
ComponentWrapper component = m_World->GetComponent(realEntity, componentType);
|
||||
auto& field = component.Info.Fields.at(fieldName);
|
||||
auto fieldIt = component.Info.Fields.find(fieldName);
|
||||
if (fieldIt == component.Info.Fields.end()) {
|
||||
return;
|
||||
}
|
||||
auto& field = fieldIt->second;
|
||||
|
||||
char* data = component.Data + field.Offset;
|
||||
EntityFile::WriteValueData(data, field, fieldData);
|
||||
|
||||
@@ -16,12 +16,12 @@ EntityFilePreprocessor::EntityFilePreprocessor(const EntityFile* entityFile)
|
||||
|
||||
for (auto& kv : m_ComponentInfo) {
|
||||
auto& info = kv.second;
|
||||
LOG_DEBUG("Component: %s (%s)", info.Name.c_str(), info.Meta.Annotation.c_str());
|
||||
LOG_DEBUG("Stride: %i", info.Meta.Stride);
|
||||
LOG_DEBUG("Allocation: %i", info.Meta.Allocation);
|
||||
LOG_DEBUG("Component: %s (%s)", info.Name.c_str(), info.Meta->Annotation.c_str());
|
||||
LOG_DEBUG("Stride: %i", info.Stride);
|
||||
LOG_DEBUG("Allocation: %i", info.Meta->Allocation);
|
||||
for (auto& kv : info.Fields) {
|
||||
auto& field = kv.second;
|
||||
LOG_DEBUG("\t%i\t%s %s", field.Offset, field.Type, kv.first.c_str());
|
||||
LOG_DEBUG("\t%i\t%s %s", field.Offset, field.Type.c_str(), kv.first.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,62 +62,36 @@ void EntityFilePreprocessor::parseComponentInfo()
|
||||
}
|
||||
|
||||
ComponentInfo compInfo;
|
||||
compInfo.Meta = std::make_shared<ComponentInfo::Meta_t>();
|
||||
|
||||
// Name
|
||||
compInfo.Name = XS::ToString(element->getName());
|
||||
// Known allocation
|
||||
compInfo.Meta.Allocation = m_ComponentCounts[compInfo.Name];
|
||||
compInfo.Meta->Allocation = m_ComponentCounts[compInfo.Name];
|
||||
// Annotation
|
||||
auto componentAnnotation = element->getAnnotation();
|
||||
if (componentAnnotation != nullptr) {
|
||||
// Parse annotation XML
|
||||
char* annotationString = XMLString::transcode(componentAnnotation->getAnnotationString());
|
||||
MemBufInputSource annotationInput(reinterpret_cast<const XMLByte*>(annotationString), strlen(annotationString), "MemBuf: Annotation String");
|
||||
XercesDOMParser parser(nullptr, XMLPlatformUtils::fgMemoryManager, grammarPool);
|
||||
parser.setErrorHandler(&errorHandler);
|
||||
parser.parse(annotationInput);
|
||||
XMLString::release(&annotationString);
|
||||
auto doc = parser.getDocument();
|
||||
|
||||
// TODO: Add allocation estimations from external file on map-to-map basis
|
||||
// Add allocation estimation(s)
|
||||
//auto allocationTags = doc->getElementsByTagName(XSTR("meta:allocation"));
|
||||
//for (int i = 0; i < allocationTags->getLength(); ++i) {
|
||||
// auto allocation = dynamic_cast<DOMElement*>(allocationTags->item(i));
|
||||
// auto child = allocation->getFirstChild();
|
||||
// if (child == nullptr) {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// XSValue::Status status;
|
||||
// XSValue* val = XSValue::getActualValue(child->getNodeValue(), XSValue::dt_integer, status);
|
||||
// compInfo.Meta.Allocation += val->fData.fValue.f_int;
|
||||
//}
|
||||
|
||||
// Save documentation string
|
||||
auto documentationTags = doc->getElementsByTagName(XS::ToXMLCh("xs:documentation"));
|
||||
if (documentationTags->getLength() != 0) {
|
||||
auto child = documentationTags->item(0)->getFirstChild();
|
||||
if (child != nullptr) {
|
||||
compInfo.Meta.Annotation = XS::ToString(child->getNodeValue());
|
||||
}
|
||||
}
|
||||
compInfo.Meta->Annotation = parseAnnotationXML(componentAnnotation->getAnnotationString());
|
||||
} else {
|
||||
LOG_WARNING("Component is missing an annotation!");
|
||||
LOG_WARNING("Component \"%s\" is missing an annotation!", compInfo.Name.c_str());
|
||||
}
|
||||
|
||||
// <xs:complexType>
|
||||
auto typeDefinition = element->getTypeDefinition();
|
||||
// Allow empty components
|
||||
if (typeDefinition == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (typeDefinition->getTypeCategory() != XSTypeDefinition::COMPLEX_TYPE) {
|
||||
LOG_ERROR("Type definition wasn't COMPLEX_TYPE! Skipping.");
|
||||
LOG_ERROR("Failed to parse component definition for \"%s\": Type definition wasn't COMPLEX_TYPE!", compInfo.Name.c_str());
|
||||
continue;
|
||||
}
|
||||
auto complexTypeDefinition = dynamic_cast<XSComplexTypeDefinition*>(typeDefinition);
|
||||
|
||||
// <xs:all>
|
||||
auto modelGroupParticle = complexTypeDefinition->getParticle();
|
||||
if (modelGroupParticle->getTermType() != XSParticle::TERM_MODELGROUP) {
|
||||
LOG_ERROR("Model group particle wasn't TERM_MODELGROUP! Skipping.");
|
||||
if (modelGroupParticle == nullptr || modelGroupParticle->getTermType() != XSParticle::TERM_MODELGROUP) {
|
||||
LOG_ERROR("Failed to parse component definition for \"%s\": Model group particle was null or wasn't TERM_MODELGROUP!", compInfo.Name.c_str());
|
||||
continue;
|
||||
}
|
||||
auto modelGroup = modelGroupParticle->getModelGroupTerm();
|
||||
@@ -129,31 +103,65 @@ void EntityFilePreprocessor::parseComponentInfo()
|
||||
for (unsigned int i = 0; i < particles->size(); ++i) {
|
||||
auto particle = particles->elementAt(i);
|
||||
if (particle->getTermType() != XSParticle::TERM_ELEMENT) {
|
||||
LOG_ERROR("Particle wasn't TERM_ELEMENT! Skipping.");
|
||||
LOG_ERROR("Failed to parse a field definition in component \"%s\": Particle wasn't TERM_ELEMENT! Skipping.", compInfo.Name.c_str());
|
||||
continue;
|
||||
}
|
||||
auto elementDeclaration = particle->getElementTerm();
|
||||
|
||||
std::string name = XS::ToString(elementDeclaration->getName());
|
||||
std::string type = XS::ToString(elementDeclaration->getTypeDefinition()->getName());
|
||||
std::string typeNamespace = XS::ToString(elementDeclaration->getTypeDefinition()->getNamespace());
|
||||
std::string baseType = XS::ToString(elementDeclaration->getTypeDefinition()->getBaseType()->getName());
|
||||
|
||||
std::string effectiveType = type;
|
||||
size_t stride = EntityFile::GetTypeStride(type);
|
||||
if (stride == 0) {
|
||||
std::cout << "Warning: Field \"" << name << "\" in component \"" << compInfo.Name << "\" uses unexpected field type \"" << type << "\". Skipping." << std::endl;
|
||||
continue;
|
||||
stride = EntityFile::GetTypeStride(baseType);
|
||||
if (stride == 0) {
|
||||
LOG_WARNING("Field \"%s\" in component \"%s\" uses unexpected field type \"%s\" with base type \"%s\". Skipping.", name.c_str(), compInfo.Name.c_str(), type.c_str(), baseType.c_str());
|
||||
continue;
|
||||
}
|
||||
effectiveType = baseType;
|
||||
}
|
||||
|
||||
// Annotation
|
||||
auto fieldAnnotation = elementDeclaration->getAnnotation();
|
||||
if (fieldAnnotation != nullptr) {
|
||||
compInfo.Meta->FieldAnnotations[name] = parseAnnotationXML(fieldAnnotation->getAnnotationString());
|
||||
} else {
|
||||
LOG_WARNING("Component field \"%s.%s\" is missing an annotation!", compInfo.Name.c_str(), name.c_str());
|
||||
}
|
||||
|
||||
if (effectiveType == "enum") {
|
||||
// Parse potential enum type definition for field type
|
||||
if (compInfo.Meta->FieldEnumDefinitions.count(name) == 0) {
|
||||
auto enumTypeDefinition = xsModel->getTypeDefinition(XS::ToXMLCh(type), XS::ToXMLCh("components"));
|
||||
auto xsComplexType = dynamic_cast<XSComplexTypeDefinition*>(enumTypeDefinition);
|
||||
auto xsComplexContent = xsComplexType->getParticle();
|
||||
auto xsExtension = xsComplexContent->getModelGroupTerm();
|
||||
auto xsExtensionParticles = xsExtension->getParticles();
|
||||
auto xsChoice = xsExtensionParticles->elementAt(0)->getModelGroupTerm();
|
||||
auto xsChoiceParticles = xsChoice->getParticles();
|
||||
for (int i = 0; i < xsChoiceParticles->size(); ++i) {
|
||||
auto enumElement = xsChoiceParticles->elementAt(i)->getElementTerm();
|
||||
std::string enumName = XS::ToString(enumElement->getName());
|
||||
std::string enumValue = XS::ToString(enumElement->getConstraintValue());
|
||||
compInfo.Meta->FieldEnumDefinitions[name][enumName] = boost::lexical_cast<int>(enumValue);
|
||||
LOG_DEBUG("ENUM %s = %s", enumName.c_str(), enumValue.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto& field = compInfo.Fields[name];
|
||||
field.Name = name;
|
||||
field.Type = type;
|
||||
field.Type = effectiveType;
|
||||
field.Offset = fieldOffset;
|
||||
field.Stride = stride;
|
||||
compInfo.FieldsInOrder.push_back(name);
|
||||
|
||||
fieldOffset += stride;
|
||||
}
|
||||
|
||||
compInfo.Meta.Stride = fieldOffset;
|
||||
compInfo.Stride = fieldOffset;
|
||||
m_ComponentInfo[compInfo.Name] = compInfo;
|
||||
}
|
||||
}
|
||||
@@ -166,13 +174,22 @@ void EntityFilePreprocessor::parseDefaults()
|
||||
|
||||
for (auto& ci : m_ComponentInfo) {
|
||||
// Allocate memory for default values
|
||||
ci.second.Defaults = std::shared_ptr<char>(new char[ci.second.Meta.Stride]);
|
||||
memset(ci.second.Defaults.get(), 0, ci.second.Meta.Stride);
|
||||
|
||||
XercesDOMParser parser(nullptr, XMLPlatformUtils::fgMemoryManager);
|
||||
parser.setErrorHandler(&errorHandler);
|
||||
ci.second.Defaults = std::shared_ptr<char>(new char[ci.second.Stride]);
|
||||
memset(ci.second.Defaults.get(), 0, ci.second.Stride);
|
||||
|
||||
std::string componentName = ci.first;
|
||||
|
||||
XercesDOMParser parser(nullptr, XMLPlatformUtils::fgMemoryManager);
|
||||
parser.setDoSchema(true);
|
||||
parser.setDoNamespaces(true);
|
||||
parser.setErrorHandler(&errorHandler);
|
||||
parser.setValidationScheme(XercesDOMParser::Val_Always);
|
||||
parser.setValidationSchemaFullChecking(true);
|
||||
//parser.setDoNamespaces(true);
|
||||
//boost::filesystem::path schemaLocation = "Schema/Components/" + componentName + ".xsd";
|
||||
//std::string namespaceSchema = schemaLocation.string();
|
||||
//parser.setExternalNoNamespaceSchemaLocation("Teamasdasdasdasd.xsd");
|
||||
|
||||
LOG_DEBUG("Parsing defaults for component %s", componentName.c_str());
|
||||
boost::filesystem::path defaultsFile = "Schema/Components/" + componentName + ".xml";
|
||||
|
||||
@@ -184,7 +201,7 @@ void EntityFilePreprocessor::parseDefaults()
|
||||
}
|
||||
|
||||
// Find the node in the components namespace matching the component name
|
||||
std::string tagName = "c:" + componentName;
|
||||
std::string tagName = componentName;
|
||||
auto rootNodes = doc->getElementsByTagName(XS::ToXMLCh(tagName));
|
||||
if (rootNodes->getLength() == 0) {
|
||||
LOG_ERROR("Couldn't find defaults for component \"%s\"! Skipping.", componentName.c_str());
|
||||
@@ -217,9 +234,19 @@ void EntityFilePreprocessor::parseDefaults()
|
||||
EntityFile::WriteAttributeData(data, field, attributes);
|
||||
}
|
||||
|
||||
// Handle potential field values
|
||||
auto childNode = fieldElement->getFirstChild();
|
||||
if (childNode != nullptr && childNode->getNodeType() == DOMNode::TEXT_NODE) {
|
||||
if (childNode == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// An enum will either have an element node with a text node inside,
|
||||
// or contain a text node directly.
|
||||
if (childNode->getNodeType() == DOMNode::ELEMENT_NODE) {
|
||||
childNode = childNode->getFirstChild();
|
||||
}
|
||||
|
||||
// Handle potential field values
|
||||
if (childNode->getNodeType() == DOMNode::TEXT_NODE) {
|
||||
char* cstrValue = XMLString::transcode(childNode->getNodeValue());
|
||||
EntityFile::WriteValueData(data, field, cstrValue);
|
||||
XMLString::release(&cstrValue);
|
||||
@@ -228,3 +255,27 @@ void EntityFilePreprocessor::parseDefaults()
|
||||
}
|
||||
}
|
||||
|
||||
std::string EntityFilePreprocessor::parseAnnotationXML(const XMLCh* xml)
|
||||
{
|
||||
using namespace xercesc;
|
||||
|
||||
// Parse annotation XML
|
||||
char* annotationString = XMLString::transcode(xml);
|
||||
MemBufInputSource annotationInput(reinterpret_cast<const XMLByte*>(annotationString), strlen(annotationString), "MemBuf: Annotation String");
|
||||
XercesDOMParser parser(nullptr, XMLPlatformUtils::fgMemoryManager);
|
||||
//parser.setErrorHandler(&errorHandler);
|
||||
parser.parse(annotationInput);
|
||||
XMLString::release(&annotationString);
|
||||
auto doc = parser.getDocument();
|
||||
|
||||
// Save documentation string
|
||||
auto documentationTags = doc->getElementsByTagName(XS::ToXMLCh("xs:documentation"));
|
||||
if (documentationTags->getLength() != 0) {
|
||||
auto child = documentationTags->item(0)->getFirstChild();
|
||||
if (child != nullptr) {
|
||||
return XS::ToString(child->getNodeValue());
|
||||
}
|
||||
}
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ void EntityFileWriter::appentEntityComponents(xercesc::DOMElement* parentElement
|
||||
fieldElement->setAttribute(X("Y"), X(boost::lexical_cast<std::string>(q.y)));
|
||||
fieldElement->setAttribute(X("Z"), X(boost::lexical_cast<std::string>(q.z)));
|
||||
fieldElement->setAttribute(X("W"), X(boost::lexical_cast<std::string>(q.w)));
|
||||
} else if (field.Type == "int") {
|
||||
} else if (field.Type == "int" || field.Type == "enum") {
|
||||
const int& value = c[fieldName];
|
||||
fieldElement->appendChild(doc->createTextNode(X(boost::lexical_cast<std::string>(value))));
|
||||
} else if (field.Type == "float") {
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
#include "Core/EntityWrapper.h"
|
||||
#include "Core/World.h"
|
||||
|
||||
const EntityWrapper EntityWrapper::Invalid = EntityWrapper(nullptr, EntityID_Invalid);
|
||||
|
||||
bool EntityWrapper::operator==(const EntityWrapper& e)
|
||||
{
|
||||
return (this->World == e.World) && (this->ID == e.ID);
|
||||
}
|
||||
|
||||
bool EntityWrapper::HasComponent(const std::string& componentName)
|
||||
{
|
||||
return World->HasComponent(ID, componentName);
|
||||
}
|
||||
|
||||
ComponentWrapper EntityWrapper::operator[](const std::string& componentName)
|
||||
{
|
||||
if (World->HasComponent(ID, componentName)) {
|
||||
return World->GetComponent(ID, componentName);
|
||||
} else {
|
||||
LOG_WARNING("EntityWrapper implicitly attached \"%s\" component to #%i as a result of a fetch request!", componentName.c_str(), ID);
|
||||
return World->AttachComponent(ID, componentName);
|
||||
}
|
||||
}
|
||||
|
||||
EntityWrapper::operator EntityID()
|
||||
{
|
||||
return this->ID;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
#include "Core/MemoryPool.h"
|
||||
namespace DisableMemoryPool
|
||||
{
|
||||
bool Value = false;
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
#include <algorithm>
|
||||
#include <bitset>
|
||||
|
||||
#include "Core/OctTree.h"
|
||||
#include "Core/Octree.h"
|
||||
#include "Collision/Collision.h"
|
||||
|
||||
namespace
|
||||
@@ -21,65 +21,61 @@ bool isFirstLower(const ChildInfo& first, const ChildInfo& second)
|
||||
|
||||
}
|
||||
|
||||
OctTree::OctTree()
|
||||
: OctTree(AABB(), 0)
|
||||
{}
|
||||
|
||||
OctTree::OctTree(const AABB& octTreeBounds, int subDivisions)
|
||||
: m_Root(new OctChild(octTreeBounds, subDivisions, m_StaticObjects, m_DynamicObjects))
|
||||
Octree::Octree(const AABB& octTreeBounds, int subDivisions)
|
||||
: m_Root(new Child(octTreeBounds, subDivisions, m_StaticObjects, m_DynamicObjects))
|
||||
, m_UpdatedOnce(false)
|
||||
{}
|
||||
{ }
|
||||
|
||||
OctTree::~OctTree()
|
||||
Octree::~Octree()
|
||||
{
|
||||
delete m_Root;
|
||||
}
|
||||
|
||||
void OctTree::AddDynamicObject(const AABB& box)
|
||||
void Octree::AddDynamicObject(const AABB& box)
|
||||
{
|
||||
m_Root->AddDynamicObject(box);
|
||||
m_DynamicObjects.push_back(box);
|
||||
}
|
||||
|
||||
void OctTree::AddStaticObject(const AABB& box)
|
||||
void Octree::AddStaticObject(const AABB& box)
|
||||
{
|
||||
m_Root->AddStaticObject(box);
|
||||
m_StaticObjects.push_back(box);
|
||||
}
|
||||
|
||||
void OctTree::BoxesInSameRegion(const AABB& box, std::vector<AABB>& outBoxes)
|
||||
void Octree::BoxesInSameRegion(const AABB& box, std::vector<AABB>& outBoxes)
|
||||
{
|
||||
falsifyObjectChecks();
|
||||
m_Root->BoxesInSameRegion(box, outBoxes);
|
||||
}
|
||||
|
||||
void OctTree::ClearObjects()
|
||||
void Octree::ClearObjects()
|
||||
{
|
||||
m_StaticObjects.clear();
|
||||
m_DynamicObjects.clear();
|
||||
m_Root->ClearObjects();
|
||||
}
|
||||
|
||||
void OctTree::ClearDynamicObjects()
|
||||
void Octree::ClearDynamicObjects()
|
||||
{
|
||||
m_DynamicObjects.clear();
|
||||
m_Root->ClearDynamicObjects();
|
||||
}
|
||||
|
||||
bool OctTree::RayCollides(const Ray& ray, Output& data)
|
||||
bool Octree::RayCollides(const Ray& ray, Output& data)
|
||||
{
|
||||
falsifyObjectChecks();
|
||||
data.CollideDistance = -1;
|
||||
return m_Root->RayCollides(ray, data);
|
||||
}
|
||||
|
||||
bool OctTree::BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected)
|
||||
bool Octree::BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected)
|
||||
{
|
||||
falsifyObjectChecks();
|
||||
return m_Root->BoxCollides(boxToTest, outBoxIntersected);
|
||||
}
|
||||
|
||||
void OctTree::falsifyObjectChecks()
|
||||
void Octree::falsifyObjectChecks()
|
||||
{
|
||||
for (auto& obj : m_StaticObjects) {
|
||||
obj.Checked = false;
|
||||
@@ -89,7 +85,7 @@ void OctTree::falsifyObjectChecks()
|
||||
}
|
||||
}
|
||||
|
||||
OctTree::OctChild::OctChild(const AABB& octTreeBounds,
|
||||
Octree::Child::Child(const AABB& octTreeBounds,
|
||||
int subDivisions,
|
||||
std::vector<ContainedObject>& staticObjects,
|
||||
std::vector<ContainedObject>& dynamicObjects)
|
||||
@@ -98,7 +94,7 @@ OctTree::OctChild::OctChild(const AABB& octTreeBounds,
|
||||
, m_DynamicObjectsRef(dynamicObjects)
|
||||
{
|
||||
if (subDivisions == 0) {
|
||||
for (OctChild*& c : m_Children) {
|
||||
for (Child*& c : m_Children) {
|
||||
c = nullptr;
|
||||
}
|
||||
} else {
|
||||
@@ -107,7 +103,7 @@ OctTree::OctChild::OctChild(const AABB& octTreeBounds,
|
||||
glm::vec3 minPos, maxPos;
|
||||
const glm::vec3& parentMin = m_Box.MinCorner();
|
||||
const glm::vec3& parentMax = m_Box.MaxCorner();
|
||||
const glm::vec3& parentCenter = m_Box.Center();
|
||||
const glm::vec3& parentCenter = m_Box.Origin();
|
||||
std::bitset<3> bits(i);
|
||||
//If child is 4,5,6,7.
|
||||
if (bits.test(2)) {
|
||||
@@ -134,14 +130,14 @@ OctTree::OctChild::OctChild(const AABB& octTreeBounds,
|
||||
minPos.z = parentMin.z;
|
||||
maxPos.z = parentCenter.z;
|
||||
}
|
||||
m_Children[i] = new OctChild(AABB(minPos, maxPos), subDivisions, m_StaticObjectsRef, m_DynamicObjectsRef);
|
||||
m_Children[i] = new Child(AABB(minPos, maxPos), subDivisions, m_StaticObjectsRef, m_DynamicObjectsRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OctTree::OctChild::~OctChild()
|
||||
Octree::Child::~Child()
|
||||
{
|
||||
for (OctChild*& c : m_Children) {
|
||||
for (Child*& c : m_Children) {
|
||||
if (c != nullptr) {
|
||||
delete c;
|
||||
c = nullptr;
|
||||
@@ -149,7 +145,7 @@ OctTree::OctChild::~OctChild()
|
||||
}
|
||||
}
|
||||
|
||||
bool OctTree::OctChild::BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected) const
|
||||
bool Octree::Child::BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected) const
|
||||
{
|
||||
if (hasChildren()) {
|
||||
for (int i : childIndicesContainingBox(boxToTest)) {
|
||||
@@ -182,7 +178,7 @@ bool OctTree::OctChild::BoxCollides(const AABB& boxToTest, AABB& outBoxIntersect
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OctTree::OctChild::RayCollides(const Ray& ray, Output& data) const
|
||||
bool Octree::Child::RayCollides(const Ray& ray, Output& data) const
|
||||
{
|
||||
//If the node AABB is missed, everything it contains is missed.
|
||||
if (Collision::RayAABBIntr(ray, m_Box)) {
|
||||
@@ -192,7 +188,7 @@ bool OctTree::OctChild::RayCollides(const Ray& ray, Output& data) const
|
||||
std::vector<ChildInfo> childInfos;
|
||||
childInfos.reserve(8);
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
childInfos.push_back({ i, glm::distance(ray.Origin(), m_Children[i]->m_Box.Center()) });
|
||||
childInfos.push_back({ i, glm::distance(ray.Origin(), m_Children[i]->m_Box.Origin()) });
|
||||
}
|
||||
std::sort(childInfos.begin(), childInfos.end(), isFirstLower);
|
||||
//Loop through the children, starting with the one closest to the ray origin. I.e the first to be hit.
|
||||
@@ -234,7 +230,7 @@ bool OctTree::OctChild::RayCollides(const Ray& ray, Output& data) const
|
||||
}
|
||||
|
||||
|
||||
void OctTree::OctChild::AddDynamicObject(const AABB& box)
|
||||
void Octree::Child::AddDynamicObject(const AABB& box)
|
||||
{
|
||||
if (hasChildren()) {
|
||||
for (auto i : childIndicesContainingBox(box)) {
|
||||
@@ -246,7 +242,7 @@ void OctTree::OctChild::AddDynamicObject(const AABB& box)
|
||||
}
|
||||
}
|
||||
|
||||
void OctTree::OctChild::AddStaticObject(const AABB& box)
|
||||
void Octree::Child::AddStaticObject(const AABB& box)
|
||||
{
|
||||
if (hasChildren()) {
|
||||
for (auto i : childIndicesContainingBox(box)) {
|
||||
@@ -258,7 +254,7 @@ void OctTree::OctChild::AddStaticObject(const AABB& box)
|
||||
}
|
||||
}
|
||||
|
||||
void OctTree::OctChild::BoxesInSameRegion(const AABB& box, std::vector<AABB>& outBoxes) const
|
||||
void Octree::Child::BoxesInSameRegion(const AABB& box, std::vector<AABB>& outBoxes) const
|
||||
{
|
||||
if (hasChildren()) {
|
||||
for (auto i : childIndicesContainingBox(box)) {
|
||||
@@ -292,10 +288,10 @@ void OctTree::OctChild::BoxesInSameRegion(const AABB& box, std::vector<AABB>& ou
|
||||
}
|
||||
}
|
||||
|
||||
void OctTree::OctChild::ClearObjects()
|
||||
void Octree::Child::ClearObjects()
|
||||
{
|
||||
if (hasChildren()) {
|
||||
for (OctChild*& c : m_Children) {
|
||||
for (Child*& c : m_Children) {
|
||||
c->ClearObjects();
|
||||
}
|
||||
} else {
|
||||
@@ -304,10 +300,10 @@ void OctTree::OctChild::ClearObjects()
|
||||
}
|
||||
}
|
||||
|
||||
void OctTree::OctChild::ClearDynamicObjects()
|
||||
void Octree::Child::ClearDynamicObjects()
|
||||
{
|
||||
if (hasChildren()) {
|
||||
for (OctChild*& c : m_Children) {
|
||||
for (Child*& c : m_Children) {
|
||||
c->ClearObjects();
|
||||
}
|
||||
} else {
|
||||
@@ -327,13 +323,13 @@ void OctTree::OctChild::ClearDynamicObjects()
|
||||
// x : - - - - + + + +
|
||||
// y : - - + + - - + +
|
||||
// z : - + - + - + - +
|
||||
int OctTree::OctChild::childIndexContainingPoint(const glm::vec3& point) const
|
||||
int Octree::Child::childIndexContainingPoint(const glm::vec3& point) const
|
||||
{
|
||||
const glm::vec3& c = m_Box.Center();
|
||||
const glm::vec3& c = m_Box.Origin();
|
||||
return (1 << 2) * (point.x >= c.x) | (1 << 1) * (point.y >= c.y) | (point.z >= c.z);
|
||||
}
|
||||
|
||||
std::vector<int> OctTree::OctChild::childIndicesContainingBox(const AABB& box) const
|
||||
std::vector<int> Octree::Child::childIndicesContainingBox(const AABB& box) const
|
||||
{
|
||||
int minInd = childIndexContainingPoint(box.MinCorner());
|
||||
int maxInd = childIndexContainingPoint(box.MaxCorner());
|
||||
@@ -371,7 +367,7 @@ std::vector<int> OctTree::OctChild::childIndicesContainingBox(const AABB& box) c
|
||||
}
|
||||
}
|
||||
|
||||
inline bool OctTree::OctChild::hasChildren() const
|
||||
inline bool Octree::Child::hasChildren() const
|
||||
{
|
||||
return m_Children[0] != nullptr;
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
#include "Core/ResourceManager.h"
|
||||
#include "boost/thread/thread.hpp"
|
||||
#include "boost/thread/mutex.hpp"
|
||||
#include "boost/thread/lock_guard.hpp"
|
||||
|
||||
std::unordered_map<std::string, std::string> ResourceManager::m_CompilerTypenameToResourceType;
|
||||
std::unordered_map<std::string, std::function<Resource*(std::string)>> ResourceManager::m_FactoryFunctions;
|
||||
@@ -6,10 +9,13 @@ std::unordered_map<std::pair<std::string, std::string>, Resource*> ResourceManag
|
||||
std::unordered_map<std::string, Resource*> ResourceManager::m_ResourceFromName;
|
||||
std::unordered_map<Resource*, Resource*> ResourceManager::m_ResourceParents;
|
||||
unsigned int ResourceManager::m_CurrentResourceTypeID = 0;
|
||||
bool ResourceManager::UseThreading = false;
|
||||
std::unordered_map<std::string, unsigned int> ResourceManager::m_ResourceTypeIDs;
|
||||
std::unordered_map<unsigned int, unsigned int> ResourceManager::m_ResourceCount;
|
||||
bool ResourceManager::m_Preloading = false;
|
||||
FileWatcher ResourceManager::m_FileWatcher;
|
||||
std::unordered_map<std::pair<std::string, std::string>, boost::thread> ResourceManager::m_LoadingThreads;
|
||||
std::unordered_map<std::pair<std::string, std::string>, std::exception_ptr> ResourceManager::m_LoadingThreadExceptions;
|
||||
boost::recursive_mutex ResourceManager::m_Mutex;
|
||||
|
||||
unsigned int ResourceManager::GetTypeID(std::string resourceType)
|
||||
{
|
||||
@@ -74,63 +80,65 @@ void ResourceManager::Update()
|
||||
m_FileWatcher.Check();
|
||||
}
|
||||
|
||||
void ResourceManager::Preload(std::string resourceType, std::string resourceName)
|
||||
{
|
||||
if (IsResourceLoaded(resourceType, resourceName)) {
|
||||
//LOG_WARNING("Attempted to preload resource \"%s\" multiple times!", resourceName.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
m_Preloading = true;
|
||||
LOG_INFO("Preloading resource \"%s\"", resourceName.c_str());
|
||||
CreateResource(resourceType, resourceName, nullptr);
|
||||
m_Preloading = false;
|
||||
}
|
||||
|
||||
Resource* ResourceManager::Load(std::string resourceType, std::string resourceName, Resource* parent /*= nullptr*/)
|
||||
{
|
||||
auto it = m_ResourceCache.find(std::make_pair(resourceType, resourceName));
|
||||
if (it != m_ResourceCache.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
if (m_Preloading) {
|
||||
LOG_INFO("Preloading resource \"%s\"", resourceName.c_str());
|
||||
} else {
|
||||
LOG_WARNING("Hot-loading resource \"%s\"", resourceName.c_str());
|
||||
}
|
||||
|
||||
return CreateResource(resourceType, resourceName, parent);
|
||||
}
|
||||
|
||||
Resource* ResourceManager::CreateResource(std::string resourceType, std::string resourceName, Resource* parent)
|
||||
Resource* ResourceManager::createResource(const std::string& resourceType, const std::string& resourceName, Resource* parent, std::exception_ptr& exception)
|
||||
{
|
||||
auto facIt = m_FactoryFunctions.find(resourceType);
|
||||
if (facIt == m_FactoryFunctions.end()) {
|
||||
LOG_ERROR("Failed to load resource \"%s\" of type \"%s\": type not registered", resourceName.c_str(), resourceType.c_str());
|
||||
return nullptr;
|
||||
cacheResource(nullptr, resourceType, resourceName, parent);
|
||||
//This basically throws an exception.
|
||||
exception = std::make_exception_ptr(Resource::FailedLoadingException()); return nullptr;
|
||||
}
|
||||
|
||||
// Call the factory function
|
||||
Resource* resource;
|
||||
try {
|
||||
resource = facIt->second(resourceName);
|
||||
return cacheResource(facIt->second(resourceName), resourceType, resourceName, parent);
|
||||
} catch (const Resource::StillLoadingException&) {
|
||||
exception = std::current_exception(); return nullptr;
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR("Failed to load resource \"%s\" of type \"%s\": %s", resourceName.c_str(), resourceType.c_str(), e.what());
|
||||
cacheResource(nullptr, resourceType, resourceName, parent);
|
||||
exception = std::current_exception(); return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Resource* ResourceManager::createResourceThrowing(const std::string& resourceType, const std::string& resourceName, Resource* parent)
|
||||
{
|
||||
std::exception_ptr exception;
|
||||
Resource* res = createResource(resourceType, resourceName, parent, exception);
|
||||
if (exception) {
|
||||
std::rethrow_exception(exception);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Resource* ResourceManager::cacheResource(Resource* resource, const std::string& resourceType, const std::string& resourceName, Resource* parent)
|
||||
{
|
||||
//Lock the mutex immediately, and unlock it when leaving the code block.
|
||||
boost::lock_guard<decltype(m_Mutex)> guard(m_Mutex);
|
||||
if (resource != nullptr) {
|
||||
// Store IDs
|
||||
resource->TypeID = GetTypeID(resourceType);
|
||||
resource->ResourceID = GetNewResourceID(resource->TypeID);
|
||||
} catch (const std::exception& e) {
|
||||
resource = nullptr;
|
||||
LOG_ERROR("Failed to load resource \"%s\" of type \"%s\": %s", resourceName.c_str(), resourceType.c_str(), e.what());
|
||||
}
|
||||
// Cache
|
||||
m_ResourceCache[std::make_pair(resourceType, resourceName)] = resource;
|
||||
m_ResourceFromName[resourceName] = resource;
|
||||
if (parent != nullptr) {
|
||||
m_ResourceParents[resource] = parent;
|
||||
}
|
||||
if (!boost::filesystem::is_directory(resourceName)) {
|
||||
LOG_DEBUG("Adding watch for %s", resourceName.c_str());
|
||||
m_FileWatcher.AddWatch(resourceName, fileWatcherCallback);
|
||||
}
|
||||
return resource;
|
||||
|
||||
// Cache
|
||||
m_ResourceCache[std::make_pair(resourceType, resourceName)] = resource;
|
||||
m_ResourceFromName[resourceName] = resource;
|
||||
if (parent != nullptr) {
|
||||
m_ResourceParents[resource] = parent;
|
||||
}
|
||||
|
||||
//if (!boost::filesystem::is_directory(resourceName)) {
|
||||
// LOG_DEBUG("Adding watch for %s", resourceName.c_str());
|
||||
// m_FileWatcher.AddWatch(resourceName, fileWatcherCallback);
|
||||
//}
|
||||
return resource;
|
||||
}
|
||||
|
||||
bool ResourceManager::IsMainThread()
|
||||
{
|
||||
static boost::thread::id MainThreadId = boost::this_thread::get_id();
|
||||
return boost::this_thread::get_id() == MainThreadId;
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ void World::RegisterComponent(ComponentInfo& ci)
|
||||
}
|
||||
}
|
||||
|
||||
ComponentWrapper World::AttachComponent(EntityID entity, std::string componentType)
|
||||
ComponentWrapper World::AttachComponent(EntityID entity, const std::string& componentType)
|
||||
{
|
||||
// TODO: Allocate dynamic pool if component isn't registered
|
||||
ComponentPool* pool = m_ComponentPools.at(componentType);
|
||||
@@ -75,31 +75,31 @@ ComponentWrapper World::AttachComponent(EntityID entity, std::string componentTy
|
||||
// Allocate space for the component
|
||||
ComponentWrapper c = pool->Allocate(entity);
|
||||
// Write default values
|
||||
memcpy(c.Data, ci.Defaults.get(), ci.Meta.Stride);
|
||||
memcpy(c.Data, ci.Defaults.get(), ci.Stride);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
bool World::HasComponent(EntityID entity, std::string componentType) const
|
||||
bool World::HasComponent(EntityID entity, const std::string& componentType) const
|
||||
{
|
||||
ComponentPool* pool = m_ComponentPools.at(componentType);
|
||||
return pool->KnowsEntity(entity);
|
||||
}
|
||||
|
||||
ComponentWrapper World::GetComponent(EntityID entity, std::string componentType)
|
||||
ComponentWrapper World::GetComponent(EntityID entity, const std::string& componentType)
|
||||
{
|
||||
ComponentPool* pool = m_ComponentPools.at(componentType);
|
||||
return pool->GetByEntity(entity);
|
||||
}
|
||||
|
||||
void World::DeleteComponent(EntityID entity, std::string componentType)
|
||||
void World::DeleteComponent(EntityID entity, const std::string& componentType)
|
||||
{
|
||||
ComponentPool* pool = m_ComponentPools.at(componentType);
|
||||
ComponentWrapper c = pool->GetByEntity(entity);
|
||||
return pool->Delete(c);
|
||||
}
|
||||
|
||||
const ComponentPool* World::GetComponents(std::string componentType)
|
||||
const ComponentPool* World::GetComponents(const std::string& componentType)
|
||||
{
|
||||
auto it = m_ComponentPools.find(componentType);
|
||||
return (it != m_ComponentPools.end()) ? it->second : nullptr;
|
||||
@@ -125,6 +125,11 @@ void World::SetParent(EntityID entity, EntityID parent)
|
||||
m_EntityChildren.insert(std::make_pair(parent, entity));
|
||||
}
|
||||
|
||||
const std::pair<std::unordered_multimap<EntityID, EntityID>::const_iterator, std::unordered_multimap<EntityID, EntityID>::const_iterator> World::GetChildren(EntityID entity)
|
||||
{
|
||||
return m_EntityChildren.equal_range(entity);
|
||||
}
|
||||
|
||||
void World::SetName(EntityID entity, const std::string& name)
|
||||
{
|
||||
m_EntityNames[entity] = name;
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
#include <imgui/imgui_internal.h>
|
||||
|
||||
EditorSystem::EditorSystem(EventBroker* eventBroker, IRenderer* renderer)
|
||||
: ImpureSystem(eventBroker)
|
||||
: System(eventBroker)
|
||||
, ImpureSystem()
|
||||
, m_Renderer(renderer)
|
||||
{
|
||||
auto config = ResourceManager::Load<ConfigFile>("Config.ini");
|
||||
@@ -483,8 +484,8 @@ void EditorSystem::drawUI(World* world, double dt)
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader(componentType.c_str())) {
|
||||
if (!ci.Meta.Annotation.empty()) {
|
||||
ImGui::Text(ci.Meta.Annotation.c_str());
|
||||
if (!ci.Meta->Annotation.empty()) {
|
||||
ImGui::Text(ci.Meta->Annotation.c_str());
|
||||
}
|
||||
|
||||
auto& component = world->GetComponent(m_Selection, componentType);
|
||||
@@ -492,9 +493,10 @@ void EditorSystem::drawUI(World* world, double dt)
|
||||
const std::string& fieldName = kv.first;
|
||||
auto& field = kv.second;
|
||||
|
||||
ImGui::PushID(fieldName.c_str());
|
||||
std::string uniqueID = componentType + fieldName;
|
||||
ImGui::PushID(uniqueID.c_str());
|
||||
if (field.Type == "Vector") {
|
||||
auto& val = component.Property<glm::vec3>(fieldName);
|
||||
auto& val = component.Field<glm::vec3>(fieldName);
|
||||
if (fieldName == "Scale") {
|
||||
ImGui::DragFloat3("", glm::value_ptr(val), 0.1f, 0.f, std::numeric_limits<float>::max());
|
||||
} else if (fieldName == "Orientation") {
|
||||
@@ -506,10 +508,10 @@ void EditorSystem::drawUI(World* world, double dt)
|
||||
ImGui::DragFloat3("", glm::value_ptr(val), 0.1f, std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max());
|
||||
}
|
||||
} else if (field.Type == "Color") {
|
||||
auto& val = component.Property<glm::vec4>(fieldName);
|
||||
auto& val = component.Field<glm::vec4>(fieldName);
|
||||
ImGui::ColorEdit4("", glm::value_ptr(val), true);
|
||||
} else if (field.Type == "string") {
|
||||
std::string& val = component.Property<std::string>(fieldName);
|
||||
std::string& val = component.Field<std::string>(fieldName);
|
||||
char tempString[1024];
|
||||
memcpy(tempString, val.c_str(), std::min(val.length() + 1, sizeof(tempString)));
|
||||
if (ImGui::InputText("", tempString, sizeof(tempString))) {
|
||||
@@ -523,12 +525,32 @@ void EditorSystem::drawUI(World* world, double dt)
|
||||
}
|
||||
|
||||
} else if (field.Type == "double") {
|
||||
float tempVal = static_cast<float>(component.Property<double>(fieldName));
|
||||
float tempVal = static_cast<float>(component.Field<double>(fieldName));
|
||||
if (ImGui::InputFloat("", &tempVal, 0.01f, 1.f)) {
|
||||
component.SetProperty(fieldName, static_cast<double>(tempVal));
|
||||
component.SetField(fieldName, static_cast<double>(tempVal));
|
||||
}
|
||||
} else if (field.Type == "int") {
|
||||
int val = component.Field<int>(fieldName);
|
||||
ImGui::InputInt("", &val);
|
||||
} else if (field.Type == "enum") {
|
||||
int currentValue = component.Field<int>(fieldName);
|
||||
int item = -1;
|
||||
std::stringstream enumKeys;
|
||||
std::vector<int> enumValues;
|
||||
int i = 0;
|
||||
for (auto& kv : ci.Meta->FieldEnumDefinitions.at(fieldName)) {
|
||||
enumKeys << kv.first << " (" << kv.second << ")" << '\0';
|
||||
enumValues.push_back(kv.second);
|
||||
if (currentValue == kv.second) {
|
||||
item = i;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (ImGui::Combo("", &item, enumKeys.str().c_str())) {
|
||||
component.SetField(fieldName, enumValues.at(item));
|
||||
}
|
||||
} else if (field.Type == "bool") {
|
||||
auto& val = component.Property<bool>(fieldName);
|
||||
auto& val = component.Field<bool>(fieldName);
|
||||
ImGui::Checkbox("", &val);
|
||||
} else {
|
||||
ImGui::TextDisabled(field.Type.c_str());
|
||||
|
||||
@@ -20,15 +20,16 @@ void InputProxy::LoadBindings(std::string file)
|
||||
for (auto& origin : config->GetAll<std::string>("Bindings")) {
|
||||
Events::BindOrigin e;
|
||||
e.Origin = origin.first;
|
||||
e.Command = origin.second;
|
||||
e.Value = 1.f;
|
||||
if (!e.Command.empty()) {
|
||||
char prefix = e.Command.at(0);
|
||||
if (prefix == '+' || prefix == '-') {
|
||||
e.Command = e.Command.substr(1);
|
||||
if (prefix == '-') {
|
||||
e.Value *= -1.f;
|
||||
}
|
||||
const std::string& command = origin.second;
|
||||
if (!command.empty()) {
|
||||
boost::char_separator<char> separator(", ");
|
||||
boost::tokenizer<decltype(separator)> tokenizer(command, separator);
|
||||
auto token = tokenizer.begin();
|
||||
e.Command = *token;
|
||||
if (++token != tokenizer.end()) {
|
||||
e.Value = boost::lexical_cast<float>(*token);
|
||||
} else {
|
||||
e.Value = 1.f;
|
||||
}
|
||||
OnBindOrigin(e);
|
||||
}
|
||||
|
||||
@@ -1,60 +1,74 @@
|
||||
#include "Rendering/Model.h"
|
||||
|
||||
Model::Model(std::string fileName)
|
||||
: RawModel(fileName)
|
||||
{
|
||||
// Generate GL buffers
|
||||
GLuint buffer;
|
||||
glGenBuffers(1, &buffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, m_Vertices.size() * sizeof(Vertex), &m_Vertices[0], GL_STATIC_DRAW);
|
||||
//Try loading the model asyncronously, if it throws any exceptions then let it propagate back to caller.
|
||||
m_RawModel = ResourceManager::Load<RawModel, true>(fileName);
|
||||
|
||||
glGenBuffers(1, &ElementBuffer);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ElementBuffer);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_Indices.size() * sizeof(unsigned int), &m_Indices[0], GL_STATIC_DRAW);
|
||||
for (auto& group : m_RawModel->MaterialGroups) {
|
||||
if (!group.TexturePath.empty()) {
|
||||
group.Texture = std::shared_ptr<Texture>(ResourceManager::Load<Texture>(group.TexturePath));
|
||||
}
|
||||
if (!group.NormalMapPath.empty()) {
|
||||
group.NormalMap = std::shared_ptr<Texture>(ResourceManager::Load<Texture>(group.NormalMapPath));
|
||||
}
|
||||
if (!group.SpecularMapPath.empty()) {
|
||||
group.SpecularMap = std::shared_ptr<Texture>(ResourceManager::Load<Texture>(group.SpecularMapPath));
|
||||
}
|
||||
}
|
||||
|
||||
glGenVertexArrays(1, &VAO);
|
||||
glBindVertexArray(VAO);
|
||||
GLERROR("GLEW: BufferFail4");
|
||||
// Generate GL buffers
|
||||
GLuint buffer;
|
||||
glGenBuffers(1, &buffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, m_RawModel->m_Vertices.size() * sizeof(RawModel::Vertex), &m_RawModel->m_Vertices[0], GL_STATIC_DRAW);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffer);
|
||||
std::vector<int> structSizes = { 3, 3, 3, 3, 2, 4, 4, 4, 4, 4, 4 };
|
||||
int stride = 0;
|
||||
for (int size : structSizes) {
|
||||
stride += size;
|
||||
}
|
||||
stride *= sizeof(GLfloat);
|
||||
int offset = 0;
|
||||
{
|
||||
int element = 0;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * offset)); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
}
|
||||
GLERROR("GLEW: BufferFail5");
|
||||
glGenBuffers(1, &ElementBuffer);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ElementBuffer);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_RawModel->m_Indices.size() * sizeof(unsigned int), &m_RawModel->m_Indices[0], GL_STATIC_DRAW);
|
||||
|
||||
glEnableVertexAttribArray(0);
|
||||
glEnableVertexAttribArray(1);
|
||||
glEnableVertexAttribArray(2);
|
||||
glEnableVertexAttribArray(3);
|
||||
glEnableVertexAttribArray(4);
|
||||
glEnableVertexAttribArray(5);
|
||||
glEnableVertexAttribArray(6);
|
||||
glEnableVertexAttribArray(7);
|
||||
glEnableVertexAttribArray(8);
|
||||
glEnableVertexAttribArray(9);
|
||||
glEnableVertexAttribArray(10);
|
||||
GLERROR("GLEW: BufferFail5");
|
||||
glGenVertexArrays(1, &VAO);
|
||||
glBindVertexArray(VAO);
|
||||
GLERROR("GLEW: BufferFail4");
|
||||
|
||||
//CreateBuffers();
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffer);
|
||||
std::vector<int> structSizes = { 3, 3, 3, 3, 2, 4, 4, 4, 4, 4, 4 };
|
||||
int stride = 0;
|
||||
for (int size : structSizes) {
|
||||
stride += size;
|
||||
}
|
||||
stride *= sizeof(GLfloat);
|
||||
int offset = 0;
|
||||
{
|
||||
int element = 0;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * offset)); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
}
|
||||
GLERROR("GLEW: BufferFail5");
|
||||
|
||||
glEnableVertexAttribArray(0);
|
||||
glEnableVertexAttribArray(1);
|
||||
glEnableVertexAttribArray(2);
|
||||
glEnableVertexAttribArray(3);
|
||||
glEnableVertexAttribArray(4);
|
||||
glEnableVertexAttribArray(5);
|
||||
glEnableVertexAttribArray(6);
|
||||
glEnableVertexAttribArray(7);
|
||||
glEnableVertexAttribArray(8);
|
||||
glEnableVertexAttribArray(9);
|
||||
glEnableVertexAttribArray(10);
|
||||
GLERROR("GLEW: BufferFail5");
|
||||
|
||||
//CreateBuffers();
|
||||
}
|
||||
|
||||
Model::~Model()
|
||||
|
||||
@@ -145,9 +145,7 @@ RawModel::RawModel(std::string fileName)
|
||||
aiString path;
|
||||
aiTextureMapping mapping;
|
||||
material->GetTexture(aiTextureType_DIFFUSE, 0, &path, &mapping);
|
||||
std::string absolutePath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string();
|
||||
//LOG_DEBUG("Diffuse texture: %s", absolutePath.c_str());
|
||||
matGroup.Texture = std::shared_ptr<Texture>(ResourceManager::Load<Texture>(absolutePath));
|
||||
matGroup.TexturePath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string();
|
||||
}
|
||||
// Normal map
|
||||
//LOG_DEBUG("%i normal maps found", material->GetTextureCount(aiTextureType_HEIGHT));
|
||||
@@ -155,9 +153,7 @@ RawModel::RawModel(std::string fileName)
|
||||
aiString path;
|
||||
aiTextureMapping mapping;
|
||||
material->GetTexture(aiTextureType_HEIGHT, 0, &path, &mapping);
|
||||
std::string absolutePath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string();
|
||||
//LOG_DEBUG("Normal map: %s", absolutePath.c_str());
|
||||
matGroup.NormalMap = std::shared_ptr<Texture>(ResourceManager::Load<Texture>(absolutePath));
|
||||
matGroup.NormalMapPath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string();
|
||||
}
|
||||
// Specular map
|
||||
//LOG_DEBUG("%i specular maps found", material->GetTextureCount(aiTextureType_SPECULAR));
|
||||
@@ -165,11 +161,9 @@ RawModel::RawModel(std::string fileName)
|
||||
aiString path;
|
||||
aiTextureMapping mapping;
|
||||
material->GetTexture(aiTextureType_SPECULAR, 0, &path, &mapping);
|
||||
std::string absolutePath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string();
|
||||
//LOG_DEBUG("Specular map: %s", absolutePath.c_str());
|
||||
matGroup.SpecularMap = std::shared_ptr<Texture>(ResourceManager::Load<Texture>(absolutePath));
|
||||
matGroup.SpecularMapPath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string();
|
||||
}
|
||||
TextureGroups.push_back(matGroup);
|
||||
MaterialGroups.push_back(matGroup);
|
||||
|
||||
// Bones
|
||||
std::map<int, std::vector<std::tuple<int, float>>> vertexWeights;
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
#include "Rendering/RenderSystem.h"
|
||||
|
||||
RenderSystem::RenderSystem(EventBroker* eventBrokerer, const IRenderer* renderer, RenderFrame* renderFrame) :ImpureSystem(eventBrokerer)
|
||||
RenderSystem::RenderSystem(EventBroker* eventBroker, const IRenderer* renderer, RenderFrame* renderFrame)
|
||||
: System(eventBroker)
|
||||
, m_Renderer(renderer)
|
||||
, m_RenderFrame(renderFrame)
|
||||
{
|
||||
m_Renderer = renderer;
|
||||
m_RenderFrame = renderFrame;
|
||||
EVENT_SUBSCRIBE_MEMBER(m_ESetCamera, &RenderSystem::OnSetCamera);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &RenderSystem::OnInputCommand);
|
||||
|
||||
m_Camera = new Camera((float)m_Renderer->Resolution().Width / m_Renderer->Resolution().Height, glm::radians(45.f), 0.01f, 5000.f);
|
||||
m_DebugCameraInputController = new DebugCameraInputController<RenderSystem>(eventBrokerer, -1);
|
||||
m_DebugCameraInputController = new DebugCameraInputController<RenderSystem>(eventBroker, -1);
|
||||
}
|
||||
|
||||
RenderSystem::~RenderSystem()
|
||||
@@ -39,11 +40,17 @@ void RenderSystem::switchCamera(EntityID entity)
|
||||
if (m_World->HasComponent(m_CurrentCamera, "Model")) {
|
||||
m_World->GetComponent(m_CurrentCamera, "Model")["Visible"] = true;
|
||||
}
|
||||
if (m_World->HasComponent(m_CurrentCamera, "Listener")) {
|
||||
m_World->DeleteComponent(m_CurrentCamera, "Listener");
|
||||
}
|
||||
}
|
||||
|
||||
if (m_World->HasComponent(entity, "Model")) {
|
||||
m_World->GetComponent(entity, "Model")["Visible"] = false;
|
||||
}
|
||||
if (!m_World->HasComponent(entity, "Listener")) {
|
||||
m_World->AttachComponent(entity, "Listener");
|
||||
}
|
||||
m_CurrentCamera = entity;
|
||||
m_SwitchCamera = false;
|
||||
|
||||
@@ -84,15 +91,23 @@ void RenderSystem::fillModels(std::list<std::shared_ptr<RenderJob>>& jobs, World
|
||||
continue;
|
||||
}
|
||||
|
||||
Model* model = ResourceManager::Load<::Model>(resource);
|
||||
if (model == nullptr) {
|
||||
model = ResourceManager::Load<::Model>("Models/Core/Error.obj");
|
||||
Model* model;
|
||||
try {
|
||||
model = ResourceManager::Load<::Model, true>(resource);
|
||||
} catch (const Resource::StillLoadingException&) {
|
||||
//continue;
|
||||
model = ResourceManager::Load<::Model>("Models/Core/UnitRaptor.obj");
|
||||
} catch (const std::exception&) {
|
||||
try {
|
||||
model = ResourceManager::Load<::Model>("Models/Core/Error.obj");
|
||||
} catch (const std::exception&) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
glm::mat4 modelMatrix = Transform::ModelMatrix(modelComponent.EntityID, world);
|
||||
|
||||
for (auto texGroup : model->TextureGroups) {
|
||||
std::shared_ptr<ModelJob> modelJob = std::shared_ptr<ModelJob>(new ModelJob(model, m_Camera, modelMatrix, texGroup, modelComponent, world));
|
||||
for (auto matGroup : model->MaterialGroups()) {
|
||||
std::shared_ptr<ModelJob> modelJob = std::shared_ptr<ModelJob>(new ModelJob(model, m_Camera, modelMatrix, matGroup, modelComponent, world));
|
||||
jobs.push_back(modelJob);
|
||||
}
|
||||
}
|
||||
@@ -175,6 +190,9 @@ void RenderSystem::updateCamera(World* world, double dt)
|
||||
{
|
||||
if (m_SwitchCamera) {
|
||||
auto cameras = world->GetComponents("Camera");
|
||||
if (cameras == nullptr) {
|
||||
return;
|
||||
}
|
||||
for (auto it = cameras->begin(); it != cameras->end(); it++) {
|
||||
if ((*it).EntityID == m_CurrentCamera) {
|
||||
it++;
|
||||
@@ -186,11 +204,13 @@ void RenderSystem::updateCamera(World* world, double dt)
|
||||
break;
|
||||
}
|
||||
}
|
||||
ComponentWrapper& cameraComponent = world->GetComponent(m_CurrentCamera, "Camera");
|
||||
ComponentWrapper& cameraTransform = world->GetComponent(m_CurrentCamera, "Transform");
|
||||
if (m_World->HasComponent(m_CurrentCamera, "Camera")) {
|
||||
ComponentWrapper& cameraComponent = world->GetComponent(m_CurrentCamera, "Camera");
|
||||
ComponentWrapper& cameraTransform = world->GetComponent(m_CurrentCamera, "Transform");
|
||||
|
||||
m_DebugCameraInputController->SetOrientation(glm::quat((glm::vec3)cameraTransform["Orientation"]));
|
||||
m_DebugCameraInputController->SetPosition(cameraTransform["Position"]);
|
||||
m_DebugCameraInputController->SetOrientation(glm::quat((glm::vec3)cameraTransform["Orientation"]));
|
||||
m_DebugCameraInputController->SetPosition(cameraTransform["Position"]);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_World->ValidEntity(m_CurrentCamera)) {
|
||||
|
||||
@@ -137,8 +137,8 @@ void Renderer::DrawScreenQuad(GLuint textureToDraw)
|
||||
|
||||
glBindVertexArray(m_ScreenQuad->VAO);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ScreenQuad->ElementBuffer);
|
||||
glDrawElementsBaseVertex(GL_TRIANGLES, m_ScreenQuad->TextureGroups[0].EndIndex - m_ScreenQuad->TextureGroups[0].StartIndex +1
|
||||
, GL_UNSIGNED_INT, 0, m_ScreenQuad->TextureGroups[0].StartIndex);
|
||||
glDrawElementsBaseVertex(GL_TRIANGLES, m_ScreenQuad->MaterialGroups()[0].EndIndex - m_ScreenQuad->MaterialGroups()[0].StartIndex +1
|
||||
, GL_UNSIGNED_INT, 0, m_ScreenQuad->MaterialGroups()[0].StartIndex);
|
||||
}
|
||||
|
||||
void Renderer::InitializeTextures()
|
||||
|
||||
@@ -2,48 +2,48 @@
|
||||
|
||||
Texture::Texture(std::string path)
|
||||
{
|
||||
PNG image(path);
|
||||
PNG image(path);
|
||||
|
||||
if (image.Width == 0 && image.Height == 0 || image.Format == Image::ImageFormat::Unknown) {
|
||||
image = PNG("Textures/Core/ErrorTexture.png");
|
||||
if (image.Width == 0 && image.Height == 0 || image.Format == Image::ImageFormat::Unknown) {
|
||||
LOG_ERROR("Couldn't even load the error texture. This is a dark day indeed.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (image.Width == 0 && image.Height == 0 || image.Format == Image::ImageFormat::Unknown) {
|
||||
image = PNG("Textures/Core/ErrorTexture.png");
|
||||
if (image.Width == 0 && image.Height == 0 || image.Format == Image::ImageFormat::Unknown) {
|
||||
LOG_ERROR("Couldn't even load the error texture. This is a dark day indeed.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this->Width = image.Width;
|
||||
this->Height = image.Height;
|
||||
this->Width = image.Width;
|
||||
this->Height = image.Height;
|
||||
|
||||
GLint format;
|
||||
switch (image.Format) {
|
||||
case Image::ImageFormat::RGB:
|
||||
format = GL_RGB;
|
||||
break;
|
||||
case Image::ImageFormat::RGBA:
|
||||
format = GL_RGBA;
|
||||
break;
|
||||
}
|
||||
GLint format;
|
||||
switch (image.Format) {
|
||||
case Image::ImageFormat::RGB:
|
||||
format = GL_RGB;
|
||||
break;
|
||||
case Image::ImageFormat::RGBA:
|
||||
format = GL_RGBA;
|
||||
break;
|
||||
}
|
||||
|
||||
// Construct the OpenGL texture
|
||||
glGenTextures(1, &m_Texture);
|
||||
glBindTexture(GL_TEXTURE_2D, m_Texture);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, format, image.Width, image.Height, 0, format, GL_UNSIGNED_BYTE, image.Data);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
GLERROR("Texture load");
|
||||
// Construct the OpenGL texture
|
||||
glGenTextures(1, &m_Texture);
|
||||
glBindTexture(GL_TEXTURE_2D, m_Texture);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, format, image.Width, image.Height, 0, format, GL_UNSIGNED_BYTE, image.Data);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
GLERROR("Texture load");
|
||||
}
|
||||
|
||||
Texture::~Texture()
|
||||
{
|
||||
glDeleteTextures(1, &m_Texture);
|
||||
glDeleteTextures(1, &m_Texture);
|
||||
}
|
||||
|
||||
void Texture::Bind(GLenum textureUnit /* = GL_TEXTURE0 */)
|
||||
{
|
||||
glActiveTexture(textureUnit);
|
||||
glBindTexture(GL_TEXTURE_2D, m_Texture);
|
||||
glActiveTexture(textureUnit);
|
||||
glBindTexture(GL_TEXTURE_2D, m_Texture);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,304 @@
|
||||
#include "Sound/SoundSystem.h"
|
||||
|
||||
SoundSystem::SoundSystem(World* world, EventBroker* eventBroker, bool editorMode)
|
||||
{
|
||||
m_EventBroker = eventBroker;
|
||||
m_World = world;
|
||||
m_EditorEnabled = editorMode;
|
||||
|
||||
initOpenAL();
|
||||
|
||||
alSpeedOfSound(340.29f);
|
||||
alDistanceModel(AL_LINEAR_DISTANCE);
|
||||
alDopplerFactor(1);
|
||||
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlaySoundOnEntity, &SoundSystem::OnPlaySoundOnEntity);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlaySoundOnPosition, &SoundSystem::OnPlaySoundOnPosition);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlayBackgroundMusic, &SoundSystem::OnPlayBackgroundMusic);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EStopSound, &SoundSystem::OnStopSound);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPauseSound, &SoundSystem::OnPauseSound);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EContinueSound, &SoundSystem::OnContinueSound);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_ESetBGMGain, &SoundSystem::OnSetBGMGain);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_ESetSFXGain, &SoundSystem::OnSetSFXGain);
|
||||
}
|
||||
|
||||
SoundSystem::~SoundSystem()
|
||||
{
|
||||
stopEmitters(); // Stopps emitters
|
||||
deleteInactiveEmitters(); // Deletes stopped emitters
|
||||
// Delete entities
|
||||
std::unordered_map<EntityID, Source*>::iterator it;
|
||||
for (it = m_Sources.begin(); it != m_Sources.end(); it++) {
|
||||
m_World->DeleteEntity((*it).first);
|
||||
}
|
||||
m_Sources.clear();
|
||||
|
||||
alcDestroyContext(m_ALCcontext);
|
||||
alcCloseDevice(m_ALCdevice);
|
||||
}
|
||||
|
||||
void SoundSystem::stopEmitters()
|
||||
{
|
||||
std::unordered_map<EntityID, Source*>::iterator it;
|
||||
for (it = m_Sources.begin(); it != m_Sources.end(); it++) {
|
||||
if (getSourceState(it->second->ALsource) == AL_PLAYING) {
|
||||
stopSound(it->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SoundSystem::Update(double dt)
|
||||
{
|
||||
m_EventBroker->Process<SoundSystem>();
|
||||
addNewEmitters(dt); // can be optimized with "EEntityCreated"
|
||||
deleteInactiveEmitters(); // can be optimized with "EEntityDeleted"
|
||||
updateEmitters( dt);
|
||||
updateListener( dt);
|
||||
}
|
||||
|
||||
void SoundSystem::deleteInactiveEmitters()
|
||||
{
|
||||
std::unordered_map<EntityID, Source*>::iterator it;
|
||||
for (it = m_Sources.begin(); it != m_Sources.end();) {
|
||||
if (m_World->ValidEntity(it->first)
|
||||
&& m_World->HasComponent(it->first, "SoundEmitter")) {
|
||||
if (getSourceState(it->second->ALsource) != AL_STOPPED) {
|
||||
// Nothing to see here, move along
|
||||
it++;
|
||||
continue;
|
||||
} else {
|
||||
// Sound has been stopped / finished playing.
|
||||
alDeleteBuffers(1, &it->second->ALsource);
|
||||
alDeleteSources(1, &it->second->ALsource);
|
||||
m_World->DeleteEntity(it->first);
|
||||
delete it->second;
|
||||
it = m_Sources.erase(it);
|
||||
}
|
||||
} else {
|
||||
// Entity / Component has been removed
|
||||
stopSound((*it).second);
|
||||
alDeleteBuffers(1, &it->second->ALsource);
|
||||
alDeleteSources(1, &it->second->ALsource);
|
||||
delete it->second;
|
||||
it = m_Sources.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SoundSystem::addNewEmitters(double dt)
|
||||
{
|
||||
auto emitterComponents = m_World->GetComponents("SoundEmitter");
|
||||
if (emitterComponents == nullptr) {
|
||||
return;
|
||||
}
|
||||
for (auto it = emitterComponents->begin(); it != emitterComponents->end(); it++) {
|
||||
EntityID emitter = (*it).EntityID;
|
||||
std::unordered_map<EntityID, Source*>::iterator source;
|
||||
source = m_Sources.find(emitter);
|
||||
if (source == m_Sources.end()) { // Did not exist, add it
|
||||
Source* source = createSource((std::string)(*it)["FilePath"]);
|
||||
m_Sources[emitter] = source;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SoundSystem::updateEmitters(double dt)
|
||||
{
|
||||
std::unordered_map<EntityID, Source*>::iterator it;
|
||||
for (it = m_Sources.begin(); it != m_Sources.end(); it++) {
|
||||
// Get previous pos
|
||||
glm::vec3 previousPos;
|
||||
alGetSource3f(it->second->ALsource, AL_POSITION, &previousPos.x, &previousPos.y, &previousPos.z);
|
||||
// Get next pos
|
||||
glm::vec3 nextPos = Transform::AbsolutePosition(m_World, it->first);
|
||||
// Calculate velocity
|
||||
glm::vec3 velocity = glm::vec3(nextPos - previousPos) / (float)dt;
|
||||
setSourcePos(it->second->ALsource, nextPos);
|
||||
setSourceVel(it->second->ALsource, velocity);
|
||||
float gain;
|
||||
(bool)(it->second->Type) ? gain = m_SFXVolumeChannel : gain = m_BGMVolumeChannel;
|
||||
auto emitter = m_World->GetComponent(it->first, "SoundEmitter");
|
||||
setSoundProperties(it->second->ALsource, &emitter);
|
||||
|
||||
// To make an emitter play when spawned in editor mode
|
||||
if (m_EditorEnabled) {
|
||||
// Path changed
|
||||
if (it->second->SoundResource->Path() != (std::string)emitter["FilePath"]) {
|
||||
it->second->SoundResource = ResourceManager::Load<Sound>((std::string)emitter["FilePath"]);
|
||||
if (it->second->SoundResource->Buffer() != 0) {
|
||||
playSound(it->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SoundSystem::updateListener(double dt)
|
||||
{
|
||||
// Should only be one listener.
|
||||
auto listenerComponents = m_World->GetComponents("Listener");
|
||||
if (listenerComponents == nullptr) {
|
||||
return;
|
||||
}
|
||||
for (auto it = listenerComponents->begin(); it != listenerComponents->end(); it++) {
|
||||
EntityID listener = (*it).EntityID;
|
||||
glm::vec3 previousPos;
|
||||
alGetListener3f(AL_POSITION, &previousPos.x, &previousPos.y, &previousPos.z); // Get previous pos
|
||||
glm::vec3 nextPos = Transform::AbsolutePosition(m_World, listener); // Get next (current) pos
|
||||
glm::vec3 velocity = glm::vec3(nextPos - previousPos) / (float)dt; // Calculate velocity
|
||||
setListenerPos(nextPos);
|
||||
setListenerVel(velocity);
|
||||
setListenerOri(glm::eulerAngles(Transform::AbsoluteOrientation(m_World, listener)));
|
||||
}
|
||||
}
|
||||
|
||||
Source* SoundSystem::createSource(std::string filePath)
|
||||
{
|
||||
ALuint alSource;
|
||||
alGenSources((ALuint)1, &alSource);
|
||||
alSourcef(alSource, AL_REFERENCE_DISTANCE, 1.0);
|
||||
alSourcef(alSource, AL_MAX_DISTANCE, FLT_MAX);
|
||||
Source* source = new Source();
|
||||
source->ALsource = alSource;
|
||||
source->SoundResource = ResourceManager::Load<Sound>(filePath);
|
||||
return source;
|
||||
}
|
||||
|
||||
void SoundSystem::playSound(Source* source)
|
||||
{
|
||||
alSourcei(source->ALsource, AL_BUFFER, source->SoundResource->Buffer());
|
||||
alSourcePlay(source->ALsource);
|
||||
}
|
||||
|
||||
void SoundSystem::stopSound(Source* source)
|
||||
{
|
||||
alSourceStop(source->ALsource);
|
||||
}
|
||||
|
||||
bool SoundSystem::OnPlaySoundOnEntity(const Events::PlaySoundOnEntity & e)
|
||||
{
|
||||
Source* source = createSource(e.FilePath);
|
||||
source->Type = SoundType::SFX;
|
||||
m_Sources[e.EmitterID] = source;
|
||||
playSound(source);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SoundSystem::OnPlaySoundOnPosition(const Events::PlaySoundOnPosition & e)
|
||||
{
|
||||
Source* source = createSource(e.FilePath);
|
||||
auto emitterID = m_World->CreateEntity();
|
||||
auto transform = m_World->AttachComponent(emitterID, "Transform");
|
||||
(glm::vec3&)transform["Position"] = e.Position;
|
||||
auto emitter = m_World->AttachComponent(emitterID, "SoundEmitter");
|
||||
(float&)(double)emitter["Gain"] = e.Gain;
|
||||
(float&)(double)emitter["Pitch"] = e.Pitch;
|
||||
(bool&)emitter["Loop"] = e.Loop;
|
||||
(float&)(double)emitter["MaxDistance"] = e.MaxDistance;
|
||||
(float&)(double)emitter["RollOffFactor"] = e.RollOffFactor;
|
||||
(float&)(double)emitter["ReferenceDistance"] = e.ReferenceDistance;
|
||||
auto model = m_World->AttachComponent(emitterID, "Model");
|
||||
(std::string&)model["Resource"] = "Models/Core/UnitCube.obj";
|
||||
source->Type = SoundType::SFX;
|
||||
m_Sources[emitterID] = source;
|
||||
playSound(source);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SoundSystem::OnPauseSound(const Events::PauseSound & e)
|
||||
{
|
||||
alSourcePause(m_Sources[e.EmitterID]->ALsource);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SoundSystem::OnStopSound(const Events::StopSound & e)
|
||||
{
|
||||
alSourceStop(m_Sources[e.EmitterID]->ALsource);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SoundSystem::OnContinueSound(const Events::ContinueSound & e)
|
||||
{
|
||||
alSourcePlay(m_Sources[e.EmitterID]->ALsource);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SoundSystem::OnPlayBackgroundMusic(const Events::PlayBackgroundMusic & e)
|
||||
{
|
||||
auto listenerComponents = m_World->GetComponents("Listener");
|
||||
for (auto it = listenerComponents->begin(); it != listenerComponents->end(); it++) {
|
||||
auto emitterChild = m_World->CreateEntity((*it).EntityID);
|
||||
auto emitter = m_World->AttachComponent(emitterChild, "SoundEmitter");
|
||||
(bool&)emitter["Loop"] = true;
|
||||
(std::string&)emitter["FilePath"] = e.FilePath;
|
||||
m_World->AttachComponent(emitterChild, "Transform");
|
||||
Source* source = createSource(e.FilePath);
|
||||
source->Type = SoundType::BGM;
|
||||
m_Sources[emitterChild] = source;
|
||||
playSound(source);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SoundSystem::OnSetBGMGain(const Events::SetBGMGain & e)
|
||||
{
|
||||
m_BGMVolumeChannel = e.Gain;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SoundSystem::OnSetSFXGain(const Events::SetSFXGain & e)
|
||||
{
|
||||
m_SFXVolumeChannel = e.Gain;
|
||||
return true;
|
||||
}
|
||||
|
||||
void SoundSystem::setListenerOri(glm::vec3 ori)
|
||||
{
|
||||
// Calculate forward and up vector.
|
||||
glm::vec3 forward = glm::vec3(0.0, 0.0, -1.0);
|
||||
forward = glm::rotateX(forward, ori.x);
|
||||
forward = glm::rotateY(forward, ori.y);
|
||||
forward = glm::rotateZ(forward, ori.z);
|
||||
glm::normalize(forward);
|
||||
glm::vec3 up = glm::vec3(0.0, 1.0, 0.0);
|
||||
up = glm::rotateX(up, ori.x);
|
||||
up = glm::rotateY(up, ori.y);
|
||||
up = glm::rotateZ(up, ori.z);
|
||||
glm::normalize(up);
|
||||
ALfloat lOri[6] = { forward.x, forward.y, forward.z, up.x, up.y, up.z };
|
||||
alListenerfv(AL_ORIENTATION, lOri);
|
||||
}
|
||||
|
||||
ALenum SoundSystem::getSourceState(ALuint source)
|
||||
{
|
||||
ALenum state;
|
||||
alGetSourcei(source, AL_SOURCE_STATE, &state);
|
||||
return state;
|
||||
}
|
||||
|
||||
void SoundSystem::setGain(Source * source, float gain)
|
||||
{
|
||||
alSourcef(source->ALsource, AL_GAIN, gain);
|
||||
}
|
||||
|
||||
void SoundSystem::setSoundProperties(ALuint source, ComponentWrapper* soundComponent)
|
||||
{
|
||||
alSourcef(source, AL_GAIN, (float)(double)(*soundComponent)["Gain"]);
|
||||
alSourcef(source, AL_PITCH, (float)(double)(*soundComponent)["Pitch"]);
|
||||
alSourcei(source, AL_LOOPING, (int)(bool)(*soundComponent)["Loop"]); // YOLO
|
||||
alSourcef(source, AL_MAX_DISTANCE, (float)(double)(*soundComponent)["MaxDistance"]);
|
||||
alSourcef(source, AL_ROLLOFF_FACTOR, (float)(double)(*soundComponent)["RollOffFactor"]);
|
||||
alSourcef(source, AL_REFERENCE_DISTANCE, (float)(double)(*soundComponent)["ReferenceDistance"]);
|
||||
}
|
||||
|
||||
void SoundSystem::initOpenAL()
|
||||
{
|
||||
// Initialize OpenAL
|
||||
m_ALCdevice = alcOpenDevice(nullptr);
|
||||
if (m_ALCdevice != nullptr) {
|
||||
m_ALCcontext = alcCreateContext(m_ALCdevice, nullptr);
|
||||
alcMakeContextCurrent(m_ALCcontext);
|
||||
} else {
|
||||
LOG_ERROR("OpenAL failed to initialize.");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user