Merged 2D and 3D modules into Graphics module
Former-commit-id: 33bf0fbe727e50e864bc52680c95a106ada508e9
This commit is contained in:
8
src/Nazara/Graphics/Background.cpp
Normal file
8
src/Nazara/Graphics/Background.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
// Copyright (C) 2013 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/Background.hpp>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
NzBackground::~NzBackground() = default;
|
||||
258
src/Nazara/Graphics/Camera.cpp
Normal file
258
src/Nazara/Graphics/Camera.cpp
Normal file
@@ -0,0 +1,258 @@
|
||||
// Copyright (C) 2013 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/Camera.hpp>
|
||||
#include <Nazara/Graphics/Scene.hpp>
|
||||
#include <Nazara/Renderer/Renderer.hpp>
|
||||
#include <Nazara/Renderer/RenderTarget.hpp>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
NzCamera::NzCamera() :
|
||||
m_viewport(0.f, 0.f, 1.f, 1.f),
|
||||
m_upVector(NzVector3f::Up()),
|
||||
m_frustumUpdated(false),
|
||||
m_projectionMatrixUpdated(false),
|
||||
m_viewMatrixUpdated(false),
|
||||
m_aspectRatio(0.f),
|
||||
m_fov(70.f),
|
||||
m_zFar(100.f),
|
||||
m_zNear(1.f)
|
||||
{
|
||||
}
|
||||
|
||||
NzCamera::~NzCamera() = default;
|
||||
|
||||
void NzCamera::Activate() const
|
||||
{
|
||||
#ifdef NAZARA_3D_SAFE
|
||||
if (!m_target)
|
||||
{
|
||||
NazaraError("No render target !");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
NzRenderer::SetTarget(m_target);
|
||||
|
||||
unsigned int width = m_target->GetWidth();
|
||||
unsigned int height = std::max(m_target->GetHeight(), 1U);
|
||||
|
||||
float vWidth = width * m_viewport.width;
|
||||
float vHeight = height * m_viewport.height;
|
||||
|
||||
NzRectui viewport;
|
||||
viewport.x = width * m_viewport.x;
|
||||
viewport.y = height * m_viewport.x;
|
||||
viewport.width = vWidth;
|
||||
viewport.height = height * m_viewport.height;
|
||||
NzRenderer::SetViewport(viewport);
|
||||
|
||||
float aspectRatio = vWidth/vHeight;
|
||||
|
||||
if (!NzNumberEquals(m_aspectRatio, aspectRatio))
|
||||
{
|
||||
m_aspectRatio = aspectRatio;
|
||||
m_frustumUpdated = false;
|
||||
m_projectionMatrixUpdated = false;
|
||||
}
|
||||
|
||||
if (!m_projectionMatrixUpdated)
|
||||
UpdateProjectionMatrix();
|
||||
|
||||
if (!m_viewMatrixUpdated)
|
||||
UpdateViewMatrix();
|
||||
|
||||
NzRenderer::SetMatrix(nzMatrixType_Projection, m_projectionMatrix);
|
||||
NzRenderer::SetMatrix(nzMatrixType_View, m_viewMatrix);
|
||||
|
||||
if (m_scene)
|
||||
m_scene->SetActiveCamera(this);
|
||||
}
|
||||
|
||||
void NzCamera::EnsureFrustumUpdate() const
|
||||
{
|
||||
if (!m_frustumUpdated)
|
||||
UpdateFrustum();
|
||||
}
|
||||
|
||||
void NzCamera::EnsureProjectionMatrixUpdate() const
|
||||
{
|
||||
if (!m_projectionMatrixUpdated)
|
||||
UpdateProjectionMatrix();
|
||||
}
|
||||
|
||||
void NzCamera::EnsureViewMatrixUpdate() const
|
||||
{
|
||||
if (!m_viewMatrixUpdated)
|
||||
UpdateViewMatrix();
|
||||
}
|
||||
|
||||
float NzCamera::GetAspectRatio() const
|
||||
{
|
||||
return m_aspectRatio;
|
||||
}
|
||||
|
||||
const NzBoundingBoxf& NzCamera::GetBoundingBox() const
|
||||
{
|
||||
///TODO: Remplacer par la bounding box du Frustum ?
|
||||
static NzBoundingBoxf dummy(nzExtend_Null);
|
||||
return dummy;
|
||||
}
|
||||
|
||||
float NzCamera::GetFOV() const
|
||||
{
|
||||
return m_fov;
|
||||
}
|
||||
|
||||
const NzFrustumf& NzCamera::GetFrustum() const
|
||||
{
|
||||
if (!m_frustumUpdated)
|
||||
UpdateFrustum();
|
||||
|
||||
return m_frustum;
|
||||
}
|
||||
|
||||
const NzMatrix4f& NzCamera::GetProjectionMatrix() const
|
||||
{
|
||||
if (!m_projectionMatrixUpdated)
|
||||
UpdateProjectionMatrix();
|
||||
|
||||
return m_projectionMatrix;
|
||||
}
|
||||
|
||||
nzSceneNodeType NzCamera::GetSceneNodeType() const
|
||||
{
|
||||
return nzSceneNodeType_Camera;
|
||||
}
|
||||
|
||||
const NzRenderTarget* NzCamera::GetTarget() const
|
||||
{
|
||||
return m_target;
|
||||
}
|
||||
|
||||
const NzVector3f& NzCamera::GetUpVector() const
|
||||
{
|
||||
return m_upVector;
|
||||
}
|
||||
|
||||
const NzMatrix4f& NzCamera::GetViewMatrix() const
|
||||
{
|
||||
if (!m_viewMatrixUpdated)
|
||||
UpdateViewMatrix();
|
||||
|
||||
return m_viewMatrix;
|
||||
}
|
||||
|
||||
const NzRectf& NzCamera::GetViewport() const
|
||||
{
|
||||
return m_viewport;
|
||||
}
|
||||
|
||||
float NzCamera::GetZFar() const
|
||||
{
|
||||
return m_zFar;
|
||||
}
|
||||
|
||||
float NzCamera::GetZNear() const
|
||||
{
|
||||
return m_zNear;
|
||||
}
|
||||
|
||||
void NzCamera::SetFOV(float fov)
|
||||
{
|
||||
m_fov = fov;
|
||||
|
||||
m_frustumUpdated = false;
|
||||
m_projectionMatrixUpdated= false;
|
||||
}
|
||||
|
||||
void NzCamera::SetTarget(const NzRenderTarget* renderTarget)
|
||||
{
|
||||
m_target = renderTarget;
|
||||
}
|
||||
|
||||
void NzCamera::SetTarget(const NzRenderTarget& renderTarget)
|
||||
{
|
||||
SetTarget(&renderTarget);
|
||||
}
|
||||
|
||||
void NzCamera::SetUpVector(const NzVector3f& upVector)
|
||||
{
|
||||
m_upVector = upVector;
|
||||
|
||||
m_frustumUpdated = false;
|
||||
m_viewMatrixUpdated = false;
|
||||
}
|
||||
|
||||
void NzCamera::SetViewport(const NzRectf& viewport)
|
||||
{
|
||||
m_viewport = viewport;
|
||||
}
|
||||
|
||||
void NzCamera::SetZFar(float zFar)
|
||||
{
|
||||
m_zFar = zFar;
|
||||
|
||||
m_frustumUpdated = false;
|
||||
m_projectionMatrixUpdated = false;
|
||||
}
|
||||
|
||||
void NzCamera::SetZNear(float zNear)
|
||||
{
|
||||
m_zNear = zNear;
|
||||
|
||||
m_frustumUpdated = false;
|
||||
m_projectionMatrixUpdated = false;
|
||||
}
|
||||
|
||||
void NzCamera::AddToRenderQueue(NzRenderQueue& renderQueue) const
|
||||
{
|
||||
NazaraUnused(renderQueue);
|
||||
|
||||
NazaraInternalError("SceneNode::AddToRenderQueue() called on SceneRoot");
|
||||
}
|
||||
|
||||
void NzCamera::Invalidate()
|
||||
{
|
||||
NzSceneNode::Invalidate();
|
||||
|
||||
m_frustumUpdated = false;
|
||||
m_viewMatrixUpdated = false;
|
||||
}
|
||||
|
||||
void NzCamera::Register()
|
||||
{
|
||||
}
|
||||
|
||||
void NzCamera::Unregister()
|
||||
{
|
||||
}
|
||||
|
||||
void NzCamera::UpdateFrustum() const
|
||||
{
|
||||
m_frustum.Build(m_fov, m_aspectRatio, m_zNear, m_zFar, m_derivedPosition, m_derivedPosition + m_derivedRotation*NzVector3f::Forward(), m_upVector);
|
||||
m_frustumUpdated = true;
|
||||
}
|
||||
|
||||
void NzCamera::UpdateProjectionMatrix() const
|
||||
{
|
||||
m_projectionMatrix.MakePerspective(m_fov, m_aspectRatio, m_zNear, m_zFar);
|
||||
m_projectionMatrixUpdated = true;
|
||||
}
|
||||
|
||||
void NzCamera::UpdateViewMatrix() const
|
||||
{
|
||||
if (!m_derivedUpdated)
|
||||
UpdateDerived();
|
||||
|
||||
m_viewMatrix.MakeLookAt(m_derivedPosition, m_derivedPosition + m_derivedRotation*NzVector3f::Forward(), m_upVector);
|
||||
m_viewMatrixUpdated = true;
|
||||
}
|
||||
|
||||
bool NzCamera::VisibilityTest(const NzFrustumf& frustum)
|
||||
{
|
||||
NazaraUnused(frustum);
|
||||
//NazaraInternalError("SceneNode::IsVisible() called on Camera");
|
||||
return false;
|
||||
}
|
||||
33
src/Nazara/Graphics/ColorBackground.cpp
Normal file
33
src/Nazara/Graphics/ColorBackground.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright (C) 2013 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/ColorBackground.hpp>
|
||||
#include <Nazara/Renderer/Renderer.hpp>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
NzColorBackground::NzColorBackground(const NzColor& color) :
|
||||
m_color(color)
|
||||
{
|
||||
}
|
||||
|
||||
void NzColorBackground::Draw() const
|
||||
{
|
||||
NzRenderer::SetClearColor(m_color);
|
||||
NzRenderer::Clear(nzRendererClear_Color);
|
||||
}
|
||||
|
||||
nzBackgroundType NzColorBackground::GetBackgroundType() const
|
||||
{
|
||||
return nzBackgroundType_Color;
|
||||
}
|
||||
|
||||
NzColor NzColorBackground::GetColor() const
|
||||
{
|
||||
return m_color;
|
||||
}
|
||||
|
||||
void NzColorBackground::SetColor(const NzColor& color)
|
||||
{
|
||||
m_color = color;
|
||||
}
|
||||
29
src/Nazara/Graphics/Debug/Leaks.cpp
Normal file
29
src/Nazara/Graphics/Debug/Leaks.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright (C) 2013 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/Config.hpp>
|
||||
#if NAZARA_GRAPHICS_MEMORYLEAKTRACKER || defined(NAZARA_DEBUG)
|
||||
#include <Nazara/Core/Debug/MemoryLeakTracker.hpp>
|
||||
#include <new>
|
||||
|
||||
void* operator new(std::size_t size)
|
||||
{
|
||||
return NzMemoryManager::Allocate(size, false);
|
||||
}
|
||||
|
||||
void* operator new[](std::size_t size)
|
||||
{
|
||||
return NzMemoryManager::Allocate(size, true);
|
||||
}
|
||||
|
||||
void operator delete(void* pointer) noexcept
|
||||
{
|
||||
NzMemoryManager::Free(pointer, false);
|
||||
}
|
||||
|
||||
void operator delete[](void* pointer) noexcept
|
||||
{
|
||||
NzMemoryManager::Free(pointer, true);
|
||||
}
|
||||
#endif
|
||||
8
src/Nazara/Graphics/Drawable.cpp
Normal file
8
src/Nazara/Graphics/Drawable.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
// Copyright (C) 2013 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/Drawable.hpp>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
NzDrawable::~NzDrawable() = default;
|
||||
70
src/Nazara/Graphics/Graphics.cpp
Normal file
70
src/Nazara/Graphics/Graphics.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
// Copyright (C) 2013 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/Graphics.hpp>
|
||||
#include <Nazara/Graphics/Config.hpp>
|
||||
#include <Nazara/Graphics/Loaders/Mesh.hpp>
|
||||
#include <Nazara/Graphics/Loaders/OBJ.hpp>
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <Nazara/Core/Log.hpp>
|
||||
#include <Nazara/Renderer/Renderer.hpp>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
bool Nz3D::Initialize()
|
||||
{
|
||||
if (s_moduleReferenceCounter++ != 0)
|
||||
return true; // Déjà initialisé
|
||||
|
||||
// Initialisation des dépendances
|
||||
if (!NzRenderer::Initialize())
|
||||
{
|
||||
NazaraError("Failed to initialize Renderer module");
|
||||
Uninitialize();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialisation du module
|
||||
|
||||
// Loaders
|
||||
NzLoaders_OBJ_Register();
|
||||
|
||||
// Loader générique
|
||||
NzLoaders_Mesh_Register();
|
||||
|
||||
NazaraNotice("Initialized: Graphics module");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Nz3D::IsInitialized()
|
||||
{
|
||||
return s_moduleReferenceCounter != 0;
|
||||
}
|
||||
|
||||
void Nz3D::Uninitialize()
|
||||
{
|
||||
if (s_moduleReferenceCounter != 1)
|
||||
{
|
||||
// Le module est soit encore utilisé, soit pas initialisé
|
||||
if (s_moduleReferenceCounter > 1)
|
||||
s_moduleReferenceCounter--;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Libération du module
|
||||
s_moduleReferenceCounter = 0;
|
||||
|
||||
// Loaders
|
||||
NzLoaders_Mesh_Unregister();
|
||||
NzLoaders_OBJ_Unregister();
|
||||
|
||||
NazaraNotice("Uninitialized: Graphics module");
|
||||
|
||||
// Libération des dépendances
|
||||
Nz2D::Uninitialize();
|
||||
}
|
||||
|
||||
unsigned int Nz3D::s_moduleReferenceCounter = 0;
|
||||
339
src/Nazara/Graphics/Light.cpp
Normal file
339
src/Nazara/Graphics/Light.cpp
Normal file
@@ -0,0 +1,339 @@
|
||||
// Copyright (C) 2013 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/Light.hpp>
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <Nazara/Math/Basic.hpp>
|
||||
#include <Nazara/Math/Sphere.hpp>
|
||||
#include <Nazara/Renderer/Renderer.hpp>
|
||||
#include <Nazara/Renderer/Shader.hpp>
|
||||
#include <cstring>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
///TODO: Utilisation des UBOs
|
||||
|
||||
NzLight::NzLight(nzLightType type) :
|
||||
m_type(type),
|
||||
m_ambientColor((type == nzLightType_Directional) ? NzColor(50, 50, 50) : NzColor::Black),
|
||||
m_diffuseColor(NzColor::White),
|
||||
m_specularColor(NzColor::White),
|
||||
m_boundingBoxUpdated(false),
|
||||
m_attenuation(0.9f),
|
||||
m_innerAngle(15.f),
|
||||
m_outerAngle(45.f),
|
||||
m_radius(500.f)
|
||||
{
|
||||
}
|
||||
|
||||
NzLight::NzLight(const NzLight& light) :
|
||||
NzSceneNode(light)
|
||||
{
|
||||
std::memcpy(this, &light, sizeof(NzLight)); // Aussi simple que ça
|
||||
}
|
||||
|
||||
NzLight::~NzLight()
|
||||
{
|
||||
}
|
||||
|
||||
void NzLight::AddToRenderQueue(NzRenderQueue& renderQueue) const
|
||||
{
|
||||
switch (m_type)
|
||||
{
|
||||
case nzLightType_Directional:
|
||||
renderQueue.directionnalLights.push_back(this);
|
||||
break;
|
||||
|
||||
case nzLightType_Point:
|
||||
case nzLightType_Spot:
|
||||
renderQueue.visibleLights.push_back(this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void NzLight::Apply(const NzShader* shader, unsigned int lightUnit) const
|
||||
{
|
||||
/*
|
||||
struct Light
|
||||
{
|
||||
int type;
|
||||
vec4 ambient;
|
||||
vec4 diffuse;
|
||||
vec4 specular;
|
||||
|
||||
vec4 parameters1;
|
||||
vec4 parameters2;
|
||||
vec2 parameters3;
|
||||
};
|
||||
|
||||
Directional
|
||||
-P1: vec3 direction
|
||||
|
||||
Point
|
||||
-P1: vec3 position + float attenuation
|
||||
-P2: float invRadius
|
||||
|
||||
Spot
|
||||
-P1: vec3 position + float attenuation
|
||||
-P2: vec3 direction + float invRadius
|
||||
-P3: float cosInnerAngle + float cosOuterAngle
|
||||
*/
|
||||
|
||||
int typeLocation = shader->GetUniformLocation("Lights[0].type");
|
||||
int ambientLocation = shader->GetUniformLocation("Lights[0].ambient");
|
||||
int diffuseLocation = shader->GetUniformLocation("Lights[0].diffuse");
|
||||
int specularLocation = shader->GetUniformLocation("Lights[0].specular");
|
||||
int parameters1Location = shader->GetUniformLocation("Lights[0].parameters1");
|
||||
int parameters2Location = shader->GetUniformLocation("Lights[0].parameters2");
|
||||
int parameters3Location = shader->GetUniformLocation("Lights[0].parameters3");
|
||||
|
||||
if (lightUnit > 0)
|
||||
{
|
||||
int type2Location = shader->GetUniformLocation("Lights[1].type");
|
||||
int offset = lightUnit * (type2Location - typeLocation); // type2Location - typeLocation donne la taille de la structure
|
||||
|
||||
// On applique cet offset
|
||||
typeLocation += offset;
|
||||
ambientLocation += offset;
|
||||
diffuseLocation += offset;
|
||||
specularLocation += offset;
|
||||
parameters1Location += offset;
|
||||
parameters2Location += offset;
|
||||
parameters3Location += offset;
|
||||
}
|
||||
|
||||
shader->SendInteger(typeLocation, m_type);
|
||||
shader->SendColor(ambientLocation, m_ambientColor);
|
||||
shader->SendColor(diffuseLocation, m_diffuseColor);
|
||||
shader->SendColor(specularLocation, m_specularColor);
|
||||
|
||||
if (!m_derivedUpdated)
|
||||
UpdateDerived();
|
||||
|
||||
switch (m_type)
|
||||
{
|
||||
case nzLightType_Directional:
|
||||
shader->SendVector(parameters1Location, NzVector4f(m_derivedRotation * NzVector3f::Forward()));
|
||||
break;
|
||||
|
||||
case nzLightType_Point:
|
||||
shader->SendVector(parameters1Location, NzVector4f(m_derivedPosition, m_attenuation));
|
||||
shader->SendVector(parameters2Location, NzVector4f(1.f/m_radius, 0.f, 0.f, 0.f));
|
||||
break;
|
||||
|
||||
case nzLightType_Spot:
|
||||
shader->SendVector(parameters1Location, NzVector4f(m_derivedPosition, m_attenuation));
|
||||
shader->SendVector(parameters2Location, NzVector4f(m_derivedRotation * NzVector3f::Forward(), 1.f/m_radius));
|
||||
shader->SendVector(parameters3Location, NzVector2f(std::cos(NzDegreeToRadian(m_innerAngle)), std::cos(NzDegreeToRadian(m_outerAngle))));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const NzBoundingBoxf& NzLight::GetBoundingBox() const
|
||||
{
|
||||
if (!m_boundingBoxUpdated)
|
||||
UpdateBoundingBox();
|
||||
|
||||
return m_boundingBox;
|
||||
}
|
||||
|
||||
NzColor NzLight::GetAmbientColor() const
|
||||
{
|
||||
return m_ambientColor;
|
||||
}
|
||||
|
||||
float NzLight::GetAttenuation() const
|
||||
{
|
||||
return m_attenuation;
|
||||
}
|
||||
|
||||
NzColor NzLight::GetDiffuseColor() const
|
||||
{
|
||||
return m_diffuseColor;
|
||||
}
|
||||
|
||||
float NzLight::GetInnerAngle() const
|
||||
{
|
||||
return m_innerAngle;
|
||||
}
|
||||
|
||||
nzLightType NzLight::GetLightType() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
float NzLight::GetOuterAngle() const
|
||||
{
|
||||
return m_outerAngle;
|
||||
}
|
||||
|
||||
float NzLight::GetRadius() const
|
||||
{
|
||||
return m_radius;
|
||||
}
|
||||
|
||||
nzSceneNodeType NzLight::GetSceneNodeType() const
|
||||
{
|
||||
return nzSceneNodeType_Light;
|
||||
}
|
||||
|
||||
NzColor NzLight::GetSpecularColor() const
|
||||
{
|
||||
return m_specularColor;
|
||||
}
|
||||
|
||||
void NzLight::SetAmbientColor(const NzColor& ambient)
|
||||
{
|
||||
m_ambientColor = ambient;
|
||||
}
|
||||
|
||||
void NzLight::SetAttenuation(float attenuation)
|
||||
{
|
||||
m_attenuation = attenuation;
|
||||
}
|
||||
|
||||
void NzLight::SetDiffuseColor(const NzColor& diffuse)
|
||||
{
|
||||
m_diffuseColor = diffuse;
|
||||
}
|
||||
|
||||
void NzLight::SetInnerAngle(float innerAngle)
|
||||
{
|
||||
m_innerAngle = innerAngle;
|
||||
}
|
||||
|
||||
void NzLight::SetOuterAngle(float outerAngle)
|
||||
{
|
||||
m_outerAngle = outerAngle;
|
||||
|
||||
m_boundingBox.MakeNull();
|
||||
m_boundingBoxUpdated = false;
|
||||
}
|
||||
|
||||
void NzLight::SetRadius(float radius)
|
||||
{
|
||||
m_radius = radius;
|
||||
|
||||
m_boundingBox.MakeNull();
|
||||
m_boundingBoxUpdated = false;
|
||||
}
|
||||
|
||||
void NzLight::SetSpecularColor(const NzColor& specular)
|
||||
{
|
||||
m_specularColor = specular;
|
||||
}
|
||||
|
||||
NzLight& NzLight::operator=(const NzLight& light)
|
||||
{
|
||||
std::memcpy(this, &light, sizeof(NzLight));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void NzLight::Invalidate()
|
||||
{
|
||||
NzSceneNode::Invalidate();
|
||||
|
||||
m_boundingBoxUpdated = false;
|
||||
}
|
||||
|
||||
void NzLight::Register()
|
||||
{
|
||||
}
|
||||
|
||||
void NzLight::Unregister()
|
||||
{
|
||||
}
|
||||
|
||||
void NzLight::UpdateBoundingBox() const
|
||||
{
|
||||
if (m_boundingBox.IsNull())
|
||||
{
|
||||
switch (m_type)
|
||||
{
|
||||
case nzLightType_Directional:
|
||||
m_boundingBox.MakeInfinite();
|
||||
m_boundingBoxUpdated = true;
|
||||
return; // Rien d'autre à faire
|
||||
|
||||
case nzLightType_Point:
|
||||
{
|
||||
NzVector3f radius(m_radius);
|
||||
m_boundingBox.Set(-radius, radius);
|
||||
break;
|
||||
}
|
||||
|
||||
case nzLightType_Spot:
|
||||
{
|
||||
// On forme un cube sur l'origine
|
||||
NzCubef cube(NzVector3f::Zero());
|
||||
|
||||
// On calcule le reste des points
|
||||
float height = m_radius;
|
||||
NzVector3f base(NzVector3f::Forward()*height);
|
||||
|
||||
// Il nous faut maintenant le rayon du cercle projeté à cette distance
|
||||
// Tangente = Opposé/Adjaçent <=> Opposé = Adjaçent*Tangente
|
||||
float radius = height*std::tan(NzDegreeToRadian(m_outerAngle));
|
||||
NzVector3f lExtend = NzVector3f::Left()*radius;
|
||||
NzVector3f uExtend = NzVector3f::Up()*radius;
|
||||
|
||||
// Et on ajoute ensuite les quatres extrêmités de la pyramide
|
||||
cube.ExtendTo(base + lExtend + uExtend);
|
||||
cube.ExtendTo(base + lExtend - uExtend);
|
||||
cube.ExtendTo(base - lExtend + uExtend);
|
||||
cube.ExtendTo(base - lExtend - uExtend);
|
||||
|
||||
m_boundingBox.Set(cube);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (m_type)
|
||||
{
|
||||
case nzLightType_Directional:
|
||||
break;
|
||||
|
||||
case nzLightType_Point:
|
||||
if (!m_derivedUpdated)
|
||||
UpdateDerived();
|
||||
|
||||
m_boundingBox.Update(NzMatrix4f::Translate(m_derivedPosition)); // Notre BoundingBox ne changera que selon la position
|
||||
break;
|
||||
|
||||
case nzLightType_Spot:
|
||||
if (!m_transformMatrixUpdated)
|
||||
UpdateTransformMatrix();
|
||||
|
||||
m_boundingBox.Update(m_transformMatrix);
|
||||
break;
|
||||
}
|
||||
|
||||
m_boundingBoxUpdated = true;
|
||||
}
|
||||
|
||||
bool NzLight::VisibilityTest(const NzFrustumf& frustum)
|
||||
{
|
||||
switch (m_type)
|
||||
{
|
||||
case nzLightType_Directional:
|
||||
return true; // Toujours visible
|
||||
|
||||
case nzLightType_Point:
|
||||
if (!m_derivedUpdated)
|
||||
UpdateDerived();
|
||||
|
||||
// Un test sphérique est bien plus rapide et précis que celui de la bounding box
|
||||
return frustum.Contains(NzSpheref(m_derivedPosition, m_radius));
|
||||
|
||||
case nzLightType_Spot:
|
||||
if (!m_boundingBoxUpdated)
|
||||
UpdateBoundingBox();
|
||||
|
||||
return frustum.Contains(m_boundingBox);
|
||||
}
|
||||
|
||||
NazaraError("Invalid light type (0x" + NzString::Number(m_type, 16) + ')');
|
||||
return false;
|
||||
}
|
||||
15
src/Nazara/Graphics/Loaders/Mesh.hpp
Normal file
15
src/Nazara/Graphics/Loaders/Mesh.hpp
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright (C) 2013 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef NAZARA_LOADERS_MESH_HPP
|
||||
#define NAZARA_LOADERS_MESH_HPP
|
||||
|
||||
#include <Nazara/Prerequesites.hpp>
|
||||
|
||||
void NzLoaders_Mesh_Register();
|
||||
void NzLoaders_Mesh_Unregister();
|
||||
|
||||
#endif // NAZARA_LOADERS_MESH_HPP
|
||||
89
src/Nazara/Graphics/Loaders/Mesh/Loader.cpp
Normal file
89
src/Nazara/Graphics/Loaders/Mesh/Loader.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
// Copyright (C) 2013 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/Loaders/Mesh.hpp>
|
||||
#include <Nazara/Graphics/Model.hpp>
|
||||
#include <Nazara/Renderer/Material.hpp>
|
||||
#include <Nazara/Utility/Mesh.hpp>
|
||||
#include <memory>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
namespace
|
||||
{
|
||||
bool Check(NzInputStream& stream, const NzModelParameters& parameters)
|
||||
{
|
||||
NazaraUnused(stream);
|
||||
NazaraUnused(parameters);
|
||||
|
||||
return true; ///FIXME: Pas bon
|
||||
}
|
||||
|
||||
bool Load(NzModel* model, NzInputStream& stream, const NzModelParameters& parameters)
|
||||
{
|
||||
NazaraUnused(parameters);
|
||||
|
||||
std::unique_ptr<NzMesh> mesh(new NzMesh);
|
||||
mesh->SetPersistent(false);
|
||||
if (!mesh->LoadFromStream(stream))
|
||||
{
|
||||
NazaraError("Failed to load model mesh");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Nous ne pouvons plus avoir recours au smart pointeur à partir d'ici si nous voulons être exception-safe
|
||||
NzMesh* meshPtr = mesh.get();
|
||||
|
||||
model->Reset();
|
||||
model->SetMesh(meshPtr);
|
||||
mesh.release();
|
||||
|
||||
if (parameters.loadAnimation && meshPtr->IsAnimable())
|
||||
{
|
||||
NzString animationPath = meshPtr->GetAnimation();
|
||||
if (!animationPath.IsEmpty())
|
||||
{
|
||||
std::unique_ptr<NzAnimation> animation(new NzAnimation);
|
||||
animation->SetPersistent(false);
|
||||
if (animation->LoadFromFile(animationPath, parameters.animation) && model->SetAnimation(animation.get()))
|
||||
animation.release();
|
||||
else
|
||||
NazaraWarning("Failed to load animation");
|
||||
}
|
||||
}
|
||||
|
||||
if (parameters.loadMaterials)
|
||||
{
|
||||
unsigned int matCount = model->GetMaterialCount();
|
||||
|
||||
for (unsigned int i = 0; i < matCount; ++i)
|
||||
{
|
||||
NzString mat = meshPtr->GetMaterial(i);
|
||||
if (!mat.IsEmpty())
|
||||
{
|
||||
std::unique_ptr<NzMaterial> material(new NzMaterial);
|
||||
material->SetPersistent(false);
|
||||
if (material->LoadFromFile(mat, parameters.material))
|
||||
{
|
||||
model->SetMaterial(i, material.get());
|
||||
material.release();
|
||||
}
|
||||
else
|
||||
NazaraWarning("Failed to load material #" + NzString::Number(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void NzLoaders_Mesh_Register()
|
||||
{
|
||||
NzModelLoader::RegisterLoader(NzMeshLoader::IsExtensionSupported, Check, Load);
|
||||
}
|
||||
|
||||
void NzLoaders_Mesh_Unregister()
|
||||
{
|
||||
NzModelLoader::UnregisterLoader(NzMeshLoader::IsExtensionSupported, Check, Load);
|
||||
}
|
||||
15
src/Nazara/Graphics/Loaders/OBJ.hpp
Normal file
15
src/Nazara/Graphics/Loaders/OBJ.hpp
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright (C) 2013 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef NAZARA_LOADERS_OBJ_HPP
|
||||
#define NAZARA_LOADERS_OBJ_HPP
|
||||
|
||||
#include <Nazara/Prerequesites.hpp>
|
||||
|
||||
void NzLoaders_OBJ_Register();
|
||||
void NzLoaders_OBJ_Unregister();
|
||||
|
||||
#endif // NAZARA_LOADERS_OBJ_HPP
|
||||
271
src/Nazara/Graphics/Loaders/OBJ/Loader.cpp
Normal file
271
src/Nazara/Graphics/Loaders/OBJ/Loader.cpp
Normal file
@@ -0,0 +1,271 @@
|
||||
// Copyright (C) 2013 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/Loaders/OBJ.hpp>
|
||||
#include <Nazara/Graphics/Loaders/OBJ/MTLParser.hpp>
|
||||
#include <Nazara/Graphics/Loaders/OBJ/OBJParser.hpp>
|
||||
#include <Nazara/Graphics/Model.hpp>
|
||||
#include <Nazara/Renderer/Material.hpp>
|
||||
#include <Nazara/Utility/BufferMapper.hpp>
|
||||
#include <Nazara/Utility/IndexMapper.hpp>
|
||||
#include <Nazara/Utility/Mesh.hpp>
|
||||
#include <Nazara/Utility/StaticMesh.hpp>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
namespace
|
||||
{
|
||||
bool IsSupported(const NzString& extension)
|
||||
{
|
||||
return (extension == "obj");
|
||||
}
|
||||
|
||||
bool Check(NzInputStream& stream, const NzModelParameters& parameters)
|
||||
{
|
||||
NazaraUnused(stream);
|
||||
NazaraUnused(parameters);
|
||||
|
||||
return true; ///FIXME: Pas bon
|
||||
}
|
||||
|
||||
bool Load(NzModel* model, NzInputStream& stream, const NzModelParameters& parameters)
|
||||
{
|
||||
NzOBJParser parser(stream);
|
||||
if (!parser.Parse())
|
||||
{
|
||||
NazaraError("OBJ parser failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<NzMesh> mesh(new NzMesh);
|
||||
mesh->SetPersistent(false);
|
||||
if (!mesh->CreateStatic()) // Ne devrait jamais échouer
|
||||
{
|
||||
NazaraInternalError("Failed to create mesh");
|
||||
return false;
|
||||
}
|
||||
|
||||
const NzString* materials = parser.GetMaterials();
|
||||
const NzVector4f* positions = parser.GetPositions();
|
||||
const NzVector3f* normals = parser.GetNormals();
|
||||
const NzVector3f* texCoords = parser.GetTexCoords();
|
||||
|
||||
std::vector<unsigned int> faceIndices;
|
||||
|
||||
const NzOBJParser::Mesh* meshes = parser.GetMeshes();
|
||||
unsigned int meshCount = parser.GetMeshCount();
|
||||
for (unsigned int i = 0; i < meshCount; ++i)
|
||||
{
|
||||
unsigned int faceCount = meshes[i].faces.size();
|
||||
|
||||
std::vector<unsigned int> indices;
|
||||
indices.reserve(faceCount*3); // Pire cas (si les faces sont des triangles)
|
||||
|
||||
// Bien plus rapide qu'un vector (pour la recherche)
|
||||
std::unordered_map<int, std::unordered_map<int, std::unordered_map<int, unsigned int>>> vertices;
|
||||
|
||||
unsigned int vertexCount = 0;
|
||||
for (unsigned int j = 0; j < faceCount; ++j)
|
||||
{
|
||||
unsigned int faceVertexCount = meshes[i].faces[j].vertices.size();
|
||||
faceIndices.resize(faceVertexCount);
|
||||
|
||||
for (unsigned int k = 0; k < faceVertexCount; ++k)
|
||||
{
|
||||
const NzOBJParser::FaceVertex& vertex = meshes[i].faces[j].vertices[k];
|
||||
|
||||
auto& map = vertices[vertex.texCoord][vertex.normal];
|
||||
auto it = map.find(vertex.position);
|
||||
if (it == map.end())
|
||||
{
|
||||
faceIndices[k] = vertexCount;
|
||||
map[vertex.position] = vertexCount++;
|
||||
}
|
||||
else
|
||||
faceIndices[k] = it->second;
|
||||
}
|
||||
|
||||
for (unsigned int k = 1; k < faceVertexCount-1; ++k)
|
||||
{
|
||||
indices.push_back(faceIndices[0]);
|
||||
indices.push_back(faceIndices[k]);
|
||||
indices.push_back(faceIndices[k+1]);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<NzIndexBuffer> indexBuffer(new NzIndexBuffer(indices.size(), vertexCount > std::numeric_limits<nzUInt16>::max(), parameters.mesh.storage, nzBufferUsage_Static));
|
||||
indexBuffer->SetPersistent(false);
|
||||
|
||||
std::unique_ptr<NzVertexBuffer> vertexBuffer(new NzVertexBuffer(NzMesh::GetDeclaration(), vertexCount, parameters.mesh.storage, nzBufferUsage_Static));
|
||||
vertexBuffer->SetPersistent(false);
|
||||
|
||||
// Remplissage des indices
|
||||
NzIndexMapper indexMapper(indexBuffer.get(), nzBufferAccess_WriteOnly);
|
||||
for (unsigned int j = 0; j < indices.size(); ++j)
|
||||
indexMapper.Set(j, indices[j]);
|
||||
|
||||
indexMapper.Unmap();
|
||||
|
||||
// Remplissage des vertices
|
||||
bool hasNormals = true;
|
||||
bool hasTexCoords = true;
|
||||
NzBufferMapper<NzVertexBuffer> vertexMapper(vertexBuffer.get(), nzBufferAccess_WriteOnly);
|
||||
NzMeshVertex* meshVertices = static_cast<NzMeshVertex*>(vertexMapper.GetPointer());
|
||||
for (auto uvIt : vertices)
|
||||
{
|
||||
for (auto normalIt : uvIt.second)
|
||||
{
|
||||
for (auto positionIt : normalIt.second)
|
||||
{
|
||||
NzMeshVertex& vertex = meshVertices[positionIt.second];
|
||||
|
||||
const NzVector4f& vec = positions[positionIt.first];
|
||||
vertex.position.Set(vec.x/vec.w, vec.y/vec.w, vec.z/vec.w);
|
||||
|
||||
int index;
|
||||
|
||||
index = normalIt.first; // Normale
|
||||
if (index >= 0)
|
||||
vertex.normal = normals[index];
|
||||
else
|
||||
hasNormals = false;
|
||||
|
||||
index = uvIt.first; // Coordonnées de texture
|
||||
if (index >= 0)
|
||||
{
|
||||
const NzVector3f& uvw = texCoords[index];
|
||||
vertex.uv.Set(uvw.x, uvw.y);
|
||||
}
|
||||
else
|
||||
hasTexCoords = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vertexMapper.Unmap();
|
||||
|
||||
std::unique_ptr<NzStaticMesh> subMesh(new NzStaticMesh(mesh.get()));
|
||||
if (!subMesh->Create(vertexBuffer.get()))
|
||||
{
|
||||
NazaraError("Failed to create StaticMesh");
|
||||
continue;
|
||||
}
|
||||
vertexBuffer.release();
|
||||
|
||||
subMesh->SetIndexBuffer(indexBuffer.get());
|
||||
indexBuffer.release();
|
||||
|
||||
subMesh->GenerateAABB();
|
||||
subMesh->SetMaterialIndex(meshes[i].material);
|
||||
subMesh->SetPrimitiveType(nzPrimitiveType_TriangleList);
|
||||
|
||||
if (hasNormals && hasTexCoords)
|
||||
subMesh->GenerateTangents();
|
||||
else if (hasTexCoords)
|
||||
subMesh->GenerateNormalsAndTangents();
|
||||
else
|
||||
subMesh->GenerateNormals();
|
||||
|
||||
if (mesh->AddSubMesh(meshes[i].name + '_' + materials[meshes[i].material], subMesh.get()))
|
||||
subMesh.release();
|
||||
else
|
||||
NazaraError("Failed to add SubMesh to Mesh");
|
||||
}
|
||||
|
||||
mesh->SetMaterialCount(parser.GetMaterialCount());
|
||||
|
||||
model->SetMesh(mesh.get());
|
||||
mesh.release();
|
||||
|
||||
// On charge les matériaux si demandé
|
||||
NzString mtlLib = parser.GetMtlLib();
|
||||
if (parameters.loadMaterials && !mtlLib.IsEmpty())
|
||||
{
|
||||
NzFile file(stream.GetDirectory() + mtlLib);
|
||||
if (file.Open(NzFile::ReadOnly | NzFile::Text))
|
||||
{
|
||||
NzMTLParser materialParser(file);
|
||||
if (materialParser.Parse())
|
||||
{
|
||||
NzString baseDir = file.GetDirectory();
|
||||
for (unsigned int i = 0; i < meshCount; ++i)
|
||||
{
|
||||
const NzString& matName = materials[meshes[i].material];
|
||||
const NzMTLParser::Material* mtlMat = materialParser.GetMaterial(matName);
|
||||
if (mtlMat)
|
||||
{
|
||||
std::unique_ptr<NzMaterial> material(new NzMaterial);
|
||||
material->SetPersistent(false);
|
||||
|
||||
NzColor ambientColor(mtlMat->ambient);
|
||||
ambientColor.a = mtlMat->alpha;
|
||||
|
||||
NzColor diffuseColor(mtlMat->diffuse);
|
||||
diffuseColor.a = mtlMat->alpha;
|
||||
|
||||
NzColor specularColor(mtlMat->specular);
|
||||
specularColor.a = mtlMat->alpha;
|
||||
|
||||
material->SetAmbientColor(ambientColor);
|
||||
material->SetDiffuseColor(diffuseColor);
|
||||
material->SetSpecularColor(specularColor);
|
||||
material->SetShininess(mtlMat->shininess);
|
||||
|
||||
if (parameters.material.loadDiffuseMap && !mtlMat->diffuseMap.IsEmpty())
|
||||
{
|
||||
std::unique_ptr<NzTexture> diffuseMap(new NzTexture);
|
||||
diffuseMap->SetPersistent(false);
|
||||
|
||||
if (diffuseMap->LoadFromFile(baseDir + mtlMat->diffuseMap))
|
||||
{
|
||||
material->SetDiffuseMap(diffuseMap.get());
|
||||
diffuseMap.release();
|
||||
}
|
||||
else
|
||||
NazaraWarning("Failed to load diffuse map (" + mtlMat->diffuseMap + ')');
|
||||
}
|
||||
|
||||
if (parameters.material.loadSpecularMap && !mtlMat->specularMap.IsEmpty())
|
||||
{
|
||||
std::unique_ptr<NzTexture> specularMap(new NzTexture);
|
||||
specularMap->SetPersistent(false);
|
||||
|
||||
if (specularMap->LoadFromFile(baseDir + mtlMat->specularMap))
|
||||
{
|
||||
material->SetSpecularMap(specularMap.get());
|
||||
specularMap.release();
|
||||
}
|
||||
else
|
||||
NazaraWarning("Failed to load specular map (" + mtlMat->diffuseMap + ')');
|
||||
}
|
||||
|
||||
model->SetMaterial(meshes[i].material, material.get());
|
||||
material.release();
|
||||
}
|
||||
else
|
||||
NazaraWarning("MTL has no material \"" + matName + '"');
|
||||
}
|
||||
}
|
||||
else
|
||||
NazaraWarning("MTL parser failed");
|
||||
}
|
||||
else
|
||||
NazaraWarning("Failed to open MTL file (" + file.GetPath() + ')');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void NzLoaders_OBJ_Register()
|
||||
{
|
||||
NzModelLoader::RegisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
|
||||
void NzLoaders_OBJ_Unregister()
|
||||
{
|
||||
NzModelLoader::UnregisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
337
src/Nazara/Graphics/Loaders/OBJ/MTLParser.cpp
Normal file
337
src/Nazara/Graphics/Loaders/OBJ/MTLParser.cpp
Normal file
@@ -0,0 +1,337 @@
|
||||
// Copyright (C) 2013 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/Loaders/OBJ/MTLParser.hpp>
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <Nazara/Core/Log.hpp>
|
||||
#include <Nazara/Utility/Config.hpp>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
NzMTLParser::NzMTLParser(NzInputStream& stream) :
|
||||
m_stream(stream),
|
||||
m_streamFlags(stream.GetStreamOptions())
|
||||
{
|
||||
if ((m_streamFlags & nzStreamOption_Text) == 0)
|
||||
m_stream.SetStreamOptions(m_streamFlags | nzStreamOption_Text);
|
||||
}
|
||||
|
||||
NzMTLParser::~NzMTLParser()
|
||||
{
|
||||
if ((m_streamFlags & nzStreamOption_Text) == 0)
|
||||
m_stream.SetStreamOptions(m_streamFlags);
|
||||
}
|
||||
|
||||
const NzMTLParser::Material* NzMTLParser::GetMaterial(const NzString& materialName) const
|
||||
{
|
||||
auto it = m_materials.find(materialName);
|
||||
if (it != m_materials.end())
|
||||
return &it->second;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool NzMTLParser::Parse()
|
||||
{
|
||||
m_keepLastLine = false;
|
||||
m_lineCount = 0;
|
||||
m_materials.clear();
|
||||
|
||||
Material* currentMaterial = nullptr;
|
||||
|
||||
while (Advance(false))
|
||||
{
|
||||
NzString keyword = m_currentLine.GetWord(0).ToLower();
|
||||
if (keyword == "ka")
|
||||
{
|
||||
float r, g, b;
|
||||
if (std::sscanf(&m_currentLine[3], "%f %f %f", &r, &g, &b) == 3)
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->ambient = NzColor(r*255.f, g*255.f, b*255.f);
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "kd")
|
||||
{
|
||||
float r, g, b;
|
||||
if (std::sscanf(&m_currentLine[3], "%f %f %f", &r, &g, &b) == 3)
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->diffuse = NzColor(r*255.f, g*255.f, b*255.f);
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "ks")
|
||||
{
|
||||
float r, g, b;
|
||||
if (std::sscanf(&m_currentLine[3], "%f %f %f", &r, &g, &b) == 3)
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->specular = NzColor(r*255.f, g*255.f, b*255.f);
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "ni")
|
||||
{
|
||||
float density;
|
||||
if (std::sscanf(&m_currentLine[3], "%f", &density) == 1)
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->refractionIndex = density;
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "ns")
|
||||
{
|
||||
float coef;
|
||||
if (std::sscanf(&m_currentLine[3], "%f", &coef) == 1)
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->shininess = coef;
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == 'd' || keyword == "tr")
|
||||
{
|
||||
float alpha;
|
||||
if (std::sscanf(&m_currentLine[(keyword[0] == 'd') ? 2 : 3], "%f", &alpha) == 1)
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->alpha = alpha;
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "illum")
|
||||
{
|
||||
unsigned int model;
|
||||
if (std::sscanf(&m_currentLine[6], "%u", &model) == 1)
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->illumModel = model;
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "map_ka")
|
||||
{
|
||||
NzString map = m_currentLine.Substr(m_currentLine.GetWordPosition(1));
|
||||
if (!map.IsEmpty())
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->ambientMap = map;
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "map_kd")
|
||||
{
|
||||
NzString map = m_currentLine.Substr(m_currentLine.GetWordPosition(1));
|
||||
if (!map.IsEmpty())
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->diffuseMap = map;
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "map_ks")
|
||||
{
|
||||
NzString map = m_currentLine.Substr(m_currentLine.GetWordPosition(1));
|
||||
if (!map.IsEmpty())
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->specularMap = map;
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "map_bump" || keyword == "bump")
|
||||
{
|
||||
NzString map = m_currentLine.Substr(m_currentLine.GetWordPosition(1));
|
||||
if (!map.IsEmpty())
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->bumpMap = map;
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "map_d")
|
||||
{
|
||||
NzString map = m_currentLine.Substr(m_currentLine.GetWordPosition(1));
|
||||
if (!map.IsEmpty())
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->alphaMap = map;
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "map_decal" || keyword == "decal")
|
||||
{
|
||||
NzString map = m_currentLine.Substr(m_currentLine.GetWordPosition(1));
|
||||
if (!map.IsEmpty())
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->decalMap = map;
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "map_disp" || keyword == "disp")
|
||||
{
|
||||
NzString map = m_currentLine.Substr(m_currentLine.GetWordPosition(1));
|
||||
if (!map.IsEmpty())
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->displacementMap = map;
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "map_refl" || keyword == "refl")
|
||||
{
|
||||
NzString map = m_currentLine.Substr(m_currentLine.GetWordPosition(1));
|
||||
if (!map.IsEmpty())
|
||||
{
|
||||
if (!currentMaterial)
|
||||
currentMaterial = &m_materials["default"];
|
||||
|
||||
currentMaterial->reflectionMap = map;
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (keyword == "newmtl")
|
||||
{
|
||||
NzString materialName = m_currentLine.Substr(m_currentLine.GetWordPosition(1));
|
||||
if (!materialName.IsEmpty())
|
||||
currentMaterial = &m_materials[materialName];
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NzMTLParser::Advance(bool required)
|
||||
{
|
||||
if (!m_keepLastLine)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (m_stream.EndOfStream())
|
||||
{
|
||||
if (required)
|
||||
Error("Incomplete MTL file");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
m_lineCount++;
|
||||
|
||||
m_currentLine = m_stream.ReadLine();
|
||||
m_currentLine = m_currentLine.SubstrTo("#"); // On ignore les commentaires
|
||||
m_currentLine.Simplify(); // Pour un traitement plus simple
|
||||
}
|
||||
while (m_currentLine.IsEmpty());
|
||||
}
|
||||
else
|
||||
m_keepLastLine = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzMTLParser::Error(const NzString& message)
|
||||
{
|
||||
NazaraError(message + " at line #" + NzString::Number(m_lineCount));
|
||||
}
|
||||
|
||||
void NzMTLParser::Warning(const NzString& message)
|
||||
{
|
||||
NazaraWarning(message + " at line #" + NzString::Number(m_lineCount));
|
||||
}
|
||||
|
||||
void NzMTLParser::UnrecognizedLine(bool error)
|
||||
{
|
||||
NzString message = "Unrecognized \"" + m_currentLine + '"';
|
||||
|
||||
if (error)
|
||||
Error(message);
|
||||
else
|
||||
Warning(message);
|
||||
}
|
||||
60
src/Nazara/Graphics/Loaders/OBJ/MTLParser.hpp
Normal file
60
src/Nazara/Graphics/Loaders/OBJ/MTLParser.hpp
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright (C) 2013 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef NAZARA_LOADERS_OBJ_MTLPARSER_HPP
|
||||
#define NAZARA_LOADERS_OBJ_MTLPARSER_HPP
|
||||
|
||||
#include <Nazara/Prerequesites.hpp>
|
||||
#include <Nazara/Core/Color.hpp>
|
||||
#include <Nazara/Core/InputStream.hpp>
|
||||
#include <Nazara/Core/String.hpp>
|
||||
#include <map>
|
||||
|
||||
class NzMTLParser
|
||||
{
|
||||
public:
|
||||
struct Material
|
||||
{
|
||||
NzColor ambient = NzColor::White;
|
||||
NzColor diffuse = NzColor::White;
|
||||
NzColor specular = NzColor::White;
|
||||
NzString alphaMap;
|
||||
NzString ambientMap;
|
||||
NzString bumpMap;
|
||||
NzString decalMap;
|
||||
NzString diffuseMap;
|
||||
NzString displacementMap;
|
||||
NzString reflectionMap;
|
||||
NzString shininessMap;
|
||||
NzString specularMap;
|
||||
float alpha = 1.f;
|
||||
float refractionIndex = 1.f;
|
||||
float shininess = 1.f;
|
||||
unsigned int illumModel = 0;
|
||||
};
|
||||
|
||||
NzMTLParser(NzInputStream& stream$);
|
||||
~NzMTLParser();
|
||||
|
||||
const Material* GetMaterial(const NzString& materialName) const;
|
||||
|
||||
bool Parse();
|
||||
|
||||
private:
|
||||
bool Advance(bool required = true);
|
||||
void Error(const NzString& message);
|
||||
void Warning(const NzString& message);
|
||||
void UnrecognizedLine(bool error = false);
|
||||
|
||||
std::map<NzString, Material> m_materials;
|
||||
NzInputStream& m_stream;
|
||||
NzString m_currentLine;
|
||||
bool m_keepLastLine;
|
||||
unsigned int m_lineCount;
|
||||
unsigned int m_streamFlags;
|
||||
};
|
||||
|
||||
#endif // NAZARA_LOADERS_OBJ_MTLPARSER_HPP
|
||||
437
src/Nazara/Graphics/Loaders/OBJ/OBJParser.cpp
Normal file
437
src/Nazara/Graphics/Loaders/OBJ/OBJParser.cpp
Normal file
@@ -0,0 +1,437 @@
|
||||
// Copyright (C) 2013 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/Loaders/OBJ/OBJParser.hpp>
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <Nazara/Core/Log.hpp>
|
||||
#include <Nazara/Utility/Config.hpp>
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
NzOBJParser::NzOBJParser(NzInputStream& stream) :
|
||||
m_stream(stream),
|
||||
m_streamFlags(stream.GetStreamOptions())
|
||||
{
|
||||
if ((m_streamFlags & nzStreamOption_Text) == 0)
|
||||
m_stream.SetStreamOptions(m_streamFlags | nzStreamOption_Text);
|
||||
}
|
||||
|
||||
NzOBJParser::~NzOBJParser()
|
||||
{
|
||||
if ((m_streamFlags & nzStreamOption_Text) == 0)
|
||||
m_stream.SetStreamOptions(m_streamFlags);
|
||||
}
|
||||
|
||||
const NzString* NzOBJParser::GetMaterials() const
|
||||
{
|
||||
return &m_materials[0];
|
||||
}
|
||||
|
||||
unsigned int NzOBJParser::GetMaterialCount() const
|
||||
{
|
||||
return m_materials.size();
|
||||
}
|
||||
|
||||
const NzOBJParser::Mesh* NzOBJParser::GetMeshes() const
|
||||
{
|
||||
return &m_meshes[0];
|
||||
}
|
||||
|
||||
unsigned int NzOBJParser::GetMeshCount() const
|
||||
{
|
||||
return m_meshes.size();
|
||||
}
|
||||
|
||||
const NzString& NzOBJParser::GetMtlLib() const
|
||||
{
|
||||
return m_mtlLib;
|
||||
}
|
||||
|
||||
const NzVector3f* NzOBJParser::GetNormals() const
|
||||
{
|
||||
return &m_normals[0];
|
||||
}
|
||||
|
||||
unsigned int NzOBJParser::GetNormalCount() const
|
||||
{
|
||||
return m_normals.size();
|
||||
}
|
||||
|
||||
const NzVector4f* NzOBJParser::GetPositions() const
|
||||
{
|
||||
return &m_positions[0];
|
||||
}
|
||||
|
||||
unsigned int NzOBJParser::GetPositionCount() const
|
||||
{
|
||||
return m_positions.size();
|
||||
}
|
||||
|
||||
const NzVector3f* NzOBJParser::GetTexCoords() const
|
||||
{
|
||||
return &m_texCoords[0];
|
||||
}
|
||||
|
||||
unsigned int NzOBJParser::GetTexCoordCount() const
|
||||
{
|
||||
return m_texCoords.size();
|
||||
}
|
||||
|
||||
bool NzOBJParser::Parse()
|
||||
{
|
||||
NzString matName, meshName;
|
||||
matName = meshName = "default";
|
||||
m_keepLastLine = false;
|
||||
m_lineCount = 0;
|
||||
m_meshes.clear();
|
||||
m_mtlLib.Clear();
|
||||
|
||||
m_normals.clear();
|
||||
m_positions.clear();
|
||||
m_texCoords.clear();
|
||||
|
||||
// Beaucoup de meshs font plus de 100 sommets, on prépare le terrain
|
||||
m_normals.reserve(100);
|
||||
m_positions.reserve(100);
|
||||
m_texCoords.reserve(100);
|
||||
|
||||
std::map<NzString, std::map<NzString, std::vector<Face>>> meshes;
|
||||
|
||||
std::vector<Face>* currentMesh = &meshes[meshName][matName];
|
||||
|
||||
while (Advance(false))
|
||||
{
|
||||
switch (std::tolower(m_currentLine[0]))
|
||||
{
|
||||
case 'f': // Une face
|
||||
{
|
||||
if (m_currentLine.GetSize() < 7) // Le minimum syndical pour définir une face de trois sommets (f 1 2 3)
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
unsigned int vertexCount = m_currentLine.Count(' ');
|
||||
if (vertexCount < 3)
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
Face face;
|
||||
face.vertices.resize(vertexCount);
|
||||
|
||||
bool error = false;
|
||||
unsigned int pos = 2;
|
||||
for (unsigned int i = 0; i < vertexCount; ++i)
|
||||
{
|
||||
int offset;
|
||||
int& n = face.vertices[i].normal;
|
||||
int& p = face.vertices[i].position;
|
||||
int& t = face.vertices[i].texCoord;
|
||||
|
||||
if (std::sscanf(&m_currentLine[pos], "%d/%d/%d%n", &p, &t, &n, &offset) != 3)
|
||||
{
|
||||
if (std::sscanf(&m_currentLine[pos], "%d//%d%n", &p, &n, &offset) != 2)
|
||||
{
|
||||
if (std::sscanf(&m_currentLine[pos], "%d/%d%n", &p, &t, &offset) != 2)
|
||||
{
|
||||
if (std::sscanf(&m_currentLine[pos], "%d%n", &p, &offset) != 1)
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
n = 0;
|
||||
t = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
n = 0;
|
||||
}
|
||||
else
|
||||
t = 0;
|
||||
}
|
||||
|
||||
if (p < 0)
|
||||
{
|
||||
p += m_positions.size();
|
||||
if (p < 0)
|
||||
{
|
||||
Error("Vertex index out of range (" + NzString::Number(p) + " < 0");
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
p--;
|
||||
|
||||
if (n < 0)
|
||||
{
|
||||
n += m_normals.size();
|
||||
if (n < 0)
|
||||
{
|
||||
Error("Vertex index out of range (" + NzString::Number(n) + " < 0");
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
n--;
|
||||
|
||||
if (t < 0)
|
||||
{
|
||||
t += m_texCoords.size();
|
||||
if (t < 0)
|
||||
{
|
||||
Error("Vertex index out of range (" + NzString::Number(t) + " < 0");
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
t--;
|
||||
|
||||
if (static_cast<unsigned int>(p) >= m_positions.size())
|
||||
{
|
||||
Error("Vertex index out of range (" + NzString::Number(p) + " >= " + NzString::Number(m_positions.size()) + ')');
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
else if (n >= 0 && static_cast<unsigned int>(n) >= m_normals.size())
|
||||
{
|
||||
Error("Normal index out of range (" + NzString::Number(n) + " >= " + NzString::Number(m_normals.size()) + ')');
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
else if (t >= 0 && static_cast<unsigned int>(t) >= m_texCoords.size())
|
||||
{
|
||||
Error("TexCoord index out of range (" + NzString::Number(t) + " >= " + NzString::Number(m_texCoords.size()) + ')');
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
|
||||
pos += offset;
|
||||
}
|
||||
|
||||
if (!error)
|
||||
currentMesh->push_back(std::move(face));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'm':
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
if (m_currentLine.GetWord(0).ToLower() != "mtllib")
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
|
||||
m_mtlLib = m_currentLine.Substr(m_currentLine.GetWordPosition(1));
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
case 'o':
|
||||
{
|
||||
if (m_currentLine[1] != ' ')
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
NzString objectName = m_currentLine.Substr(m_currentLine.GetWordPosition(1));
|
||||
if (objectName.IsEmpty())
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
meshName = objectName;
|
||||
currentMesh = &meshes[meshName][matName];
|
||||
break;
|
||||
}
|
||||
|
||||
case 's':
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
if (m_currentLine[1] == ' ')
|
||||
{
|
||||
NzString param = m_currentLine.Substr(2);
|
||||
if (param != "all" && param != "on" && param != "off" && !param.IsNumber())
|
||||
UnrecognizedLine();
|
||||
}
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
if (m_currentLine.GetWord(0) != "usemtl")
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
|
||||
matName = m_currentLine.Substr(m_currentLine.GetWordPosition(1));
|
||||
if (matName.IsEmpty())
|
||||
{
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
currentMesh = &meshes[meshName][matName];
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
{
|
||||
NzString word = m_currentLine.GetWord(0).ToLower();
|
||||
if (word == 'v')
|
||||
{
|
||||
NzVector4f vertex(NzVector3f::Zero(), 1.f);
|
||||
unsigned int paramCount = std::sscanf(&m_currentLine[2], "%f %f %f %f", &vertex.x, &vertex.y, &vertex.z, &vertex.w);
|
||||
if (paramCount >= 3)
|
||||
m_positions.push_back(vertex);
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (word == "vn")
|
||||
{
|
||||
NzVector3f normal(NzVector3f::Zero());
|
||||
unsigned int paramCount = std::sscanf(&m_currentLine[3], "%f %f %f", &normal.x, &normal.y, &normal.z);
|
||||
if (paramCount == 3)
|
||||
m_normals.push_back(normal);
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
else if (word == "vt")
|
||||
{
|
||||
NzVector3f uvw(NzVector3f::Zero());
|
||||
unsigned int paramCount = std::sscanf(&m_currentLine[3], "%f %f %f", &uvw.x, &uvw.y, &uvw.z);
|
||||
if (paramCount >= 2)
|
||||
m_texCoords.push_back(uvw);
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
}
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
else
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||
UnrecognizedLine();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::map<NzString, unsigned int> materials;
|
||||
unsigned int matCount = 0;
|
||||
|
||||
for (auto meshIt : meshes)
|
||||
{
|
||||
for (auto matIt : meshIt.second)
|
||||
{
|
||||
if (!matIt.second.empty())
|
||||
{
|
||||
Mesh mesh;
|
||||
mesh.faces = std::move(matIt.second);
|
||||
mesh.name = meshIt.first;
|
||||
|
||||
auto it = materials.find(matIt.first);
|
||||
if (it == materials.end())
|
||||
{
|
||||
mesh.material = matCount;
|
||||
materials[matIt.first] = matCount++;
|
||||
}
|
||||
else
|
||||
mesh.material = it->second;
|
||||
|
||||
m_meshes.push_back(std::move(mesh));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_meshes.empty())
|
||||
{
|
||||
NazaraError("No meshes");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_materials.resize(matCount);
|
||||
for (auto it : materials)
|
||||
m_materials[it.second] = it.first;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NzOBJParser::Advance(bool required)
|
||||
{
|
||||
if (!m_keepLastLine)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (m_stream.EndOfStream())
|
||||
{
|
||||
if (required)
|
||||
Error("Incomplete OBJ file");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
m_lineCount++;
|
||||
|
||||
m_currentLine = m_stream.ReadLine();
|
||||
m_currentLine = m_currentLine.SubstrTo("#"); // On ignore les commentaires
|
||||
m_currentLine.Simplify(); // Pour un traitement plus simple
|
||||
}
|
||||
while (m_currentLine.IsEmpty());
|
||||
}
|
||||
else
|
||||
m_keepLastLine = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzOBJParser::Error(const NzString& message)
|
||||
{
|
||||
NazaraError(message + " at line #" + NzString::Number(m_lineCount));
|
||||
}
|
||||
|
||||
void NzOBJParser::Warning(const NzString& message)
|
||||
{
|
||||
NazaraWarning(message + " at line #" + NzString::Number(m_lineCount));
|
||||
}
|
||||
|
||||
void NzOBJParser::UnrecognizedLine(bool error)
|
||||
{
|
||||
NzString message = "Unrecognized \"" + m_currentLine + '"';
|
||||
|
||||
if (error)
|
||||
Error(message);
|
||||
else
|
||||
Warning(message);
|
||||
}
|
||||
75
src/Nazara/Graphics/Loaders/OBJ/OBJParser.hpp
Normal file
75
src/Nazara/Graphics/Loaders/OBJ/OBJParser.hpp
Normal file
@@ -0,0 +1,75 @@
|
||||
// Copyright (C) 2013 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef NAZARA_LOADERS_OBJ_OBJPARSER_HPP
|
||||
#define NAZARA_LOADERS_OBJ_OBJPARSER_HPP
|
||||
|
||||
#include <Nazara/Prerequesites.hpp>
|
||||
#include <Nazara/Core/InputStream.hpp>
|
||||
#include <Nazara/Core/String.hpp>
|
||||
#include <Nazara/Math/Vector3.hpp>
|
||||
#include <Nazara/Math/Vector4.hpp>
|
||||
#include <vector>
|
||||
|
||||
class NzOBJParser
|
||||
{
|
||||
public:
|
||||
struct FaceVertex
|
||||
{
|
||||
int normal;
|
||||
int position;
|
||||
int texCoord;
|
||||
};
|
||||
|
||||
struct Face
|
||||
{
|
||||
std::vector<FaceVertex> vertices;
|
||||
};
|
||||
|
||||
struct Mesh
|
||||
{
|
||||
std::vector<Face> faces;
|
||||
NzString name;
|
||||
unsigned int material;
|
||||
};
|
||||
|
||||
NzOBJParser(NzInputStream& stream$);
|
||||
~NzOBJParser();
|
||||
|
||||
const NzString* GetMaterials() const;
|
||||
unsigned int GetMaterialCount() const;
|
||||
const Mesh* GetMeshes() const;
|
||||
unsigned int GetMeshCount() const;
|
||||
const NzString& GetMtlLib() const;
|
||||
const NzVector3f* GetNormals() const;
|
||||
unsigned int GetNormalCount() const;
|
||||
const NzVector4f* GetPositions() const;
|
||||
unsigned int GetPositionCount() const;
|
||||
const NzVector3f* GetTexCoords() const;
|
||||
unsigned int GetTexCoordCount() const;
|
||||
|
||||
bool Parse();
|
||||
|
||||
private:
|
||||
bool Advance(bool required = true);
|
||||
void Error(const NzString& message);
|
||||
void Warning(const NzString& message);
|
||||
void UnrecognizedLine(bool error = false);
|
||||
|
||||
std::vector<Mesh> m_meshes;
|
||||
std::vector<NzString> m_materials;
|
||||
std::vector<NzVector3f> m_normals;
|
||||
std::vector<NzVector4f> m_positions;
|
||||
std::vector<NzVector3f> m_texCoords;
|
||||
NzInputStream& m_stream;
|
||||
NzString m_currentLine;
|
||||
NzString m_mtlLib;
|
||||
bool m_keepLastLine;
|
||||
unsigned int m_lineCount;
|
||||
unsigned int m_streamFlags;
|
||||
};
|
||||
|
||||
#endif // NAZARA_LOADERS_OBJ_OBJPARSER_HPP
|
||||
607
src/Nazara/Graphics/Model.cpp
Normal file
607
src/Nazara/Graphics/Model.cpp
Normal file
@@ -0,0 +1,607 @@
|
||||
// Copyright (C) 2013 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/Model.hpp>
|
||||
#include <Nazara/Graphics/Config.hpp>
|
||||
#include <Nazara/Utility/SkeletalMesh.hpp>
|
||||
#include <Nazara/Utility/StaticMesh.hpp>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
bool NzModelParameters::IsValid() const
|
||||
{
|
||||
if (loadAnimation && !animation.IsValid())
|
||||
return false;
|
||||
|
||||
if (loadMaterials && !material.IsValid())
|
||||
return false;
|
||||
|
||||
return mesh.IsValid();
|
||||
}
|
||||
|
||||
NzModel::NzModel() :
|
||||
m_currentSequence(nullptr),
|
||||
m_animationEnabled(true),
|
||||
m_boundingBoxUpdated(true),
|
||||
m_drawEnabled(true),
|
||||
m_matCount(0),
|
||||
m_skin(0),
|
||||
m_skinCount(1)
|
||||
{
|
||||
}
|
||||
|
||||
NzModel::NzModel(const NzModel& model) :
|
||||
NzSceneNode(model),
|
||||
m_materials(model.m_materials),
|
||||
m_boundingBox(model.m_boundingBox),
|
||||
m_currentSequence(model.m_currentSequence),
|
||||
m_animationEnabled(model.m_animationEnabled),
|
||||
m_boundingBoxUpdated(model.m_boundingBoxUpdated),
|
||||
m_drawEnabled(model.m_drawEnabled),
|
||||
m_interpolation(model.m_interpolation),
|
||||
m_currentFrame(model.m_currentFrame),
|
||||
m_matCount(model.m_matCount),
|
||||
m_nextFrame(model.m_nextFrame),
|
||||
m_skin(model.m_skin),
|
||||
m_skinCount(model.m_skinCount)
|
||||
{
|
||||
if (model.m_mesh)
|
||||
{
|
||||
// Nous n'avons une animation et des matériaux que si nous avons un mesh
|
||||
m_animation = model.m_animation;
|
||||
m_mesh = model.m_mesh;
|
||||
m_materials = model.m_materials;
|
||||
|
||||
if (m_mesh->GetAnimationType() == nzAnimationType_Skeletal)
|
||||
m_skeleton = model.m_skeleton;
|
||||
}
|
||||
}
|
||||
|
||||
NzModel::~NzModel()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
void NzModel::AddToRenderQueue(NzRenderQueue& renderQueue) const
|
||||
{
|
||||
if (!m_transformMatrixUpdated)
|
||||
UpdateTransformMatrix();
|
||||
|
||||
unsigned int subMeshCount = m_mesh->GetSubMeshCount();
|
||||
for (unsigned int i = 0; i < subMeshCount; ++i)
|
||||
{
|
||||
NzSubMesh* subMesh = m_mesh->GetSubMesh(i);
|
||||
NzMaterial* material = m_materials[m_skin*m_matCount + subMesh->GetMaterialIndex()];
|
||||
|
||||
switch (subMesh->GetAnimationType())
|
||||
{
|
||||
case nzAnimationType_Skeletal:
|
||||
{
|
||||
NzSkeletalMesh* skeletalMesh = static_cast<NzSkeletalMesh*>(subMesh);
|
||||
std::vector<NzRenderQueue::SkeletalData>& data = renderQueue.visibleSkeletalModels[material][skeletalMesh];
|
||||
|
||||
///TODO: Corriger cette abomination
|
||||
data.resize(data.size()+1);
|
||||
NzRenderQueue::SkeletalData& skeletalData = data.back();
|
||||
|
||||
skeletalData.skinnedVertices.resize(skeletalMesh->GetVertexCount());
|
||||
skeletalData.transformMatrix = m_transformMatrix;
|
||||
|
||||
skeletalMesh->Skin(&skeletalData.skinnedVertices[0], &m_skeleton);
|
||||
break;
|
||||
}
|
||||
|
||||
case nzAnimationType_Static:
|
||||
{
|
||||
NzStaticMesh* staticMesh = static_cast<NzStaticMesh*>(subMesh);
|
||||
std::vector<NzMatrix4f>& matrices = renderQueue.visibleStaticModels[material][staticMesh];
|
||||
|
||||
matrices.push_back(m_transformMatrix);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NzModel::AdvanceAnimation(float elapsedTime)
|
||||
{
|
||||
#if NAZARA_3D_SAFE
|
||||
if (!m_animation)
|
||||
{
|
||||
NazaraError("Model has no animation");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_interpolation += m_currentSequence->frameRate * elapsedTime;
|
||||
while (m_interpolation > 1.f)
|
||||
{
|
||||
m_interpolation -= 1.f;
|
||||
|
||||
unsigned lastFrame = m_currentSequence->firstFrame + m_currentSequence->frameCount - 1;
|
||||
if (m_nextFrame+1 > lastFrame)
|
||||
{
|
||||
if (m_animation->IsLoopPointInterpolationEnabled())
|
||||
{
|
||||
m_currentFrame = m_nextFrame;
|
||||
m_nextFrame = m_currentSequence->firstFrame;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_currentFrame = m_currentSequence->firstFrame;
|
||||
m_nextFrame = m_currentFrame+1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_currentFrame = m_nextFrame;
|
||||
m_nextFrame++;
|
||||
}
|
||||
}
|
||||
|
||||
m_animation->AnimateSkeleton(&m_skeleton, m_currentFrame, m_nextFrame, m_interpolation);
|
||||
m_boundingBox.MakeNull();
|
||||
m_boundingBoxUpdated = false;
|
||||
}
|
||||
|
||||
void NzModel::EnableAnimation(bool animation)
|
||||
{
|
||||
m_animationEnabled = animation;
|
||||
}
|
||||
|
||||
void NzModel::EnableDraw(bool draw)
|
||||
{
|
||||
m_drawEnabled = draw;
|
||||
}
|
||||
|
||||
NzAnimation* NzModel::GetAnimation() const
|
||||
{
|
||||
return m_animation;
|
||||
}
|
||||
|
||||
const NzBoundingBoxf& NzModel::GetBoundingBox() const
|
||||
{
|
||||
#if NAZARA_3D_SAFE
|
||||
if (!m_mesh)
|
||||
{
|
||||
NazaraError("Model has no mesh");
|
||||
|
||||
static NzBoundingBoxf dummy(nzExtend_Null);
|
||||
return dummy;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!m_boundingBoxUpdated)
|
||||
UpdateBoundingBox();
|
||||
|
||||
return m_boundingBox;
|
||||
}
|
||||
|
||||
NzMaterial* NzModel::GetMaterial(unsigned int matIndex) const
|
||||
{
|
||||
#if NAZARA_3D_SAFE
|
||||
if (matIndex >= m_matCount)
|
||||
{
|
||||
NazaraError("Material index out of range (" + NzString::Number(matIndex) + " >= " + NzString::Number(m_matCount));
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_materials[m_skin*m_matCount + matIndex];
|
||||
}
|
||||
|
||||
NzMaterial* NzModel::GetMaterial(unsigned int skinIndex, unsigned int matIndex) const
|
||||
{
|
||||
#if NAZARA_3D_SAFE
|
||||
if (skinIndex >= m_skinCount)
|
||||
{
|
||||
NazaraError("Skin index out of range (" + NzString::Number(skinIndex) + " >= " + NzString::Number(m_skinCount));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (matIndex >= m_matCount)
|
||||
{
|
||||
NazaraError("Material index out of range (" + NzString::Number(matIndex) + " >= " + NzString::Number(m_matCount));
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_materials[skinIndex*m_matCount + matIndex];
|
||||
}
|
||||
|
||||
unsigned int NzModel::GetMaterialCount() const
|
||||
{
|
||||
return m_matCount;
|
||||
}
|
||||
|
||||
NzMesh* NzModel::GetMesh() const
|
||||
{
|
||||
return m_mesh;
|
||||
}
|
||||
|
||||
nzSceneNodeType NzModel::GetSceneNodeType() const
|
||||
{
|
||||
return nzSceneNodeType_Model;
|
||||
}
|
||||
|
||||
NzSkeleton* NzModel::GetSkeleton()
|
||||
{
|
||||
return &m_skeleton;
|
||||
}
|
||||
|
||||
const NzSkeleton* NzModel::GetSkeleton() const
|
||||
{
|
||||
return &m_skeleton;
|
||||
}
|
||||
|
||||
unsigned int NzModel::GetSkin() const
|
||||
{
|
||||
return m_skin;
|
||||
}
|
||||
|
||||
unsigned int NzModel::GetSkinCount() const
|
||||
{
|
||||
return m_skinCount;
|
||||
}
|
||||
|
||||
bool NzModel::HasAnimation() const
|
||||
{
|
||||
return m_animation != nullptr;
|
||||
}
|
||||
|
||||
bool NzModel::IsAnimationEnabled() const
|
||||
{
|
||||
return m_animationEnabled;
|
||||
}
|
||||
|
||||
bool NzModel::IsDrawEnabled() const
|
||||
{
|
||||
return m_drawEnabled;
|
||||
}
|
||||
|
||||
bool NzModel::LoadFromFile(const NzString& filePath, const NzModelParameters& params)
|
||||
{
|
||||
return NzModelLoader::LoadFromFile(this, filePath, params);
|
||||
}
|
||||
|
||||
bool NzModel::LoadFromMemory(const void* data, std::size_t size, const NzModelParameters& params)
|
||||
{
|
||||
return NzModelLoader::LoadFromMemory(this, data, size, params);
|
||||
}
|
||||
|
||||
bool NzModel::LoadFromStream(NzInputStream& stream, const NzModelParameters& params)
|
||||
{
|
||||
return NzModelLoader::LoadFromStream(this, stream, params);
|
||||
}
|
||||
|
||||
void NzModel::Reset()
|
||||
{
|
||||
if (m_scene)
|
||||
m_scene->UnregisterForUpdate(this);
|
||||
|
||||
m_matCount = 0;
|
||||
m_skinCount = 0;
|
||||
|
||||
if (m_mesh)
|
||||
{
|
||||
m_animation.Reset();
|
||||
m_mesh.Reset();
|
||||
m_materials.clear();
|
||||
|
||||
m_skeleton.Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
bool NzModel::SetAnimation(NzAnimation* animation)
|
||||
{
|
||||
#if NAZARA_3D_SAFE
|
||||
if (!m_mesh)
|
||||
{
|
||||
NazaraError("Model has no animation");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (animation)
|
||||
{
|
||||
if (!animation->IsValid())
|
||||
{
|
||||
NazaraError("Invalid animation");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (animation->GetType() != m_mesh->GetAnimationType())
|
||||
{
|
||||
NazaraError("Animation type must match mesh animation type");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (animation->GetType() == nzAnimationType_Skeletal && animation->GetJointCount() != m_mesh->GetJointCount())
|
||||
{
|
||||
NazaraError("Animation joint count must match mesh joint count");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
m_animation = animation;
|
||||
if (m_animation)
|
||||
{
|
||||
m_currentFrame = 0;
|
||||
m_interpolation = 0.f;
|
||||
|
||||
SetSequence(0);
|
||||
|
||||
if (m_scene)
|
||||
m_scene->RegisterForUpdate(this);
|
||||
}
|
||||
else if (m_scene)
|
||||
m_scene->UnregisterForUpdate(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzModel::SetMaterial(unsigned int matIndex, NzMaterial* material)
|
||||
{
|
||||
#if NAZARA_3D_SAFE
|
||||
if (matIndex >= m_matCount)
|
||||
{
|
||||
NazaraError("Material index out of range (" + NzString::Number(matIndex) + " >= " + NzString::Number(m_matCount));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
unsigned int index = m_skin*m_matCount + matIndex;
|
||||
|
||||
if (material)
|
||||
m_materials[index] = material;
|
||||
else
|
||||
m_materials[index] = NzMaterial::GetDefault();
|
||||
}
|
||||
|
||||
void NzModel::SetMaterial(unsigned int skinIndex, unsigned int matIndex, NzMaterial* material)
|
||||
{
|
||||
#if NAZARA_3D_SAFE
|
||||
if (skinIndex >= m_skinCount)
|
||||
{
|
||||
NazaraError("Skin index out of range (" + NzString::Number(skinIndex) + " >= " + NzString::Number(m_skinCount));
|
||||
return;
|
||||
}
|
||||
|
||||
if (matIndex >= m_matCount)
|
||||
{
|
||||
NazaraError("Material index out of range (" + NzString::Number(matIndex) + " >= " + NzString::Number(m_matCount));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
unsigned int index = skinIndex*m_matCount + matIndex;
|
||||
|
||||
if (material)
|
||||
m_materials[index] = material;
|
||||
else
|
||||
m_materials[index] = NzMaterial::GetDefault();
|
||||
}
|
||||
|
||||
void NzModel::SetMesh(NzMesh* mesh)
|
||||
{
|
||||
m_mesh = mesh;
|
||||
|
||||
if (m_mesh)
|
||||
{
|
||||
m_boundingBoxUpdated = false;
|
||||
|
||||
if (m_mesh->GetAnimationType() == nzAnimationType_Skeletal)
|
||||
m_skeleton = *mesh->GetSkeleton(); // Copie du squelette template
|
||||
|
||||
if (m_animation)
|
||||
{
|
||||
if (m_animation->GetJointCount() != m_mesh->GetJointCount())
|
||||
{
|
||||
NazaraWarning("Animation joint count is not matching new mesh joint count");
|
||||
SetAnimation(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
m_matCount = mesh->GetMaterialCount();
|
||||
m_materials.resize(m_matCount, NzMaterial::GetDefault());
|
||||
m_skinCount = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_boundingBox.MakeNull();
|
||||
m_boundingBoxUpdated = true;
|
||||
m_matCount = 0;
|
||||
m_skinCount = 0;
|
||||
m_materials.clear();
|
||||
|
||||
SetAnimation(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
bool NzModel::SetSequence(const NzString& sequenceName)
|
||||
{
|
||||
///TODO: Rendre cette erreur "safe" avec le nouveau système de gestions d'erreur (No-log)
|
||||
#if NAZARA_3D_SAFE
|
||||
if (!m_animation)
|
||||
{
|
||||
NazaraError("Model has no animation");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
const NzSequence* currentSequence = m_animation->GetSequence(sequenceName);
|
||||
if (!currentSequence)
|
||||
{
|
||||
NazaraError("Sequence not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_currentSequence = currentSequence;
|
||||
m_nextFrame = m_currentSequence->firstFrame;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzModel::SetSequence(unsigned int sequenceIndex)
|
||||
{
|
||||
#if NAZARA_3D_SAFE
|
||||
if (!m_animation)
|
||||
{
|
||||
NazaraError("Model has no animation");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
const NzSequence* currentSequence = m_animation->GetSequence(sequenceIndex);
|
||||
#if NAZARA_3D_SAFE
|
||||
if (!currentSequence)
|
||||
{
|
||||
NazaraError("Sequence not found");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_currentSequence = currentSequence;
|
||||
m_nextFrame = m_currentSequence->firstFrame;
|
||||
}
|
||||
|
||||
void NzModel::SetSkin(unsigned int skin)
|
||||
{
|
||||
#if NAZARA_3D_SAFE
|
||||
if (skin >= m_skinCount)
|
||||
{
|
||||
NazaraError("Skin index out of range (" + NzString::Number(skin) + " >= " + NzString::Number(m_skinCount));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_skin = skin;
|
||||
}
|
||||
|
||||
void NzModel::SetSkinCount(unsigned int skinCount)
|
||||
{
|
||||
#if NAZARA_3D_SAFE
|
||||
if (skinCount == 0)
|
||||
{
|
||||
NazaraError("Skin count must be over 0");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_materials.resize(m_matCount*skinCount, NzMaterial::GetDefault());
|
||||
m_skinCount = skinCount;
|
||||
}
|
||||
|
||||
NzModel& NzModel::operator=(const NzModel& node)
|
||||
{
|
||||
NzSceneNode::operator=(node);
|
||||
|
||||
m_animation = node.m_animation;
|
||||
m_animationEnabled = node.m_animationEnabled;
|
||||
m_boundingBox = node.m_boundingBox;
|
||||
m_boundingBoxUpdated = node.m_boundingBoxUpdated;
|
||||
m_currentFrame = node.m_currentFrame;
|
||||
m_currentSequence = node.m_currentSequence;
|
||||
m_drawEnabled = node.m_drawEnabled;
|
||||
m_interpolation = node.m_interpolation;
|
||||
m_matCount = node.m_matCount;
|
||||
m_materials = node.m_materials;
|
||||
m_mesh = node.m_mesh;
|
||||
m_nextFrame = node.m_nextFrame;
|
||||
m_skin = node.m_skin;
|
||||
m_skinCount = node.m_skinCount;
|
||||
|
||||
if (m_mesh->GetAnimationType() == nzAnimationType_Skeletal)
|
||||
m_skeleton = node.m_skeleton;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
NzModel& NzModel::operator=(NzModel&& node)
|
||||
{
|
||||
NzSceneNode::operator=(node);
|
||||
|
||||
m_animation = std::move(node.m_animation);
|
||||
m_animationEnabled = node.m_animationEnabled;
|
||||
m_boundingBox = node.m_boundingBox;
|
||||
m_boundingBoxUpdated = node.m_boundingBoxUpdated;
|
||||
m_currentFrame = node.m_currentFrame;
|
||||
m_currentSequence = node.m_currentSequence;
|
||||
m_drawEnabled = node.m_drawEnabled;
|
||||
m_interpolation = node.m_interpolation;
|
||||
m_matCount = node.m_matCount;
|
||||
m_materials = std::move(node.m_materials);
|
||||
m_mesh = std::move(node.m_mesh);
|
||||
m_nextFrame = node.m_nextFrame;
|
||||
m_skin = node.m_skin;
|
||||
m_skinCount = node.m_skinCount;
|
||||
|
||||
if (m_mesh->GetAnimationType() == nzAnimationType_Skeletal)
|
||||
m_skeleton = std::move(node.m_skeleton);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void NzModel::Invalidate()
|
||||
{
|
||||
NzSceneNode::Invalidate();
|
||||
|
||||
m_boundingBoxUpdated = false;
|
||||
}
|
||||
|
||||
void NzModel::Register()
|
||||
{
|
||||
if (m_animation)
|
||||
m_scene->RegisterForUpdate(this);
|
||||
}
|
||||
|
||||
void NzModel::Unregister()
|
||||
{
|
||||
m_scene->UnregisterForUpdate(this);
|
||||
}
|
||||
|
||||
void NzModel::Update()
|
||||
{
|
||||
if (m_animationEnabled && m_animation)
|
||||
AdvanceAnimation(m_scene->GetUpdateTime());
|
||||
}
|
||||
|
||||
void NzModel::UpdateBoundingBox() const
|
||||
{
|
||||
if (m_boundingBox.IsNull())
|
||||
{
|
||||
if (m_mesh->GetAnimationType() == nzAnimationType_Skeletal)
|
||||
m_boundingBox.Set(m_skeleton.GetAABB());
|
||||
else
|
||||
m_boundingBox.Set(m_mesh->GetAABB());
|
||||
}
|
||||
|
||||
if (!m_transformMatrixUpdated)
|
||||
UpdateTransformMatrix();
|
||||
|
||||
m_boundingBox.Update(m_transformMatrix);
|
||||
m_boundingBoxUpdated = true;
|
||||
}
|
||||
|
||||
bool NzModel::VisibilityTest(const NzFrustumf& frustum)
|
||||
{
|
||||
#if NAZARA_3D_SAFE
|
||||
if (!m_mesh)
|
||||
{
|
||||
NazaraError("Model has no mesh");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!m_drawEnabled)
|
||||
return false;
|
||||
|
||||
if (!m_boundingBoxUpdated)
|
||||
UpdateBoundingBox();
|
||||
|
||||
return frustum.Contains(m_boundingBox);
|
||||
}
|
||||
|
||||
NzModelLoader::LoaderList NzModel::s_loaders;
|
||||
96
src/Nazara/Graphics/RenderQueue.cpp
Normal file
96
src/Nazara/Graphics/RenderQueue.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
// Copyright (C) 2013 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/RenderQueue.hpp>
|
||||
#include <Nazara/Renderer/Material.hpp>
|
||||
#include <Nazara/Utility/SkeletalMesh.hpp>
|
||||
#include <Nazara/Utility/StaticMesh.hpp>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
bool NzRenderQueue::MaterialComparator::operator()(const NzMaterial* mat1, const NzMaterial* mat2)
|
||||
{
|
||||
const NzShader* shader1 = mat1->GetCustomShader();
|
||||
const NzShader* shader2 = mat2->GetCustomShader();
|
||||
|
||||
if (shader1)
|
||||
{
|
||||
if (shader2)
|
||||
return shader1 < shader2;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
else if (shader2)
|
||||
return false;
|
||||
else
|
||||
{
|
||||
nzUInt32 shaderFlags1 = mat1->GetShaderFlags();
|
||||
nzUInt32 shaderFlags2 = mat2->GetShaderFlags();
|
||||
|
||||
if (shaderFlags1 == shaderFlags2)
|
||||
return mat1 < mat2;
|
||||
else
|
||||
return shaderFlags1 < shaderFlags2;
|
||||
}
|
||||
}
|
||||
|
||||
bool NzRenderQueue::SkeletalMeshComparator::operator()(const NzSkeletalMesh* subMesh1, const NzSkeletalMesh* subMesh2)
|
||||
{
|
||||
const NzBuffer* buffer1;
|
||||
const NzIndexBuffer* iBuffer1 = subMesh1->GetIndexBuffer();
|
||||
if (iBuffer1)
|
||||
buffer1 = iBuffer1->GetBuffer();
|
||||
else
|
||||
buffer1 = nullptr;
|
||||
|
||||
const NzBuffer* buffer2;
|
||||
const NzIndexBuffer* iBuffer2 = subMesh1->GetIndexBuffer();
|
||||
if (iBuffer2)
|
||||
buffer2 = iBuffer1->GetBuffer();
|
||||
else
|
||||
buffer2 = nullptr;
|
||||
|
||||
if (buffer1 == buffer2)
|
||||
return subMesh1 < subMesh2;
|
||||
else
|
||||
return buffer2 < buffer2;
|
||||
}
|
||||
|
||||
bool NzRenderQueue::StaticMeshComparator::operator()(const NzStaticMesh* subMesh1, const NzStaticMesh* subMesh2)
|
||||
{
|
||||
const NzBuffer* buffer1;
|
||||
const NzIndexBuffer* iBuffer1 = subMesh1->GetIndexBuffer();
|
||||
if (iBuffer1)
|
||||
buffer1 = iBuffer1->GetBuffer();
|
||||
else
|
||||
buffer1 = nullptr;
|
||||
|
||||
const NzBuffer* buffer2;
|
||||
const NzIndexBuffer* iBuffer2 = subMesh1->GetIndexBuffer();
|
||||
if (iBuffer2)
|
||||
buffer2 = iBuffer1->GetBuffer();
|
||||
else
|
||||
buffer2 = nullptr;
|
||||
|
||||
if (iBuffer1 == iBuffer2)
|
||||
{
|
||||
buffer1 = subMesh1->GetVertexBuffer()->GetBuffer();
|
||||
buffer2 = subMesh2->GetVertexBuffer()->GetBuffer();
|
||||
|
||||
if (buffer1 == buffer2)
|
||||
return subMesh1 < subMesh2;
|
||||
else
|
||||
return buffer1 < buffer2;
|
||||
}
|
||||
else
|
||||
return iBuffer1 < iBuffer2;
|
||||
}
|
||||
|
||||
void NzRenderQueue::Clear()
|
||||
{
|
||||
directionnalLights.clear();
|
||||
otherDrawables.clear();
|
||||
visibleLights.clear();
|
||||
visibleSkeletalModels.clear();
|
||||
visibleStaticModels.clear();
|
||||
}
|
||||
443
src/Nazara/Graphics/Scene.cpp
Normal file
443
src/Nazara/Graphics/Scene.cpp
Normal file
@@ -0,0 +1,443 @@
|
||||
// Copyright (C) 2013 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/Scene.hpp>
|
||||
#include <Nazara/Core/Clock.hpp>
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <Nazara/Graphics/ColorBackground.hpp>
|
||||
#include <Nazara/Graphics/Drawable.hpp>
|
||||
#include <Nazara/Graphics/Camera.hpp>
|
||||
#include <Nazara/Graphics/Light.hpp>
|
||||
#include <Nazara/Graphics/Model.hpp>
|
||||
#include <Nazara/Graphics/RenderQueue.hpp>
|
||||
#include <Nazara/Graphics/SceneRoot.hpp>
|
||||
#include <Nazara/Renderer/Config.hpp>
|
||||
#include <Nazara/Renderer/Renderer.hpp>
|
||||
#include <Nazara/Renderer/Shader.hpp>
|
||||
#include <Nazara/Renderer/ShaderBuilder.hpp>
|
||||
#include <Nazara/Utility/BufferMapper.hpp>
|
||||
#include <Nazara/Utility/SkeletalMesh.hpp>
|
||||
#include <Nazara/Utility/StaticMesh.hpp>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <Nazara/3D/Debug.hpp>
|
||||
|
||||
namespace
|
||||
{
|
||||
const unsigned int maxLights = 8; ///TODO: Config
|
||||
|
||||
struct LightComparator
|
||||
{
|
||||
bool operator()(const NzLight* light1, const NzLight* light2)
|
||||
{
|
||||
return light1->GetPosition().SquaredDistance(pos) < light2->GetPosition().SquaredDistance(pos);
|
||||
}
|
||||
|
||||
NzVector3f pos;
|
||||
};
|
||||
}
|
||||
|
||||
struct NzSceneImpl
|
||||
{
|
||||
NzSceneImpl(NzScene* scene) :
|
||||
root(scene)
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<NzBackground> background;
|
||||
std::vector<NzUpdatable*> updateList;
|
||||
std::vector<NzUpdatable*> visibleUpdateList;
|
||||
std::vector<NzRenderer::InstancingData> instancingData;
|
||||
NzClock updateClock;
|
||||
NzColor ambientColor = NzColor(25,25,25);
|
||||
NzRenderQueue renderQueue;
|
||||
NzSceneRoot root;
|
||||
const NzCamera* activeCamera;
|
||||
NzVertexBuffer* skinningBuffer;
|
||||
bool update;
|
||||
float frameTime;
|
||||
float updateTime;
|
||||
unsigned int updatePerSecond = 60;
|
||||
};
|
||||
|
||||
NzScene::NzScene()
|
||||
{
|
||||
m_impl = new NzSceneImpl(this);
|
||||
m_impl->background.reset(new NzColorBackground);
|
||||
m_impl->skinningBuffer = new NzVertexBuffer(NzMesh::GetDeclaration(), 20000, nzBufferStorage_Hardware, nzBufferUsage_Dynamic);
|
||||
|
||||
if (NzRenderer::HasCapability(nzRendererCap_Instancing))
|
||||
m_impl->instancingData.resize(NAZARA_RENDERER_INSTANCING_MAX);
|
||||
}
|
||||
|
||||
NzScene::~NzScene()
|
||||
{
|
||||
for (NzNode* child : m_impl->root.GetChilds())
|
||||
{
|
||||
if (child->GetNodeType() == nzNodeType_Scene)
|
||||
static_cast<NzSceneNode*>(child)->SetScene(nullptr);
|
||||
}
|
||||
|
||||
delete m_impl->skinningBuffer;
|
||||
delete m_impl;
|
||||
}
|
||||
|
||||
void NzScene::AddToVisibilityList(NzUpdatable* object)
|
||||
{
|
||||
m_impl->visibleUpdateList.push_back(object);
|
||||
}
|
||||
|
||||
void NzScene::Cull()
|
||||
{
|
||||
m_impl->renderQueue.Clear();
|
||||
m_impl->visibleUpdateList.clear();
|
||||
|
||||
// Frustum culling
|
||||
RecursiveFrustumCull(m_impl->renderQueue, m_impl->activeCamera->GetFrustum(), &m_impl->root);
|
||||
|
||||
///TODO: Occlusion culling
|
||||
|
||||
///TODO: Light culling
|
||||
}
|
||||
|
||||
void NzScene::Draw()
|
||||
{
|
||||
NzRenderer::Clear(nzRendererClear_Depth);
|
||||
|
||||
if (m_impl->background)
|
||||
m_impl->background->Draw();
|
||||
|
||||
LightComparator lightComparator;
|
||||
|
||||
// Pour les meshs squelettiques, on utilise un buffer commun
|
||||
NzRenderer::SetVertexBuffer(m_impl->skinningBuffer);
|
||||
for (auto matIt : m_impl->renderQueue.visibleSkeletalModels)
|
||||
{
|
||||
// On applique le shader du matériau
|
||||
nzUInt32 shaderFlags = matIt.first->GetShaderFlags();
|
||||
|
||||
const NzShader* shader = NzShaderBuilder::Get(shaderFlags);
|
||||
|
||||
NzRenderer::SetShader(shader);
|
||||
matIt.first->Apply(shader);
|
||||
|
||||
// Position de la caméra
|
||||
int camPosLocation = shader->GetUniformLocation("CameraPosition");
|
||||
if (camPosLocation != -1)
|
||||
shader->SendVector(camPosLocation, m_impl->activeCamera->GetPosition());
|
||||
|
||||
// Couleur ambiante de la scène
|
||||
int sceneAmbientLocation = shader->GetUniformLocation("SceneAmbient");
|
||||
if (sceneAmbientLocation != -1)
|
||||
shader->SendColor(sceneAmbientLocation, m_impl->ambientColor);
|
||||
|
||||
// Gestion des lumières (D'abord directionnelles)
|
||||
int lightCountLocation = shader->GetUniformLocation("LightCount");
|
||||
|
||||
unsigned int lightIndex = 0;
|
||||
if (lightCountLocation != -1)
|
||||
{
|
||||
for (const NzLight* light : m_impl->renderQueue.directionnalLights)
|
||||
{
|
||||
light->Apply(shader, lightIndex++);
|
||||
if (lightIndex > maxLights)
|
||||
break; // N'arrivera jamais mais pourrait résulter en un bug
|
||||
}
|
||||
}
|
||||
|
||||
for (auto subMeshIt : matIt.second)
|
||||
{
|
||||
const NzSkeletalMesh* skeletalMesh = subMeshIt.first;
|
||||
const NzIndexBuffer* indexBuffer = skeletalMesh->GetIndexBuffer();
|
||||
|
||||
unsigned int vertexCount = skeletalMesh->GetVertexCount();
|
||||
|
||||
// Gestion du draw call avant la boucle de rendu
|
||||
std::function<void(nzPrimitiveType, unsigned int, unsigned int)> drawFunc;
|
||||
nzPrimitiveType primitiveType = skeletalMesh->GetPrimitiveType();
|
||||
unsigned int indexCount;
|
||||
if (indexBuffer)
|
||||
{
|
||||
drawFunc = NzRenderer::DrawIndexedPrimitives;
|
||||
indexCount = indexBuffer->GetIndexCount();
|
||||
NzRenderer::SetIndexBuffer(indexBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
drawFunc = NzRenderer::DrawPrimitives;
|
||||
indexCount = skeletalMesh->GetVertexCount();
|
||||
}
|
||||
|
||||
for (const NzRenderQueue::SkeletalData& data : subMeshIt.second)
|
||||
{
|
||||
// Transfert du résultat du skinning vers notre buffer hardware
|
||||
NzBufferMapper<NzVertexBuffer> outputMapper(m_impl->skinningBuffer, nzBufferAccess_DiscardAndWrite, 0, vertexCount);
|
||||
std::memcpy(outputMapper.GetPointer(), &data.skinnedVertices[0], vertexCount*sizeof(NzMeshVertex));
|
||||
outputMapper.Unmap();
|
||||
|
||||
// Calcul des lumières les plus proches (TODO: LightManager ?)
|
||||
if (lightCountLocation != -1)
|
||||
{
|
||||
auto visibleLights = m_impl->renderQueue.visibleLights;
|
||||
lightComparator.pos = data.transformMatrix.GetTranslation();
|
||||
std::sort(visibleLights.begin(), visibleLights.end(), lightComparator);
|
||||
|
||||
const unsigned int maxLightPerObject = 3; ///TODO: Config
|
||||
unsigned int max = std::min(std::min(maxLights - lightIndex, maxLightPerObject), static_cast<unsigned int>(visibleLights.size()));
|
||||
for (unsigned int i = 0; i < max; ++i)
|
||||
visibleLights[i]->Apply(shader, lightIndex + i);
|
||||
|
||||
shader->SendInteger(lightCountLocation, lightIndex + max);
|
||||
}
|
||||
|
||||
NzRenderer::SetMatrix(nzMatrixType_World, data.transformMatrix);
|
||||
|
||||
drawFunc(primitiveType, 0, indexCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pour les meshs statiques, on utilise le buffer du mesh
|
||||
for (auto matIt : m_impl->renderQueue.visibleStaticModels)
|
||||
{
|
||||
// On applique le shader du matériau
|
||||
nzUInt32 shaderFlags = matIt.first->GetShaderFlags();
|
||||
if (NzRenderer::HasCapability(nzRendererCap_Instancing) && m_impl->renderQueue.visibleLights.empty())
|
||||
shaderFlags |= nzShaderFlags_Instancing;
|
||||
|
||||
const NzShader* shader = NzShaderBuilder::Get(shaderFlags);
|
||||
|
||||
NzRenderer::SetShader(shader);
|
||||
matIt.first->Apply(shader);
|
||||
|
||||
bool instancing = shader->GetFlags() & nzShaderFlags_Instancing;
|
||||
|
||||
// Position de la caméra
|
||||
int camPosLocation = shader->GetUniformLocation("CameraPosition");
|
||||
if (camPosLocation != -1)
|
||||
shader->SendVector(camPosLocation, m_impl->activeCamera->GetPosition());
|
||||
|
||||
// Couleur ambiante de la scène
|
||||
int sceneAmbientLocation = shader->GetUniformLocation("SceneAmbient");
|
||||
if (sceneAmbientLocation != -1)
|
||||
shader->SendColor(sceneAmbientLocation, m_impl->ambientColor);
|
||||
|
||||
// Gestion des lumières (D'abord directionnelles)
|
||||
int lightCountLocation = shader->GetUniformLocation("LightCount");
|
||||
|
||||
unsigned int lightIndex = 0;
|
||||
if (lightCountLocation != -1)
|
||||
{
|
||||
for (const NzLight* light : m_impl->renderQueue.directionnalLights)
|
||||
{
|
||||
light->Apply(shader, lightIndex++);
|
||||
if (lightIndex > maxLights)
|
||||
break; // N'arrivera probablement jamais mais pourrait résulter en un bug
|
||||
}
|
||||
}
|
||||
|
||||
for (auto subMeshIt : matIt.second)
|
||||
{
|
||||
NzStaticMesh* staticMesh = subMeshIt.first;
|
||||
|
||||
const NzIndexBuffer* indexBuffer = staticMesh->GetIndexBuffer();
|
||||
const NzVertexBuffer* vertexBuffer = staticMesh->GetVertexBuffer();
|
||||
|
||||
NzRenderer::SetVertexBuffer(vertexBuffer);
|
||||
|
||||
// Gestion du draw call avant la boucle de rendu
|
||||
std::function<void(nzPrimitiveType, unsigned int, unsigned int)> draw;
|
||||
std::function<void(unsigned int, nzPrimitiveType, unsigned int, unsigned int)> instancedDraw;
|
||||
nzPrimitiveType primitiveType = staticMesh->GetPrimitiveType();
|
||||
unsigned int indexCount;
|
||||
if (indexBuffer)
|
||||
{
|
||||
draw = NzRenderer::DrawIndexedPrimitives;
|
||||
indexCount = indexBuffer->GetIndexCount();
|
||||
instancedDraw = NzRenderer::DrawIndexedPrimitivesInstanced;
|
||||
NzRenderer::SetIndexBuffer(indexBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
draw = NzRenderer::DrawPrimitives;
|
||||
indexCount = vertexBuffer->GetVertexCount();
|
||||
instancedDraw = NzRenderer::DrawPrimitivesInstanced;
|
||||
}
|
||||
|
||||
if (instancing)
|
||||
{
|
||||
if (lightCountLocation != -1)
|
||||
shader->SendInteger(lightCountLocation, lightIndex);
|
||||
|
||||
unsigned int count = 0;
|
||||
for (const NzMatrix4f& matrix : subMeshIt.second)
|
||||
{
|
||||
m_impl->instancingData[count++].worldMatrix = matrix;
|
||||
if (count == m_impl->instancingData.size())
|
||||
{
|
||||
NzRenderer::SetInstancingData(&m_impl->instancingData[0], count);
|
||||
instancedDraw(count, primitiveType, 0, indexCount);
|
||||
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
NzRenderer::SetInstancingData(&m_impl->instancingData[0], count);
|
||||
instancedDraw(count, primitiveType, 0, indexCount);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const NzMatrix4f& matrix : subMeshIt.second)
|
||||
{
|
||||
// Calcul des lumières les plus proches (TODO: LightManager ?)
|
||||
if (lightCountLocation != -1)
|
||||
{
|
||||
std::vector<const NzLight*>& visibleLights = m_impl->renderQueue.visibleLights;
|
||||
lightComparator.pos = matrix.GetTranslation();
|
||||
std::sort(visibleLights.begin(), visibleLights.end(), lightComparator);
|
||||
|
||||
const unsigned int maxLightPerObject = 3; ///TODO: Config
|
||||
unsigned int max = std::min(std::min(maxLights - lightIndex, maxLightPerObject), visibleLights.size());
|
||||
for (unsigned int i = 0; i < max; ++i)
|
||||
visibleLights[i]->Apply(shader, lightIndex + i);
|
||||
|
||||
shader->SendInteger(lightCountLocation, lightIndex + max);
|
||||
}
|
||||
|
||||
NzRenderer::SetMatrix(nzMatrixType_World, matrix);
|
||||
|
||||
draw(primitiveType, 0, indexCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Les autres drawables (Exemple: Terrain)
|
||||
for (const NzDrawable* drawable : m_impl->renderQueue.otherDrawables)
|
||||
drawable->Draw();
|
||||
}
|
||||
|
||||
const NzCamera* NzScene::GetActiveCamera() const
|
||||
{
|
||||
return m_impl->activeCamera;
|
||||
}
|
||||
|
||||
NzBackground* NzScene::GetBackground() const
|
||||
{
|
||||
return m_impl->background.get();
|
||||
}
|
||||
|
||||
NzSceneNode& NzScene::GetRoot() const
|
||||
{
|
||||
return m_impl->root;
|
||||
}
|
||||
|
||||
float NzScene::GetUpdateTime() const
|
||||
{
|
||||
return m_impl->updateTime;
|
||||
}
|
||||
|
||||
unsigned int NzScene::GetUpdatePerSecond() const
|
||||
{
|
||||
return m_impl->updatePerSecond;
|
||||
}
|
||||
|
||||
void NzScene::RegisterForUpdate(NzUpdatable* object)
|
||||
{
|
||||
#if NAZARA_3D_SAFE
|
||||
if (!object)
|
||||
{
|
||||
NazaraError("Invalid object");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_impl->updateList.push_back(object);
|
||||
}
|
||||
|
||||
void NzScene::SetAmbientColor(const NzColor& color)
|
||||
{
|
||||
m_impl->ambientColor = color;
|
||||
}
|
||||
|
||||
void NzScene::SetBackground(NzBackground* background)
|
||||
{
|
||||
m_impl->background.reset(background);
|
||||
}
|
||||
|
||||
void NzScene::SetUpdatePerSecond(unsigned int updatePerSecond)
|
||||
{
|
||||
m_impl->updatePerSecond = updatePerSecond;
|
||||
}
|
||||
|
||||
void NzScene::UnregisterForUpdate(NzUpdatable* object)
|
||||
{
|
||||
#if NAZARA_3D_SAFE
|
||||
if (!object)
|
||||
{
|
||||
NazaraError("Invalid object");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
auto it = std::find(m_impl->updateList.begin(), m_impl->updateList.end(), object);
|
||||
if (it != m_impl->updateList.end())
|
||||
m_impl->updateList.erase(it);
|
||||
}
|
||||
|
||||
void NzScene::Update()
|
||||
{
|
||||
m_impl->update = (m_impl->updatePerSecond == 0 || m_impl->updateClock.GetMilliseconds() > 1000/m_impl->updatePerSecond);
|
||||
if (m_impl->update)
|
||||
{
|
||||
m_impl->updateTime = m_impl->updateClock.GetSeconds();
|
||||
m_impl->updateClock.Restart();
|
||||
|
||||
for (NzUpdatable* updatable : m_impl->updateList)
|
||||
///TODO: Multihreading
|
||||
updatable->Update();
|
||||
}
|
||||
}
|
||||
|
||||
void NzScene::UpdateVisible()
|
||||
{
|
||||
if (m_impl->update)
|
||||
{
|
||||
for (NzUpdatable* node : m_impl->visibleUpdateList)
|
||||
node->Update();
|
||||
}
|
||||
}
|
||||
|
||||
NzScene::operator const NzSceneNode&() const
|
||||
{
|
||||
return m_impl->root;
|
||||
}
|
||||
|
||||
void NzScene::RecursiveFrustumCull(NzRenderQueue& renderQueue, const NzFrustumf& frustum, NzNode* node)
|
||||
{
|
||||
for (NzNode* child : node->GetChilds())
|
||||
{
|
||||
if (child->GetNodeType() == nzNodeType_Scene)
|
||||
{
|
||||
NzSceneNode* sceneNode = static_cast<NzSceneNode*>(child);
|
||||
///TODO: Empêcher le rendu des enfants si le parent est cullé selon un flag
|
||||
sceneNode->UpdateVisibility(frustum);
|
||||
if (sceneNode->IsVisible())
|
||||
sceneNode->AddToRenderQueue(renderQueue);
|
||||
}
|
||||
|
||||
if (child->HasChilds())
|
||||
RecursiveFrustumCull(renderQueue, frustum, child);
|
||||
}
|
||||
}
|
||||
|
||||
void NzScene::SetActiveCamera(const NzCamera* camera)
|
||||
{
|
||||
m_impl->activeCamera = camera;
|
||||
}
|
||||
107
src/Nazara/Graphics/SceneNode.cpp
Normal file
107
src/Nazara/Graphics/SceneNode.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
// Copyright (C) 2013 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/SceneNode.hpp>
|
||||
#include <Nazara/Graphics/Scene.hpp>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
NzSceneNode::NzSceneNode() :
|
||||
m_scene(nullptr),
|
||||
m_visible(false)
|
||||
{
|
||||
}
|
||||
|
||||
NzSceneNode::NzSceneNode(const NzSceneNode& node) :
|
||||
NzNode(node),
|
||||
m_scene(node.m_scene),
|
||||
m_visible(false)
|
||||
{
|
||||
}
|
||||
|
||||
NzSceneNode::~NzSceneNode() = default;
|
||||
|
||||
nzNodeType NzSceneNode::GetNodeType() const
|
||||
{
|
||||
return nzNodeType_Scene;
|
||||
}
|
||||
|
||||
NzScene* NzSceneNode::GetScene() const
|
||||
{
|
||||
return m_scene;
|
||||
}
|
||||
|
||||
bool NzSceneNode::IsVisible() const
|
||||
{
|
||||
return m_visible;
|
||||
}
|
||||
|
||||
void NzSceneNode::OnParenting(const NzNode* parent)
|
||||
{
|
||||
if (parent)
|
||||
{
|
||||
if (parent->GetNodeType() == nzNodeType_Scene)
|
||||
SetScene(static_cast<const NzSceneNode*>(parent)->m_scene);
|
||||
}
|
||||
else
|
||||
SetScene(nullptr);
|
||||
}
|
||||
|
||||
void NzSceneNode::OnVisibilityChange(bool visibility)
|
||||
{
|
||||
NazaraUnused(visibility);
|
||||
|
||||
///TODO: Envoyer l'évènements aux listeners
|
||||
}
|
||||
|
||||
void NzSceneNode::RecursiveSetScene(NzScene* scene, NzNode* node)
|
||||
{
|
||||
for (NzNode* child : node->GetChilds())
|
||||
{
|
||||
if (child->GetNodeType() == nzNodeType_Scene)
|
||||
{
|
||||
NzSceneNode* sceneNode = static_cast<NzSceneNode*>(child);
|
||||
sceneNode->SetScene(scene);
|
||||
}
|
||||
|
||||
if (node->HasChilds())
|
||||
RecursiveSetScene(scene, node);
|
||||
}
|
||||
}
|
||||
|
||||
void NzSceneNode::Register()
|
||||
{
|
||||
}
|
||||
|
||||
void NzSceneNode::SetScene(NzScene* scene)
|
||||
{
|
||||
if (m_scene != scene)
|
||||
{
|
||||
if (m_scene)
|
||||
Unregister();
|
||||
|
||||
m_scene = scene;
|
||||
if (m_scene)
|
||||
Register();
|
||||
|
||||
RecursiveSetScene(scene, this);
|
||||
}
|
||||
}
|
||||
|
||||
void NzSceneNode::Unregister()
|
||||
{
|
||||
}
|
||||
|
||||
void NzSceneNode::Update()
|
||||
{
|
||||
}
|
||||
|
||||
void NzSceneNode::UpdateVisibility(const NzFrustumf& frustum)
|
||||
{
|
||||
bool wasVisible = m_visible;
|
||||
|
||||
m_visible = VisibilityTest(frustum);
|
||||
|
||||
if (m_visible != wasVisible)
|
||||
OnVisibilityChange(m_visible);
|
||||
}
|
||||
49
src/Nazara/Graphics/SceneRoot.cpp
Normal file
49
src/Nazara/Graphics/SceneRoot.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright (C) 2013 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/SceneRoot.hpp>
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
NzSceneRoot::NzSceneRoot(NzScene* scene)
|
||||
{
|
||||
m_scene = scene;
|
||||
}
|
||||
|
||||
NzSceneRoot::~NzSceneRoot() = default;
|
||||
|
||||
void NzSceneRoot::AddToRenderQueue(NzRenderQueue& renderQueue) const
|
||||
{
|
||||
NazaraUnused(renderQueue);
|
||||
|
||||
NazaraInternalError("SceneNode::AddToRenderQueue() called on SceneRoot");
|
||||
}
|
||||
|
||||
const NzBoundingBoxf& NzSceneRoot::GetBoundingBox() const
|
||||
{
|
||||
static NzBoundingBoxf infinite(nzExtend_Infinite);
|
||||
return infinite;
|
||||
}
|
||||
|
||||
nzSceneNodeType NzSceneRoot::GetSceneNodeType() const
|
||||
{
|
||||
return nzSceneNodeType_Root;
|
||||
}
|
||||
|
||||
void NzSceneRoot::Register()
|
||||
{
|
||||
NazaraInternalError("SceneNode::Register() called on SceneRoot");
|
||||
}
|
||||
|
||||
void NzSceneRoot::Unregister()
|
||||
{
|
||||
NazaraInternalError("SceneNode::Unregister() called on SceneRoot");
|
||||
}
|
||||
|
||||
bool NzSceneRoot::VisibilityTest(const NzFrustumf& frustum)
|
||||
{
|
||||
NazaraUnused(frustum);
|
||||
|
||||
return true; // Toujours visible
|
||||
}
|
||||
44
src/Nazara/Graphics/TextureBackground.cpp
Normal file
44
src/Nazara/Graphics/TextureBackground.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (C) 2013 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/TextureBackground.hpp>
|
||||
#include <Nazara/Renderer/Renderer.hpp>
|
||||
#include <Nazara/Renderer/RenderTarget.hpp>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
NzTextureBackground::NzTextureBackground(NzTexture* texture) :
|
||||
m_texture(texture)
|
||||
{
|
||||
}
|
||||
|
||||
void NzTextureBackground::Draw() const
|
||||
{
|
||||
const NzRenderTarget* target = NzRenderer::GetTarget();
|
||||
NzRectui viewport = NzRenderer::GetViewport();
|
||||
|
||||
// Sous forme de flottants pour la division flottante
|
||||
float width = target->GetWidth();
|
||||
float height = target->GetHeight();
|
||||
|
||||
NzVector2f uv0(viewport.x/width, viewport.y/height);
|
||||
NzVector2f uv1((viewport.x+viewport.width)/width, (viewport.y+viewport.height)/height);
|
||||
|
||||
NzRenderer::SetTexture(0, m_texture);
|
||||
NzRenderer::DrawTexture(0, NzRectf(0.f, 0.f, width, height), uv0, uv1, 1.f);
|
||||
}
|
||||
|
||||
nzBackgroundType NzTextureBackground::GetBackgroundType() const
|
||||
{
|
||||
return nzBackgroundType_Texture;
|
||||
}
|
||||
|
||||
NzTexture* NzTextureBackground::GetTexture() const
|
||||
{
|
||||
return m_texture;
|
||||
}
|
||||
|
||||
void NzTextureBackground::SetTexture(NzTexture* texture)
|
||||
{
|
||||
m_texture = texture;
|
||||
}
|
||||
Reference in New Issue
Block a user