From ab1fc99fcdcb37502b0e14dd47183cdad54b0b2d Mon Sep 17 00:00:00 2001 From: Lynix Date: Sat, 2 Mar 2013 01:29:44 +0100 Subject: [PATCH] Big 3D update (WIP) Former-commit-id: 9f55dae0521bded91640a7ea2d223a49a378c97c --- include/Nazara/3D.hpp | 6 +- include/Nazara/3D/Camera.hpp | 73 ++++++ include/Nazara/3D/Enums.hpp | 5 +- include/Nazara/3D/Light.hpp | 16 +- include/Nazara/3D/Model.hpp | 32 ++- include/Nazara/3D/RenderQueue.hpp | 62 +++++ include/Nazara/3D/Scene.hpp | 53 ++++ include/Nazara/3D/SceneNode.hpp | 29 ++- include/Nazara/3D/SceneRoot.hpp | 35 +++ src/Nazara/3D/3D.cpp | 7 +- src/Nazara/3D/Camera.cpp | 243 ++++++++++++++++++ src/Nazara/3D/Light.cpp | 51 +++- src/Nazara/3D/Model.cpp | 291 +++++++++++++++++---- src/Nazara/3D/RenderQueue.cpp | 95 +++++++ src/Nazara/3D/Scene.cpp | 407 ++++++++++++++++++++++++++++++ src/Nazara/3D/SceneNode.cpp | 69 +++++ src/Nazara/3D/SceneRoot.cpp | 49 ++++ 17 files changed, 1446 insertions(+), 77 deletions(-) create mode 100644 include/Nazara/3D/Camera.hpp create mode 100644 include/Nazara/3D/RenderQueue.hpp create mode 100644 include/Nazara/3D/Scene.hpp create mode 100644 include/Nazara/3D/SceneRoot.hpp create mode 100644 src/Nazara/3D/Camera.cpp create mode 100644 src/Nazara/3D/RenderQueue.cpp create mode 100644 src/Nazara/3D/Scene.cpp create mode 100644 src/Nazara/3D/SceneRoot.cpp diff --git a/include/Nazara/3D.hpp b/include/Nazara/3D.hpp index 6a255bdfb..3995dca4e 100644 --- a/include/Nazara/3D.hpp +++ b/include/Nazara/3D.hpp @@ -1,4 +1,4 @@ -// This file was automatically generated by Nazara +// This file was automatically generated on 20 Feb 2013 at 17:02:32 /* Nazara Engine - 3D module @@ -30,9 +30,13 @@ #define NAZARA_GLOBAL_3D_HPP #include +#include #include #include +#include #include +#include #include +#include #endif // NAZARA_GLOBAL_3D_HPP diff --git a/include/Nazara/3D/Camera.hpp b/include/Nazara/3D/Camera.hpp new file mode 100644 index 000000000..33485206a --- /dev/null +++ b/include/Nazara/3D/Camera.hpp @@ -0,0 +1,73 @@ +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - 3D Module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_CAMERA_HPP +#define NAZARA_CAMERA_HPP + +#include +#include +#include +#include +#include +#include + +class NzRenderTarget; + +class NAZARA_API NzCamera : public NzSceneNode +{ + public: + NzCamera(); + ~NzCamera(); + + void Activate() const; + + void EnsureFrustumUpdate() const; + void EnsureProjectionMatrixUpdate() const; + void EnsureViewMatrixUpdate() const; + + float GetAspectRatio() const; + const NzBoundingBoxf& GetBoundingBox() const override; + float GetFOV() const; + const NzFrustumf& GetFrustum() const; + const NzMatrix4f& GetProjectionMatrix() const; + nzSceneNodeType GetSceneNodeType() const override; + const NzVector3f& GetUpVector() const; + const NzMatrix4f& GetViewMatrix() const; + const NzRectf& GetViewport() const; + float GetZFar() const; + float GetZNear() const; + + void SetFOV(float fov); + void SetUpVector(const NzVector3f& upVector); + void SetViewport(const NzRectf& viewport); + void SetZFar(float zFar); + void SetZNear(float zNear); + + private: + void AddToRenderQueue(NzRenderQueue& renderQueue) const; + void Invalidate(); + bool IsVisible(const NzFrustumf& frustum) const override; + void Register(); + void Unregister(); + void UpdateFrustum() const; + void UpdateProjectionMatrix() const; + void UpdateViewMatrix() const; + + mutable NzFrustumf m_frustum; + mutable NzMatrix4f m_projectionMatrix; + mutable NzMatrix4f m_viewMatrix; + NzRectf m_viewport; + NzVector3f m_upVector; + mutable bool m_frustumUpdated; + mutable bool m_projectionMatrixUpdated; + mutable bool m_viewMatrixUpdated; + mutable float m_aspectRatio; + float m_fov; + float m_zFar; + float m_zNear; +}; + +#endif // NAZARA_CAMERA_HPP diff --git a/include/Nazara/3D/Enums.hpp b/include/Nazara/3D/Enums.hpp index 2cae8e416..446b415b3 100644 --- a/include/Nazara/3D/Enums.hpp +++ b/include/Nazara/3D/Enums.hpp @@ -18,10 +18,13 @@ enum nzLightType enum nzSceneNodeType { + nzSceneNodeType_Camera, nzSceneNodeType_Light, nzSceneNodeType_Model, + nzSceneNodeType_Root, + nzSceneNodeType_User, - nzSceneNodeType_Max = nzSceneNodeType_Model + nzSceneNodeType_Max = nzSceneNodeType_User }; #endif // NAZARA_ENUMS_3D_HPP diff --git a/include/Nazara/3D/Light.hpp b/include/Nazara/3D/Light.hpp index 3eab48855..19ac8aea2 100644 --- a/include/Nazara/3D/Light.hpp +++ b/include/Nazara/3D/Light.hpp @@ -12,6 +12,8 @@ #include #include +class NzShader; + class NAZARA_API NzLight : public NzSceneNode { public: @@ -19,9 +21,11 @@ class NAZARA_API NzLight : public NzSceneNode NzLight(const NzLight& light); ~NzLight(); - void Apply(unsigned int unit) const; + void AddToRenderQueue(NzRenderQueue& renderQueue) const; - const NzAxisAlignedBox& GetAABB() const; + void Apply(const NzShader* shader, unsigned int lightUnit) const; + + const NzBoundingBoxf& GetBoundingBox() const; NzColor GetAmbientColor() const; float GetAttenuation() const; NzColor GetDiffuseColor() const; @@ -32,6 +36,8 @@ class NAZARA_API NzLight : public NzSceneNode nzSceneNodeType GetSceneNodeType() const; NzColor GetSpecularColor() const; + bool IsVisible(const NzFrustumf& frustum) const; + void SetAmbientColor(const NzColor& ambient); void SetAttenuation(float attenuation); void SetDiffuseColor(const NzColor& diffuse); @@ -43,10 +49,16 @@ class NAZARA_API NzLight : public NzSceneNode NzLight& operator=(const NzLight& light); private: + void Register(); + void Unregister(); + void UpdateFrustum(); + nzLightType m_type; + NzBoundingBoxf m_boundingBox; NzColor m_ambientColor; NzColor m_diffuseColor; NzColor m_specularColor; + bool m_boundingBoxUpdated; float m_attenuation; float m_innerAngle; float m_outerAngle; diff --git a/include/Nazara/3D/Model.hpp b/include/Nazara/3D/Model.hpp index 29243cfd6..0eda190ef 100644 --- a/include/Nazara/3D/Model.hpp +++ b/include/Nazara/3D/Model.hpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -21,18 +22,27 @@ struct NzModelParameters NzMaterialParams materialParams; }; -class NAZARA_API NzModel : public NzSceneNode +class NAZARA_API NzModel : public NzSceneNode, public NzUpdatable { + friend class NzScene; + public: NzModel(); NzModel(const NzModel& model); ~NzModel(); + void AddToRenderQueue(NzRenderQueue& renderQueue) const; + void AdvanceAnimation(float elapsedTime); + + void EnableAnimation(bool animation); + void EnableDraw(bool draw); + NzAnimation* GetAnimation() const; - const NzAxisAlignedBox& GetAABB() const; + const NzBoundingBoxf& GetBoundingBox() const; NzMaterial* GetMaterial(unsigned int matIndex) const; NzMaterial* GetMaterial(unsigned int skinIndex, unsigned int matIndex) const; unsigned int GetMaterialCount() const; + unsigned int GetSkin() const; unsigned int GetSkinCount() const; NzMesh* GetMesh() const; nzSceneNodeType GetSceneNodeType() const override; @@ -41,6 +51,10 @@ class NAZARA_API NzModel : public NzSceneNode bool HasAnimation() const; + bool IsAnimationEnabled() const; + bool IsDrawEnabled() const; + bool IsVisible(const NzFrustumf& frustum) const override; + bool LoadFromFile(const NzString& meshPath, const NzMeshParams& meshParameters = NzMeshParams(), const NzModelParameters& modelParameters = NzModelParameters()); bool LoadFromMemory(const void* data, std::size_t size, const NzMeshParams& meshParameters = NzMeshParams(), const NzModelParameters& modelParameters = NzModelParameters()); bool LoadFromStream(NzInputStream& stream, const NzMeshParams& meshParameters = NzMeshParams(), const NzModelParameters& modelParameters = NzModelParameters()); @@ -51,22 +65,32 @@ class NAZARA_API NzModel : public NzSceneNode void SetMaterial(unsigned int matIndex, NzMaterial* material); void SetMaterial(unsigned int skinIndex, unsigned int matIndex, NzMaterial* material); void SetMesh(NzMesh* mesh, const NzModelParameters& parameters = NzModelParameters()); + void SetSkin(unsigned int skin); void SetSkinCount(unsigned int skinCount); bool SetSequence(const NzString& sequenceName); void SetSequence(unsigned int sequenceIndex); - void Update(float elapsedTime); - private: + void Invalidate() override; + void Register() override; + void Unregister() override; + void Update() override; + void UpdateBoundingBox() const; + std::vector m_materials; + mutable NzBoundingBoxf m_boundingBox; NzSkeleton m_skeleton; // Uniquement pour les animations squelettiques NzAnimation* m_animation; NzMesh* m_mesh; const NzSequence* m_currentSequence; + bool m_animationEnabled; + mutable bool m_boundingBoxUpdated; + bool m_drawEnabled; float m_interpolation; unsigned int m_currentFrame; unsigned int m_matCount; unsigned int m_nextFrame; + unsigned int m_skin; unsigned int m_skinCount; }; diff --git a/include/Nazara/3D/RenderQueue.hpp b/include/Nazara/3D/RenderQueue.hpp new file mode 100644 index 000000000..726e5f28f --- /dev/null +++ b/include/Nazara/3D/RenderQueue.hpp @@ -0,0 +1,62 @@ +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - 3D Module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#ifndef NAZARA_RENDERQUEUE_HPP +#define NAZARA_RENDERQUEUE_HPP + +#include +#include +#include +#include +#include + +class NzDrawable; +class NzLight; +class NzMaterial; +class NzModel; +class NzSkeletalMesh; +class NzStaticMesh; + +class NAZARA_API NzRenderQueue +{ + public: + struct MaterialComparator + { + bool operator()(const NzMaterial* mat1, const NzMaterial* mat2); + }; + + struct SkeletalMeshComparator + { + bool operator()(const NzSkeletalMesh* subMesh1, const NzSkeletalMesh* subMesh2); + }; + + struct StaticMeshComparator + { + bool operator()(const NzStaticMesh* subMesh1, const NzStaticMesh* subMesh2); + }; + + NzRenderQueue() = default; + ~NzRenderQueue() = default; + + void Clear(); + + struct SkeletalData + { + NzMatrix4f transformMatrix; + + ///TODO: Déplacer vers un container séparé qui ne serait pas sujer à Clear(); + std::vector skinnedVertices; + }; + + typedef std::map, SkeletalMeshComparator> SkeletalMeshContainer; + typedef std::map, StaticMeshComparator> StaticMeshContainer; + + std::map visibleSkeletalModels; + std::map visibleStaticModels; + std::vector otherDrawables; + std::vector directionnalLights; + std::vector visibleLights; +}; + + #endif // NAZARA_RENDERQUEUE_HPP diff --git a/include/Nazara/3D/Scene.hpp b/include/Nazara/3D/Scene.hpp new file mode 100644 index 000000000..5e49cedd8 --- /dev/null +++ b/include/Nazara/3D/Scene.hpp @@ -0,0 +1,53 @@ +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - 3D Module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_SCENE_HPP +#define NAZARA_SCENE_HPP + +#include +#include +#include + +class NzCamera; +class NzLight; +class NzModel; +class NzSceneNode; +struct NzSceneImpl; + +class NAZARA_API NzScene +{ + friend NzCamera; + + public: + NzScene(); + ~NzScene(); + + void Cull(); + void Draw(); + + NzSceneNode& GetRoot() const; + float GetUpdateTime() const; + unsigned int GetUpdatePerSecond() const; + + void RegisterForUpdate(NzUpdatable* node); + + void SetAmbientColor(const NzColor& color); + void SetUpdatePerSecond(unsigned int updatePerSecond); + + void UnregisterForUpdate(NzUpdatable* node); + + void Update(); + void UpdateVisible(); + + operator const NzSceneNode&() const; + + private: + void SetActiveCamera(const NzCamera* camera); + + NzSceneImpl* m_impl; +}; + +#endif // NAZARA_SCENE_HPP diff --git a/include/Nazara/3D/SceneNode.hpp b/include/Nazara/3D/SceneNode.hpp index cc531eab8..482d55da2 100644 --- a/include/Nazara/3D/SceneNode.hpp +++ b/include/Nazara/3D/SceneNode.hpp @@ -9,16 +9,39 @@ #include #include -#include +#include +#include +#include +#include #include -class NzSceneNode : public NzNode +class NAZARA_API NzSceneNode : public NzNode { + friend class NzScene; + public: + NzSceneNode(); + NzSceneNode(const NzSceneNode& node); virtual ~NzSceneNode(); - virtual const NzAxisAlignedBox& GetAABB() const = 0; + virtual void AddToRenderQueue(NzRenderQueue& renderQueue) const = 0; + + virtual const NzBoundingBoxf& GetBoundingBox() const = 0; + nzNodeType GetNodeType() const final; + NzScene* GetScene() const; virtual nzSceneNodeType GetSceneNodeType() const = 0; + + virtual bool IsVisible(const NzFrustumf& frustum) const = 0; + + protected: + virtual void OnParenting(const NzNode* parent) override; + virtual void Register(); + void SetScene(NzScene* scene); + virtual bool ShouldUpdateWhenVisible(); + virtual void Unregister(); + virtual void Update(); + + NzScene* m_scene; }; #endif // NAZARA_SCENENODE_HPP diff --git a/include/Nazara/3D/SceneRoot.hpp b/include/Nazara/3D/SceneRoot.hpp new file mode 100644 index 000000000..b1c34731e --- /dev/null +++ b/include/Nazara/3D/SceneRoot.hpp @@ -0,0 +1,35 @@ +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - 3D Module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_SCENEROOT_HPP +#define NAZARA_SCENEROOT_HPP + +#include +#include + +struct NzSceneImpl; + +class NAZARA_API NzSceneRoot : public NzSceneNode +{ + friend struct NzSceneImpl; + + public: + void AddToRenderQueue(NzRenderQueue& renderQueue) const override; + + const NzBoundingBoxf& GetBoundingBox() const override; + nzSceneNodeType GetSceneNodeType() const override; + + bool IsVisible(const NzFrustumf& frustum) const override; + + private: + NzSceneRoot(NzScene* scene); + virtual ~NzSceneRoot(); + + void Register(); + void Unregister(); +}; + +#endif // NAZARA_SCENEROOT_HPP diff --git a/src/Nazara/3D/3D.cpp b/src/Nazara/3D/3D.cpp index 3ec86b628..6069abff6 100644 --- a/src/Nazara/3D/3D.cpp +++ b/src/Nazara/3D/3D.cpp @@ -3,6 +3,7 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #include #include #include @@ -15,9 +16,9 @@ bool Nz3D::Initialize() return true; // Déjà initialisé // Initialisation des dépendances - if (!NzRenderer::Initialize()) + if (!Nz2D::Initialize()) { - NazaraError("Failed to initialize renderer module"); + NazaraError("Failed to initialize 2D module"); Uninitialize(); return false; @@ -52,7 +53,7 @@ void Nz3D::Uninitialize() NazaraNotice("Uninitialized: 3D module"); // Libération des dépendances - NzRenderer::Uninitialize(); + Nz2D::Uninitialize(); } unsigned int Nz3D::s_moduleReferenceCounter = 0; diff --git a/src/Nazara/3D/Camera.cpp b/src/Nazara/3D/Camera.cpp new file mode 100644 index 000000000..08c8b9e35 --- /dev/null +++ b/src/Nazara/3D/Camera.cpp @@ -0,0 +1,243 @@ +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - 3D Module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include + +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 +{ + NzRenderTarget* renderTarget = NzRenderer::GetTarget(); + + #ifdef NAZARA_3D_SAFE + if (!renderTarget) + { + NazaraError("No render target !"); + return; + } + #endif + + unsigned int width = renderTarget->GetWidth(); + unsigned int height = std::max(renderTarget->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 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::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; +} + +bool NzCamera::IsVisible(const NzFrustumf& frustum) const +{ + NazaraUnused(frustum); + //NazaraInternalError("SceneNode::IsVisible() called on Camera"); + return 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; +} diff --git a/src/Nazara/3D/Light.cpp b/src/Nazara/3D/Light.cpp index b59c0a998..0ef739e60 100644 --- a/src/Nazara/3D/Light.cpp +++ b/src/Nazara/3D/Light.cpp @@ -17,6 +17,7 @@ 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), @@ -24,14 +25,32 @@ m_radius(500.f) { } -NzLight::NzLight(const NzLight& light) +NzLight::NzLight(const NzLight& light) : +NzSceneNode(light) { std::memcpy(this, &light, sizeof(NzLight)); // Aussi simple que ça } -NzLight::~NzLight() = default; +NzLight::~NzLight() +{ +} -void NzLight::Apply(unsigned int unit) const +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 @@ -58,7 +77,6 @@ void NzLight::Apply(unsigned int unit) const -P2: vec3 direction + float invRadius -P3: float cosInnerAngle + float cosOuterAngle */ - const NzShader* shader = NzRenderer::GetShader(); int typeLocation = shader->GetUniformLocation("Lights[0].type"); int ambientLocation = shader->GetUniformLocation("Lights[0].ambient"); @@ -68,10 +86,10 @@ void NzLight::Apply(unsigned int unit) const int parameters2Location = shader->GetUniformLocation("Lights[0].parameters2"); int parameters3Location = shader->GetUniformLocation("Lights[0].parameters3"); - if (unit > 0) + if (lightUnit > 0) { int type2Location = shader->GetUniformLocation("Lights[1].type"); - int offset = unit * (type2Location - typeLocation); // type2Location - typeLocation donne la taille de la structure + int offset = lightUnit * (type2Location - typeLocation); // type2Location - typeLocation donne la taille de la structure // On applique cet offset typeLocation += offset; @@ -110,9 +128,10 @@ void NzLight::Apply(unsigned int unit) const } } -const NzAxisAlignedBox& NzLight::GetAABB() const +const NzBoundingBoxf& NzLight::GetBoundingBox() const { - return NzAxisAlignedBox::Null; + static NzBoundingBoxf dummy(nzExtend_Null); + return dummy; } NzColor NzLight::GetAmbientColor() const @@ -160,6 +179,14 @@ NzColor NzLight::GetSpecularColor() const return m_specularColor; } +bool NzLight::IsVisible(const NzFrustumf& frustum) const +{ + NazaraUnused(frustum); + + ///FIXME: Pour l'instant toujours visible + return true; // Toujours visible +} + void NzLight::SetAmbientColor(const NzColor& ambient) { m_ambientColor = ambient; @@ -201,3 +228,11 @@ NzLight& NzLight::operator=(const NzLight& light) return *this; } + +void NzLight::Register() +{ +} + +void NzLight::Unregister() +{ +} diff --git a/src/Nazara/3D/Model.cpp b/src/Nazara/3D/Model.cpp index 36a00a6aa..26e14d86a 100644 --- a/src/Nazara/3D/Model.cpp +++ b/src/Nazara/3D/Model.cpp @@ -4,24 +4,40 @@ #include #include +#include +#include #include +#include #include NzModel::NzModel() : m_animation(nullptr), m_mesh(nullptr), m_currentSequence(nullptr), +m_animationEnabled(true), +m_boundingBoxUpdated(false), +m_drawEnabled(true), m_matCount(0), -m_skinCount(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_animation(model.m_animation), m_mesh(model.m_mesh), 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 (m_mesh) @@ -31,6 +47,9 @@ m_skinCount(model.m_skinCount) m_mesh->AddResourceReference(); + if (m_mesh->GetAnimationType() == nzAnimationType_Skeletal) + m_skeleton = model.m_skeleton; + // Nous n'avons des matériaux que si nous avons un mesh for (const NzMaterial* material : m_materials) material->AddResourceReference(); @@ -42,22 +61,119 @@ 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(subMesh); + std::vector& 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(subMesh); + std::vector& 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 NzAxisAlignedBox& NzModel::GetAABB() const +const NzBoundingBoxf& NzModel::GetBoundingBox() const { #if NAZARA_3D_SAFE if (!m_mesh) { NazaraError("Model has no mesh"); - return NzAxisAlignedBox::Null; + + static NzBoundingBoxf dummy(nzExtend_Null); + return dummy; } #endif - return m_mesh->GetAABB(); + if (!m_boundingBoxUpdated) + UpdateBoundingBox(); + + return m_boundingBox; } NzMaterial* NzModel::GetMaterial(unsigned int matIndex) const @@ -70,7 +186,7 @@ NzMaterial* NzModel::GetMaterial(unsigned int matIndex) const } #endif - return m_materials[matIndex]; + return m_materials[m_skin*m_matCount + matIndex]; } NzMaterial* NzModel::GetMaterial(unsigned int skinIndex, unsigned int matIndex) const @@ -89,7 +205,7 @@ NzMaterial* NzModel::GetMaterial(unsigned int skinIndex, unsigned int matIndex) } #endif - return m_materials[matIndex]; + return m_materials[skinIndex*m_matCount + matIndex]; } unsigned int NzModel::GetMaterialCount() const @@ -97,11 +213,6 @@ unsigned int NzModel::GetMaterialCount() const return m_matCount; } -unsigned int NzModel::GetSkinCount() const -{ - return m_skinCount; -} - NzMesh* NzModel::GetMesh() const { return m_mesh; @@ -122,11 +233,50 @@ 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::IsVisible(const NzFrustumf& frustum) const +{ + #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); +} + bool NzModel::LoadFromFile(const NzString& meshPath, const NzMeshParams& meshParameters, const NzModelParameters& modelParameters) { ///TODO: ResourceManager @@ -175,6 +325,9 @@ bool NzModel::LoadFromStream(NzInputStream& stream, const NzMeshParams& meshPara void NzModel::Reset() { + if (m_scene) + m_scene->UnregisterForUpdate(this); + m_matCount = 0; m_skinCount = 0; @@ -238,7 +391,12 @@ bool NzModel::SetAnimation(NzAnimation* animation) m_interpolation = 0.f; SetSequence(0); + + if (m_scene) + m_scene->RegisterForUpdate(this); } + else if (m_scene) + m_scene->UnregisterForUpdate(this); return true; } @@ -253,14 +411,16 @@ void NzModel::SetMaterial(unsigned int matIndex, NzMaterial* material) } #endif - m_materials[matIndex]->RemoveResourceReference(); + unsigned int index = m_skin*m_matCount + matIndex; + + m_materials[index]->RemoveResourceReference(); if (material) - m_materials[matIndex] = material; + m_materials[index] = material; else - m_materials[matIndex] = NzMaterial::GetDefault(); + m_materials[index] = NzMaterial::GetDefault(); - m_materials[matIndex]->AddResourceReference(); + m_materials[index]->AddResourceReference(); } void NzModel::SetMaterial(unsigned int skinIndex, unsigned int matIndex, NzMaterial* material) @@ -319,6 +479,7 @@ void NzModel::SetMesh(NzMesh* mesh, const NzModelParameters& modelParameters) } } + m_boundingBoxUpdated = false; m_matCount = mesh->GetMaterialCount(); m_materials.resize(m_matCount, NzMaterial::GetDefault()); if (modelParameters.loadMaterials) @@ -345,20 +506,6 @@ void NzModel::SetMesh(NzMesh* mesh, const NzModelParameters& modelParameters) } } -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; -} - bool NzModel::SetSequence(const NzString& sequenceName) { ///TODO: Rendre cette erreur "safe" avec le nouveau système de gestions d'erreur (No-log) @@ -406,36 +553,70 @@ void NzModel::SetSequence(unsigned int sequenceIndex) m_nextFrame = m_currentSequence->firstFrame; } -void NzModel::Update(float elapsedTime) +void NzModel::SetSkin(unsigned int skin) { - if (!m_animation) - return; - - m_interpolation += m_currentSequence->frameRate * elapsedTime; - while (m_interpolation > 1.f) + #if NAZARA_3D_SAFE + if (skin >= m_skinCount) { - m_interpolation -= 1.f; + NazaraError("Skin index out of range (" + NzString::Number(skin) + " >= " + NzString::Number(m_skinCount)); + return; + } + #endif - 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; - } - } + 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; +} + +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_currentFrame = m_nextFrame; - m_nextFrame++; - } + m_boundingBox.Set(m_mesh->GetAABB()); } - m_animation->AnimateSkeleton(&m_skeleton, m_currentFrame, m_nextFrame, m_interpolation); + if (!m_transformMatrixUpdated) + UpdateTransformMatrix(); + + m_boundingBox.Update(m_transformMatrix); + m_boundingBoxUpdated = true; } diff --git a/src/Nazara/3D/RenderQueue.cpp b/src/Nazara/3D/RenderQueue.cpp new file mode 100644 index 000000000..46117a4ab --- /dev/null +++ b/src/Nazara/3D/RenderQueue.cpp @@ -0,0 +1,95 @@ +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - 3D Module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include + +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(); + visibleLights.clear(); + visibleSkeletalModels.clear(); + visibleStaticModels.clear(); +} diff --git a/src/Nazara/3D/Scene.cpp b/src/Nazara/3D/Scene.cpp new file mode 100644 index 000000000..219805c89 --- /dev/null +++ b/src/Nazara/3D/Scene.cpp @@ -0,0 +1,407 @@ +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - 3D Module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; + }; + + void RecursiveFrustumCull(NzRenderQueue& renderQueue, const NzFrustumf& frustum, NzSceneNode* node) + { + for (NzNode* child : node->GetChilds()) + { + if (child->GetNodeType() == nzNodeType_Scene) + { + NzSceneNode* sceneNode = static_cast(child); + ///TODO: Empêcher le rendu des enfants si le parent est cullé selon un flag + if (sceneNode->IsVisible(frustum)) + sceneNode->AddToRenderQueue(renderQueue); + + RecursiveFrustumCull(renderQueue, frustum, sceneNode); + } + } + } +} + +struct NzSceneImpl +{ + NzSceneImpl(NzScene* scene) : + root(scene) + { + } + + std::set updateList; + std::vector instancingData; + NzClock updateClock; + NzColor ambientColor = NzColor(25,25,25); + NzRenderQueue renderQueue; + NzSceneRoot root; + const NzCamera* activeCamera; + NzVertexBuffer* skinningBuffer; + bool update = true; + float frameTime; + float updateTime; + unsigned int updatePerSecond = 60; +}; + +NzScene::NzScene() +{ + m_impl = new NzSceneImpl(this); + 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(child)->SetScene(nullptr); + } + + delete m_impl->skinningBuffer; + delete m_impl; +} + +void NzScene::Cull() +{ + m_impl->renderQueue.Clear(); + + // Frustum culling + RecursiveFrustumCull(m_impl->renderQueue, m_impl->activeCamera->GetFrustum(), &m_impl->root); +} + +void NzScene::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 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 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), 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 draw; + std::function 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) + { + 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::FillInstancingBuffer(&m_impl->instancingData[0], count); + instancedDraw(count, primitiveType, 0, indexCount); + + count = 0; + } + } + + if (count > 0) + { + NzRenderer::FillInstancingBuffer(&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& 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(); +} + +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.insert(object); +} + +void NzScene::SetAmbientColor(const NzColor& color) +{ + m_impl->ambientColor = color; +} + +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 + + m_impl->updateList.erase(object); +} + +void NzScene::Update() +{ + if (m_impl->updatePerSecond == 0 || m_impl->updateClock.GetMilliseconds() > 1000/m_impl->updatePerSecond) + { + 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 (NzSceneNode* node : m_impl->visibleNodes) + { + if (node->ShouldUpdateWhenVisible()) + node->Update(); + } + }*/ +} + +NzScene::operator const NzSceneNode&() const +{ + return m_impl->root; +} + +void NzScene::SetActiveCamera(const NzCamera* camera) +{ + m_impl->activeCamera = camera; +} diff --git a/src/Nazara/3D/SceneNode.cpp b/src/Nazara/3D/SceneNode.cpp index fb48e863d..3deb1db87 100644 --- a/src/Nazara/3D/SceneNode.cpp +++ b/src/Nazara/3D/SceneNode.cpp @@ -3,6 +3,75 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #include +NzSceneNode::NzSceneNode() : +m_scene(nullptr) +{ +} + +NzSceneNode::NzSceneNode(const NzSceneNode& node) : +NzNode(node), +m_scene(node.m_scene) +{ +} + NzSceneNode::~NzSceneNode() = default; + +nzNodeType NzSceneNode::GetNodeType() const +{ + return nzNodeType_Scene; +} + +NzScene* NzSceneNode::GetScene() const +{ + return m_scene; +} + +void NzSceneNode::OnParenting(const NzNode* parent) +{ + if (parent) + { + if (parent->GetNodeType() == nzNodeType_Scene) + SetScene(static_cast(parent)->m_scene); + } + else + SetScene(nullptr); +} + +void NzSceneNode::Register() +{ +} + +void NzSceneNode::SetScene(NzScene* scene) +{ + if (m_scene != scene) + { + if (m_scene) + Unregister(); + + m_scene = scene; + if (m_scene) + Register(); + + for (NzNode* child : m_childs) + { + if (child->GetNodeType() == nzNodeType_Scene) + static_cast(child)->SetScene(scene); + } + } +} + +bool NzSceneNode::ShouldUpdateWhenVisible() +{ + return false; +} + +void NzSceneNode::Unregister() +{ +} + +void NzSceneNode::Update() +{ +} diff --git a/src/Nazara/3D/SceneRoot.cpp b/src/Nazara/3D/SceneRoot.cpp new file mode 100644 index 000000000..4661222ab --- /dev/null +++ b/src/Nazara/3D/SceneRoot.cpp @@ -0,0 +1,49 @@ +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - 3D Module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include + +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; +} + +bool NzSceneRoot::IsVisible(const NzFrustumf& frustum) const +{ + NazaraUnused(frustum); + + return true; // Toujours visible +} + +void NzSceneRoot::Register() +{ + NazaraInternalError("SceneNode::Register() called on SceneRoot"); +} + +void NzSceneRoot::Unregister() +{ + NazaraInternalError("SceneNode::Unregister() called on SceneRoot"); +}