diff --git a/examples/FirstScene/main.cpp b/examples/FirstScene/main.cpp index e0bf7680f..4a8ad90e5 100644 --- a/examples/FirstScene/main.cpp +++ b/examples/FirstScene/main.cpp @@ -149,6 +149,9 @@ int main() // La distance entre l'oeil et le plan rapproché (0 est une valeur interdite car la division par zéro l'est également) camera.SetZNear(0.1f); + // On indique à la scène que le viewer (Le point de vue) sera la caméra + scene.SetViewer(&camera); + // Attention que le ratio entre les deux (zFar/zNear) doit rester raisonnable, dans le cas contraire vous risquez un phénomène // de "Z-Fighting" (Impossibilité de déduire quelle surface devrait apparaître en premier) sur les surfaces éloignées. @@ -326,14 +329,10 @@ int main() updateClock.Restart(); } - // Rendu de la scène - - // On active la caméra (Qui s'occupera de préparer la fenêtre au rendu) - camera.Activate(); - + // Rendu de la scène: // On procède maintenant au rendu de la scène en elle-même, celui-ci se décompose en quatre étapes distinctes - // Pour commencer, on mets à jour la scène, ceci appelle la méthode Update de tous les SceneNode enregistrés + // Pour commencer, on met à jour la scène, ceci appelle la méthode Update de tous les SceneNode enregistrés // pour la mise à jour globale (Scene::RegisterForUpdate) scene.Update(); diff --git a/include/Nazara/Graphics.hpp b/include/Nazara/Graphics.hpp index 41e78ba9e..0b57acf76 100644 --- a/include/Nazara/Graphics.hpp +++ b/include/Nazara/Graphics.hpp @@ -1,4 +1,4 @@ -// This file was automatically generated on 09 Jun 2013 at 11:23:10 +// This file was automatically generated on 21 Aug 2013 at 19:43:23 /* Nazara Engine - Graphics module @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -41,11 +42,16 @@ #include #include #include +#include #include #include +#include #include #include +#include #include +#include #include +#include #endif // NAZARA_GLOBAL_GRAPHICS_HPP diff --git a/include/Nazara/Graphics/AbstractRenderQueue.hpp b/include/Nazara/Graphics/AbstractRenderQueue.hpp index ba3037a30..23f1066e4 100644 --- a/include/Nazara/Graphics/AbstractRenderQueue.hpp +++ b/include/Nazara/Graphics/AbstractRenderQueue.hpp @@ -13,6 +13,7 @@ class NzDrawable; class NzLight; class NzModel; +class NzSprite; class NAZARA_API NzAbstractRenderQueue : NzNonCopyable { @@ -23,6 +24,7 @@ class NAZARA_API NzAbstractRenderQueue : NzNonCopyable virtual void AddDrawable(const NzDrawable* drawable) = 0; virtual void AddLight(const NzLight* light) = 0; virtual void AddModel(const NzModel* model) = 0; + virtual void AddSprite(const NzSprite* sprite) = 0; virtual void Clear(bool fully) = 0; }; diff --git a/include/Nazara/Graphics/AbstractViewer.hpp b/include/Nazara/Graphics/AbstractViewer.hpp new file mode 100644 index 000000000..34fec453e --- /dev/null +++ b/include/Nazara/Graphics/AbstractViewer.hpp @@ -0,0 +1,38 @@ +// 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_ABSTRACTVIEWER_HPP +#define NAZARA_ABSTRACTVIEWER_HPP + +#include +#include +#include +#include +#include + +class NzRenderTarget; +class NzScene; + +class NAZARA_API NzAbstractViewer +{ + public: + NzAbstractViewer() = default; + virtual ~NzAbstractViewer(); + + virtual void ApplyView() const = 0; + + virtual float GetAspectRatio() const = 0; + virtual NzVector3f GetEyePosition() const = 0; + virtual const NzFrustumf& GetFrustum() const = 0; + virtual const NzMatrix4f& GetProjectionMatrix() const = 0; + virtual const NzRenderTarget* GetTarget() const = 0; + virtual const NzMatrix4f& GetViewMatrix() const = 0; + virtual const NzRectui& GetViewport() const = 0; + virtual float GetZFar() const = 0; + virtual float GetZNear() const = 0; +}; + +#endif // NAZARA_ABSTRACTVIEWER_HPP diff --git a/include/Nazara/Graphics/Camera.hpp b/include/Nazara/Graphics/Camera.hpp index d85da5b71..06553fd02 100644 --- a/include/Nazara/Graphics/Camera.hpp +++ b/include/Nazara/Graphics/Camera.hpp @@ -8,6 +8,7 @@ #define NAZARA_CAMERA_HPP #include +#include #include #include #include @@ -15,24 +16,22 @@ #include #include -class NAZARA_API NzCamera : public NzSceneNode, NzRenderTarget::Listener +class NAZARA_API NzCamera : public NzAbstractViewer, public NzNode, NzRenderTarget::Listener { public: NzCamera(); ~NzCamera(); - void Activate(); - void EnsureFrustumUpdate() const; void EnsureProjectionMatrixUpdate() const; void EnsureViewMatrixUpdate() const; + void EnsureViewportUpdate() const; float GetAspectRatio() const; - const NzBoundingVolumef& GetBoundingVolume() const override; + NzVector3f GetEyePosition() const; float GetFOV() const; const NzFrustumf& GetFrustum() const; const NzMatrix4f& GetProjectionMatrix() const; - nzSceneNodeType GetSceneNodeType() const override; const NzRenderTarget* GetTarget() const; const NzRectf& GetTargetRegion() const; const NzMatrix4f& GetViewMatrix() const; @@ -49,22 +48,17 @@ class NAZARA_API NzCamera : public NzSceneNode, NzRenderTarget::Listener void SetZNear(float zNear); private: - void AddToRenderQueue(NzAbstractRenderQueue* renderQueue) const override; - void Invalidate(); + void ApplyView() const override; + void Invalidate() override; void OnRenderTargetReleased(const NzRenderTarget* renderTarget, void* userdata) override; bool OnRenderTargetSizeChange(const NzRenderTarget* renderTarget, void* userdata) override; - void Register() override; - void Unregister() override; - void UpdateFrustum() const; void UpdateProjectionMatrix() const; void UpdateViewMatrix() const; void UpdateViewport() const; - bool VisibilityTest(const NzCamera* camera) override; - mutable NzFrustumf m_frustum; mutable NzMatrix4f m_projectionMatrix; mutable NzMatrix4f m_viewMatrix; diff --git a/include/Nazara/Graphics/Enums.hpp b/include/Nazara/Graphics/Enums.hpp index 74718e800..fadcb7949 100644 --- a/include/Nazara/Graphics/Enums.hpp +++ b/include/Nazara/Graphics/Enums.hpp @@ -28,10 +28,10 @@ enum nzLightType enum nzSceneNodeType { - nzSceneNodeType_Camera, // NzCamera nzSceneNodeType_Light, // NzLight nzSceneNodeType_Model, // NzModel nzSceneNodeType_Root, // NzSceneRoot + nzSceneNodeType_Sprite, // NzSprite nzSceneNodeType_User, nzSceneNodeType_Max = nzSceneNodeType_User diff --git a/include/Nazara/Graphics/ForwardRenderQueue.hpp b/include/Nazara/Graphics/ForwardRenderQueue.hpp index 17f9a3cf0..8e51121d9 100644 --- a/include/Nazara/Graphics/ForwardRenderQueue.hpp +++ b/include/Nazara/Graphics/ForwardRenderQueue.hpp @@ -32,6 +32,7 @@ class NAZARA_API NzForwardRenderQueue : public NzAbstractRenderQueue, NzResource void AddDrawable(const NzDrawable* drawable); void AddLight(const NzLight* light); void AddModel(const NzModel* model); + void AddSprite(const NzSprite* sprite); void Clear(bool fully); @@ -40,32 +41,17 @@ class NAZARA_API NzForwardRenderQueue : public NzAbstractRenderQueue, NzResource private: bool OnResourceDestroy(const NzResource* resource, int index) override; - struct ModelMaterialComparator - { - bool operator()(const NzMaterial* mat1, const NzMaterial* mat2); - }; - struct SkeletalData { ///TODO NzMatrix4f transformMatrix; }; - struct SkeletalMeshComparator - { - bool operator()(const NzSkeletalMesh* subMesh1, const NzSkeletalMesh* subMesh2); - }; - struct StaticData { NzMatrix4f transformMatrix; }; - struct StaticMeshComparator - { - bool operator()(const NzStaticMesh* subMesh1, const NzStaticMesh* subMesh2); - }; - struct TransparentModel { NzMatrix4f transformMatrix; @@ -83,17 +69,42 @@ class NAZARA_API NzForwardRenderQueue : public NzAbstractRenderQueue, NzResource NzStaticMesh* mesh; }; - typedef std::map, SkeletalMeshComparator> SkeletalMeshContainer; - typedef std::map>, StaticMeshComparator> StaticMeshContainer; - typedef std::map, ModelMaterialComparator> MeshContainer; - MeshContainer opaqueModels; - std::vector> transparentsModels; + struct BatchedModelMaterialComparator + { + bool operator()(const NzMaterial* mat1, const NzMaterial* mat2); + }; + + struct BatchedSpriteMaterialComparator + { + bool operator()(const NzMaterial* mat1, const NzMaterial* mat2); + }; + + struct BatchedSkeletalMeshComparator + { + bool operator()(const NzSkeletalMesh* subMesh1, const NzSkeletalMesh* subMesh2); + }; + + struct BatchedStaticMeshComparator + { + bool operator()(const NzStaticMesh* subMesh1, const NzStaticMesh* subMesh2); + }; + + typedef std::map, BatchedSkeletalMeshComparator> BatchedSkeletalMeshContainer; + typedef std::map>, BatchedStaticMeshComparator> BatchedStaticMeshContainer; + typedef std::map, BatchedModelMaterialComparator> BatchedModelContainer; + typedef std::map> BatchedSpriteContainer; + typedef std::vector LightContainer; + typedef std::vector> TransparentModelContainer; + + BatchedModelContainer opaqueModels; + BatchedSpriteContainer sprites; + TransparentModelContainer transparentsModels; std::vector transparentSkeletalModels; std::vector transparentStaticModels; std::vector otherDrawables; - std::vector directionnalLights; - std::vector lights; + LightContainer directionnalLights; + LightContainer lights; }; #endif // NAZARA_FORWARDRENDERQUEUE_HPP diff --git a/include/Nazara/Graphics/ForwardRenderTechnique.hpp b/include/Nazara/Graphics/ForwardRenderTechnique.hpp index 385b5f7e2..a67c12dbf 100644 --- a/include/Nazara/Graphics/ForwardRenderTechnique.hpp +++ b/include/Nazara/Graphics/ForwardRenderTechnique.hpp @@ -10,12 +10,17 @@ #include #include #include +#include +#include +#include + +class NzLightManager; class NAZARA_API NzForwardRenderTechnique : public NzAbstractRenderTechnique { public: NzForwardRenderTechnique(); - ~NzForwardRenderTechnique() = default; + ~NzForwardRenderTechnique(); void Clear(const NzScene* scene); void Draw(const NzScene* scene); @@ -26,7 +31,15 @@ class NAZARA_API NzForwardRenderTechnique : public NzAbstractRenderTechnique void SetMaxLightsPerObject(unsigned int lightCount); private: + void DrawOpaqueModels(const NzScene* scene, NzForwardRenderQueue::BatchedModelContainer& opaqueModels); + void DrawSprites(const NzScene* scene, NzForwardRenderQueue::BatchedSpriteContainer& sprites); + void DrawTransparentModels(const NzScene* scene, NzForwardRenderQueue::TransparentModelContainer& transparentModels); + NzForwardRenderQueue m_renderQueue; + NzIndexBufferRef m_indexBuffer; + NzLightManager m_directionnalLights; + NzLightManager m_lights; + NzVertexBuffer m_spriteBuffer; unsigned int m_maxLightsPerObject; }; diff --git a/include/Nazara/Graphics/Light.hpp b/include/Nazara/Graphics/Light.hpp index b82000aa7..f95ce1e69 100644 --- a/include/Nazara/Graphics/Light.hpp +++ b/include/Nazara/Graphics/Light.hpp @@ -49,11 +49,11 @@ class NAZARA_API NzLight : public NzSceneNode static void Disable(const NzShaderProgram* program, unsigned int lightUnit); private: - void Invalidate(); - void Register(); - void Unregister(); + bool FrustumCull(const NzFrustumf& frustum) override; + void Invalidate() override; + void Register() override; + void Unregister() override; void UpdateBoundingVolume() const; - bool VisibilityTest(const NzCamera* camera) override; nzLightType m_type; mutable NzBoundingVolumef m_boundingVolume; diff --git a/include/Nazara/Graphics/LightManager.hpp b/include/Nazara/Graphics/LightManager.hpp new file mode 100644 index 000000000..6af38c448 --- /dev/null +++ b/include/Nazara/Graphics/LightManager.hpp @@ -0,0 +1,48 @@ +// 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_LIGHTMANAGER_HPP +#define NAZARA_LIGHTMANAGER_HPP + +#include +#include + +class NzLight; + +class NAZARA_API NzLightManager +{ + public: + NzLightManager(); + NzLightManager(const NzLight** lights, unsigned int lightCount); + ~NzLightManager() = default; + + void AddLights(const NzLight** lights, unsigned int lightCount); + + void Clear(); + + unsigned int ComputeClosestLights(const NzVector3f& position, float squaredRadius, unsigned int maxResults); + + const NzLight* GetLight(unsigned int index) const; + unsigned int GetLightCount() const; + const NzLight* GetResult(unsigned int i) const; + + bool IsEmpty() const; + + void SetLights(const NzLight** lights, unsigned int lightCount); + + private: + struct Light + { + const NzLight* light; + unsigned int score; + }; + + std::vector> m_lights; + std::vector m_results; + unsigned int m_lightCount; +}; + +#endif // NAZARA_LIGHTMANAGER_HPP diff --git a/include/Nazara/Graphics/Model.hpp b/include/Nazara/Graphics/Model.hpp index 74ef71edd..6f738757b 100644 --- a/include/Nazara/Graphics/Model.hpp +++ b/include/Nazara/Graphics/Model.hpp @@ -88,12 +88,12 @@ class NAZARA_API NzModel : public NzSceneNode, public NzUpdatable NzModel& operator=(NzModel&& node); private: + bool FrustumCull(const NzFrustumf& frustum) override; void Invalidate() override; void Register() override; void Unregister() override; void Update() override; void UpdateBoundingVolume() const; - bool VisibilityTest(const NzCamera* camera) override; std::vector m_materials; NzAnimationRef m_animation; diff --git a/include/Nazara/Graphics/Scene.hpp b/include/Nazara/Graphics/Scene.hpp index 147450f8f..d59d0d4e4 100644 --- a/include/Nazara/Graphics/Scene.hpp +++ b/include/Nazara/Graphics/Scene.hpp @@ -15,6 +15,7 @@ #include class NzAbstractRenderQueue; +class NzAbstractViewer; class NzCamera; class NzLight; class NzModel; @@ -36,11 +37,11 @@ class NAZARA_API NzScene void Cull(); void Draw(); - NzCamera* GetActiveCamera() const; NzColor GetAmbientColor() const; NzAbstractBackground* GetBackground() const; NzAbstractRenderTechnique* GetRenderTechnique() const; NzSceneNode& GetRoot() const; + NzAbstractViewer* GetViewer() const; float GetUpdateTime() const; unsigned int GetUpdatePerSecond() const; @@ -49,6 +50,7 @@ class NAZARA_API NzScene void SetAmbientColor(const NzColor& color); void SetBackground(NzAbstractBackground* background); void SetRenderTechnique(NzAbstractRenderTechnique* renderTechnique); + void SetViewer(NzAbstractViewer* viewer); void SetUpdatePerSecond(unsigned int updatePerSecond); void UnregisterForUpdate(NzUpdatable* object); @@ -59,8 +61,7 @@ class NAZARA_API NzScene operator const NzSceneNode&() const; private: - void RecursiveCameraCull(NzAbstractRenderQueue* renderQueue, const NzCamera* camera, NzNode* node); - void SetActiveCamera(NzCamera* camera); + void RecursiveFrustumCull(NzAbstractRenderQueue* renderQueue, const NzFrustumf& frustum, NzNode* node); NzSceneImpl* m_impl; }; diff --git a/include/Nazara/Graphics/SceneLayer.hpp b/include/Nazara/Graphics/SceneLayer.hpp new file mode 100644 index 000000000..6af4fc251 --- /dev/null +++ b/include/Nazara/Graphics/SceneLayer.hpp @@ -0,0 +1,34 @@ +// 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_SCENELAYER_HPP +#define NAZARA_SCENELAYER_HPP + +#include + +class NzAbstractRenderTechnique; +class NzAbstractViewer; + +class NAZARA_API NzSceneLayer +{ + public: + NzSceneLayer(); + ~NzSceneLayer(); + + void Draw(); + + nzUInt32 GetBufferClearFlags() const; + NzAbstractRenderQueue* GetRenderQueue() const; + NzAbstractRenderTechnique* GetRenderTechnique() const; + NzScene* GetScene() const; + NzAbstractViewer* GetViewer() const; + + void SetBufferClearFlags(nzUInt32 flags); + void SetRenderTechnique(NzAbstractRenderTechnique* renderTechnique); + void SetViewer(NzAbstractViewer* viewer); +}; + +#endif // NAZARA_SCENELAYER_HPP diff --git a/include/Nazara/Graphics/SceneNode.hpp b/include/Nazara/Graphics/SceneNode.hpp index b45d6cc53..7c4ac54c7 100644 --- a/include/Nazara/Graphics/SceneNode.hpp +++ b/include/Nazara/Graphics/SceneNode.hpp @@ -35,18 +35,18 @@ class NAZARA_API NzSceneNode : public NzNode protected: virtual void OnParenting(const NzNode* parent) override; virtual void OnVisibilityChange(bool visibility); + virtual bool FrustumCull(const NzFrustumf& frustum) = 0; void RecursiveSetScene(NzScene* scene, NzNode* node); virtual void Register(); void SetScene(NzScene* scene); virtual void Unregister(); virtual void Update(); - virtual bool VisibilityTest(const NzCamera* camera) = 0; NzScene* m_scene; bool m_visible; private: - void UpdateVisibility(const NzCamera* camera); + void UpdateVisibility(const NzFrustumf& frustum); }; #endif // NAZARA_SCENENODE_HPP diff --git a/include/Nazara/Graphics/SceneRoot.hpp b/include/Nazara/Graphics/SceneRoot.hpp index a01fd5216..12a7415a2 100644 --- a/include/Nazara/Graphics/SceneRoot.hpp +++ b/include/Nazara/Graphics/SceneRoot.hpp @@ -10,8 +10,6 @@ #include #include -struct NzSceneImpl; - class NAZARA_API NzSceneRoot : public NzSceneNode { friend struct NzSceneImpl; @@ -26,9 +24,9 @@ class NAZARA_API NzSceneRoot : public NzSceneNode NzSceneRoot(NzScene* scene); virtual ~NzSceneRoot(); + bool FrustumCull(const NzFrustumf& frustum) override; void Register(); void Unregister(); - bool VisibilityTest(const NzCamera* camera) override; }; #endif // NAZARA_SCENEROOT_HPP diff --git a/include/Nazara/Graphics/ScreenNode.hpp b/include/Nazara/Graphics/ScreenNode.hpp new file mode 100644 index 000000000..f8fbf8dcc --- /dev/null +++ b/include/Nazara/Graphics/ScreenNode.hpp @@ -0,0 +1,52 @@ +// 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_SCENENODE_HPP +#define NAZARA_SCENENODE_HPP + +#include +#include +#include +#include +#include +#include + +class NAZARA_API NzSceneNode : public NzNode +{ + friend class NzScene; + + public: + NzSceneNode(); + NzSceneNode(const NzSceneNode& node); + virtual ~NzSceneNode(); + + virtual void AddToRenderQueue(NzAbstractRenderQueue* renderQueue) const = 0; + + virtual const NzBoundingVolumef& GetBoundingVolume() const = 0; + nzNodeType GetNodeType() const final; + NzScene* GetScene() const; + virtual nzSceneNodeType GetSceneNodeType() const = 0; + + bool IsVisible() const; + + protected: + virtual void OnParenting(const NzNode* parent) override; + virtual void OnVisibilityChange(bool visibility); + void RecursiveSetScene(NzScene* scene, NzNode* node); + virtual void Register(); + void SetScene(NzScene* scene); + virtual void Unregister(); + virtual void Update(); + virtual bool VisibilityTest(const NzCamera* camera) = 0; + + NzScene* m_scene; + bool m_visible; + + private: + void UpdateVisibility(const NzCamera& camera); +}; + +#endif // NAZARA_SCENENODE_HPP diff --git a/include/Nazara/Graphics/Sprite.hpp b/include/Nazara/Graphics/Sprite.hpp new file mode 100644 index 000000000..8acc1e513 --- /dev/null +++ b/include/Nazara/Graphics/Sprite.hpp @@ -0,0 +1,45 @@ +// 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_SPRITE_HPP +#define NAZARA_SPRITE_HPP + +#include +#include +#include + +class NAZARA_API NzSprite : public NzSceneNode +{ + public: + NzSprite(); + NzSprite(const NzSprite& sprite); + NzSprite(NzSprite&& sprite); + ~NzSprite(); + + void AddToRenderQueue(NzAbstractRenderQueue* renderQueue) const override; + + const NzBoundingVolumef& GetBoundingVolume() const override; + NzMaterial* GetMaterial() const; + nzSceneNodeType GetSceneNodeType() const override; + const NzVector2f& GetSize() const; + const NzRectf& GetTextureCoords() const; + + void SetMaterial(NzMaterial* material); + void SetSize(const NzVector2f& size); + void SetTextureCoords(const NzRectf& coords); + void SetTextureRect(const NzRectui& rect); + + private: + bool FrustumCull(const NzFrustumf& frustum) override; + void Register() override; + void Unregister() override; + + NzRectf m_textureCoords; + NzVector2f m_size; + NzMaterialRef m_material; +}; + +#endif // NAZARA_SPRITE_HPP diff --git a/include/Nazara/Graphics/View.hpp b/include/Nazara/Graphics/View.hpp new file mode 100644 index 000000000..bfd618fc9 --- /dev/null +++ b/include/Nazara/Graphics/View.hpp @@ -0,0 +1,74 @@ +// 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_VIEW_HPP +#define NAZARA_VIEW_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +class NAZARA_API NzView : public NzAbstractViewer, public NzNode, NzRenderTarget::Listener +{ + public: + NzView(); + ~NzView(); + + void EnsureFrustumUpdate() const; + void EnsureProjectionMatrixUpdate() const; + void EnsureViewMatrixUpdate() const; + void EnsureViewportUpdate() const; + + float GetAspectRatio() const; + NzVector3f GetEyePosition() const; + const NzFrustumf& GetFrustum() const; + const NzMatrix4f& GetProjectionMatrix() const; + const NzRenderTarget* GetTarget() const; + const NzRectf& GetTargetRegion() const; + const NzMatrix4f& GetViewMatrix() const; + const NzRectui& GetViewport() const; + float GetZFar() const; + float GetZNear() const; + + void SetTarget(const NzRenderTarget* renderTarget); + void SetTarget(const NzRenderTarget& renderTarget); + void SetTargetRegion(const NzRectf& region); + void SetViewport(const NzRectui& viewport); + void SetZFar(float zFar); + void SetZNear(float zNear); + + private: + void ApplyView() const override; + void Invalidate() override; + + void OnRenderTargetReleased(const NzRenderTarget* renderTarget, void* userdata) override; + bool OnRenderTargetSizeChange(const NzRenderTarget* renderTarget, void* userdata) override; + + void UpdateFrustum() const; + void UpdateProjectionMatrix() const; + void UpdateViewMatrix() const; + void UpdateViewport() const; + + mutable NzFrustumf m_frustum; + mutable NzMatrix4f m_projectionMatrix; + mutable NzMatrix4f m_viewMatrix; + NzRectf m_targetRegion; + mutable NzRectui m_viewport; + const NzRenderTarget* m_target; + mutable bool m_frustumUpdated; + mutable bool m_projectionMatrixUpdated; + mutable bool m_viewMatrixUpdated; + mutable bool m_viewportUpdated; + float m_zFar; + float m_zNear; +}; + +#endif // NAZARA_VIEW_HPP diff --git a/include/Nazara/Renderer.hpp b/include/Nazara/Renderer.hpp index 2c9cb8354..76f7bbf81 100644 --- a/include/Nazara/Renderer.hpp +++ b/include/Nazara/Renderer.hpp @@ -1,4 +1,4 @@ -// This file was automatically generated on 13 Mar 2013 at 23:20:31 +// This file was automatically generated on 21 Aug 2013 at 19:43:23 /* Nazara Engine - Renderer module @@ -38,12 +38,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include diff --git a/include/Nazara/Renderer/Enums.hpp b/include/Nazara/Renderer/Enums.hpp index 95df7b547..2e38c68df 100644 --- a/include/Nazara/Renderer/Enums.hpp +++ b/include/Nazara/Renderer/Enums.hpp @@ -179,13 +179,14 @@ enum nzShaderTarget nzShaderTarget_FullscreenQuad, nzShaderTarget_Model, nzShaderTarget_None, + nzShaderTarget_Sprite, - nzShaderTarget_Max = nzShaderTarget_None + nzShaderTarget_Max = nzShaderTarget_Sprite }; enum nzShaderUniform { - nzShaderUniform_CameraPosition, + nzShaderUniform_EyePosition, nzShaderUniform_InvTargetSize, nzShaderUniform_MaterialAlphaMap, nzShaderUniform_MaterialAlphaThreshold, diff --git a/include/Nazara/Renderer/RenderWindow.hpp b/include/Nazara/Renderer/RenderWindow.hpp index b1068d6c3..9eca87021 100644 --- a/include/Nazara/Renderer/RenderWindow.hpp +++ b/include/Nazara/Renderer/RenderWindow.hpp @@ -58,6 +58,7 @@ class NAZARA_API NzRenderWindow : public NzRenderTarget, public NzWindow private: bool OnWindowCreated() override; void OnWindowDestroy() override; + void OnWindowResized() override; NzClock m_clock; NzContextParameters m_parameters; diff --git a/include/Nazara/Renderer/ShaderProgramManagerParams.hpp b/include/Nazara/Renderer/ShaderProgramManagerParams.hpp index e0b0214e8..a3ca2d4df 100644 --- a/include/Nazara/Renderer/ShaderProgramManagerParams.hpp +++ b/include/Nazara/Renderer/ShaderProgramManagerParams.hpp @@ -30,6 +30,13 @@ struct NzShaderProgramManagerParams bool specularMapping; }; + struct Sprite + { + bool alphaMapping; + bool alphaTest; + bool diffuseMapping; + }; + nzShaderTarget target; nzUInt32 flags; @@ -37,6 +44,7 @@ struct NzShaderProgramManagerParams { FullscreenQuad fullscreenQuad; Model model; + Sprite sprite; }; }; diff --git a/src/Nazara/Graphics/AbstractViewer.cpp b/src/Nazara/Graphics/AbstractViewer.cpp new file mode 100644 index 000000000..97d3c79e5 --- /dev/null +++ b/src/Nazara/Graphics/AbstractViewer.cpp @@ -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 +#include + +NzAbstractViewer::~NzAbstractViewer() = default; diff --git a/src/Nazara/Graphics/Camera.cpp b/src/Nazara/Graphics/Camera.cpp index 259762129..554f30117 100644 --- a/src/Nazara/Graphics/Camera.cpp +++ b/src/Nazara/Graphics/Camera.cpp @@ -9,7 +9,7 @@ #include NzCamera::NzCamera() : -m_viewport(0.f, 0.f, 1.f, 1.f), +m_targetRegion(0.f, 0.f, 1.f, 1.f), m_target(nullptr), m_frustumUpdated(false), m_projectionMatrixUpdated(false), @@ -28,26 +28,6 @@ NzCamera::~NzCamera() m_target->RemoveListener(this); } -void NzCamera::Activate() -{ - #ifdef NAZARA_GRAPHICS_SAFE - if (!m_target) - { - NazaraError("Camera has no render target"); - return; - } - #endif - - if (!m_viewportUpdated) - UpdateViewport(); - - NzRenderer::SetTarget(m_target); - NzRenderer::SetViewport(m_viewport); - - if (m_scene) - m_scene->SetActiveCamera(this); -} - void NzCamera::EnsureFrustumUpdate() const { if (!m_frustumUpdated) @@ -66,16 +46,20 @@ void NzCamera::EnsureViewMatrixUpdate() const UpdateViewMatrix(); } +void NzCamera::EnsureViewportUpdate() const +{ + if (!m_viewportUpdated) + UpdateViewport(); +} + float NzCamera::GetAspectRatio() const { return m_aspectRatio; } -const NzBoundingVolumef& NzCamera::GetBoundingVolume() const +NzVector3f NzCamera::GetEyePosition() const { - ///TODO: Remplacer par le bounding volume du Frustum ? - static NzBoundingVolumef dummy(nzExtend_Null); - return dummy; + return GetPosition(nzCoordSys_Global); } float NzCamera::GetFOV() const @@ -99,11 +83,6 @@ const NzMatrix4f& NzCamera::GetProjectionMatrix() const return m_projectionMatrix; } -nzSceneNodeType NzCamera::GetSceneNodeType() const -{ - return nzSceneNodeType_Camera; -} - const NzRenderTarget* NzCamera::GetTarget() const { return m_target; @@ -174,6 +153,9 @@ void NzCamera::SetTarget(const NzRenderTarget& renderTarget) void NzCamera::SetTargetRegion(const NzRectf& region) { m_targetRegion = region; + + m_frustumUpdated = false; + m_projectionMatrixUpdated = false; m_viewportUpdated = false; } @@ -210,16 +192,33 @@ void NzCamera::SetZNear(float zNear) m_projectionMatrixUpdated = false; } -void NzCamera::AddToRenderQueue(NzAbstractRenderQueue* renderQueue) const +void NzCamera::ApplyView() const { - NazaraUnused(renderQueue); + #if NAZARA_GRAPHICS_SAFE + if (!m_target) + { + NazaraError("Camera has no render target"); + return; + } + #endif - NazaraInternalError("SceneNode::AddToRenderQueue() called on Camera"); + if (!m_projectionMatrixUpdated) + UpdateProjectionMatrix(); + + if (!m_viewMatrixUpdated) + UpdateViewMatrix(); + + if (!m_viewportUpdated) + UpdateViewport(); + + NzRenderer::SetMatrix(nzMatrixType_Projection, m_projectionMatrix); + NzRenderer::SetMatrix(nzMatrixType_View, m_viewMatrix); + NzRenderer::SetViewport(m_viewport); } void NzCamera::Invalidate() { - NzSceneNode::Invalidate(); + NzNode::Invalidate(); // Le frustum et la view matrix dépendent des paramètres du node, invalidons-les m_frustumUpdated = false; @@ -241,21 +240,17 @@ bool NzCamera::OnRenderTargetSizeChange(const NzRenderTarget* renderTarget, void NazaraUnused(userdata); if (renderTarget == m_target) + { + m_frustumUpdated = false; + m_projectionMatrixUpdated = false; m_viewportUpdated = false; + } else NazaraInternalError("Not listening to " + NzString::Pointer(renderTarget)); return true; } -void NzCamera::Register() -{ -} - -void NzCamera::Unregister() -{ -} - void NzCamera::UpdateFrustum() const { if (!m_projectionMatrixUpdated) @@ -270,6 +265,9 @@ void NzCamera::UpdateFrustum() const void NzCamera::UpdateProjectionMatrix() const { + if (!m_viewportUpdated) + UpdateViewport(); // Peut affecter l'aspect ratio + m_projectionMatrix.MakePerspective(m_fov, m_aspectRatio, m_zNear, m_zFar); m_projectionMatrixUpdated = true; } @@ -288,8 +286,8 @@ void NzCamera::UpdateViewport() const 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; + float vWidth = width * m_targetRegion.width; + float vHeight = height * m_targetRegion.height; float aspectRatio = vWidth/vHeight; if (!NzNumberEquals(m_aspectRatio, aspectRatio, 0.001f)) @@ -305,10 +303,3 @@ void NzCamera::UpdateViewport() const m_viewport.height = vHeight; m_viewportUpdated = true; } - -bool NzCamera::VisibilityTest(const NzCamera* camera) -{ - NazaraUnused(camera); - //NazaraInternalError("SceneNode::IsVisible() called on Camera"); - return false; -} diff --git a/src/Nazara/Graphics/ForwardRenderQueue.cpp b/src/Nazara/Graphics/ForwardRenderQueue.cpp index 69b7bb318..03f83aea9 100644 --- a/src/Nazara/Graphics/ForwardRenderQueue.cpp +++ b/src/Nazara/Graphics/ForwardRenderQueue.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -131,13 +132,18 @@ void NzForwardRenderQueue::AddModel(const NzModel* model) } else { - auto pair = opaqueModels.insert(std::make_pair(material, MeshContainer::mapped_type())); + auto pair = opaqueModels.insert(std::make_pair(material, BatchedModelContainer::mapped_type())); if (pair.second) material->AddResourceListener(this, ResourceType_Material); - auto& meshMap = std::get<2>(pair.first->second); + bool& used = std::get<0>(pair.first->second); + bool& enableInstancing = std::get<1>(pair.first->second); - auto pair2 = meshMap.insert(std::make_pair(staticMesh, StaticMeshContainer::mapped_type())); + used = true; + + auto& meshMap = std::get<3>(pair.first->second); + + auto pair2 = meshMap.insert(std::make_pair(staticMesh, BatchedStaticMeshContainer::mapped_type())); if (pair2.second) { staticMesh->AddResourceListener(this, ResourceType_StaticMesh); @@ -153,7 +159,7 @@ void NzForwardRenderQueue::AddModel(const NzModel* model) // As-t-on suffisamment d'instances pour que le coût d'utilisation de l'instancing soit payé ? if (instanceCount >= NAZARA_GRAPHICS_INSTANCING_MIN_INSTANCES_COUNT) - std::get<0>(pair.first->second) = true; // Apparemment oui, activons l'instancing avec ce matériau + enableInstancing = true; // Apparemment oui, activons l'instancing avec ce matériau staticDataContainer.resize(instanceCount); StaticData& data = staticDataContainer.back(); @@ -166,6 +172,19 @@ void NzForwardRenderQueue::AddModel(const NzModel* model) } } +void NzForwardRenderQueue::AddSprite(const NzSprite* sprite) +{ + #if NAZARA_GRAPHICS_SAFE + if (!sprite) + { + NazaraError("Invalid sprite"); + return; + } + #endif + + sprites[sprite->GetMaterial()].push_back(sprite); +} + void NzForwardRenderQueue::Clear(bool fully) { directionnalLights.clear(); @@ -176,7 +195,10 @@ void NzForwardRenderQueue::Clear(bool fully) transparentStaticModels.clear(); if (fully) + { opaqueModels.clear(); + sprites.clear(); + } } void NzForwardRenderQueue::Sort(const NzCamera& camera) @@ -219,7 +241,7 @@ bool NzForwardRenderQueue::OnResourceDestroy(const NzResource* resource, int ind case ResourceType_SkeletalMesh: { for (auto& pair : opaqueModels) - std::get<1>(pair.second).erase(static_cast(resource)); + std::get<2>(pair.second).erase(static_cast(resource)); break; } @@ -227,7 +249,7 @@ bool NzForwardRenderQueue::OnResourceDestroy(const NzResource* resource, int ind case ResourceType_StaticMesh: { for (auto& pair : opaqueModels) - std::get<2>(pair.second).erase(static_cast(resource)); + std::get<3>(pair.second).erase(static_cast(resource)); break; } @@ -236,7 +258,7 @@ bool NzForwardRenderQueue::OnResourceDestroy(const NzResource* resource, int ind return false; // Nous ne voulons plus recevoir d'évènement de cette ressource } -bool NzForwardRenderQueue::ModelMaterialComparator::operator()(const NzMaterial* mat1, const NzMaterial* mat2) +bool NzForwardRenderQueue::BatchedModelMaterialComparator::operator()(const NzMaterial* mat1, const NzMaterial* mat2) { for (unsigned int i = 0; i <= nzShaderFlags_Max; ++i) { @@ -255,7 +277,26 @@ bool NzForwardRenderQueue::ModelMaterialComparator::operator()(const NzMaterial* return mat1 < mat2; } -bool NzForwardRenderQueue::SkeletalMeshComparator::operator()(const NzSkeletalMesh* subMesh1, const NzSkeletalMesh* subMesh2) +bool NzForwardRenderQueue::BatchedSpriteMaterialComparator::operator()(const NzMaterial* mat1, const NzMaterial* mat2) +{ + for (unsigned int i = 0; i <= nzShaderFlags_Max; ++i) + { + const NzShaderProgram* program1 = mat1->GetShaderProgram(nzShaderTarget_Sprite, i); + const NzShaderProgram* program2 = mat2->GetShaderProgram(nzShaderTarget_Sprite, i); + + if (program1 != program2) + return program1 < program2; + } + + const NzTexture* diffuseMap1 = mat1->GetDiffuseMap(); + const NzTexture* diffuseMap2 = mat2->GetDiffuseMap(); + if (diffuseMap1 != diffuseMap2) + return diffuseMap1 < diffuseMap2; + + return mat1 < mat2; +} + +bool NzForwardRenderQueue::BatchedSkeletalMeshComparator::operator()(const NzSkeletalMesh* subMesh1, const NzSkeletalMesh* subMesh2) { const NzIndexBuffer* iBuffer1 = subMesh1->GetIndexBuffer(); const NzBuffer* buffer1 = (iBuffer1) ? iBuffer1->GetBuffer() : nullptr; @@ -269,7 +310,7 @@ bool NzForwardRenderQueue::SkeletalMeshComparator::operator()(const NzSkeletalMe return buffer2 < buffer2; } -bool NzForwardRenderQueue::StaticMeshComparator::operator()(const NzStaticMesh* subMesh1, const NzStaticMesh* subMesh2) +bool NzForwardRenderQueue::BatchedStaticMeshComparator::operator()(const NzStaticMesh* subMesh1, const NzStaticMesh* subMesh2) { const NzIndexBuffer* iBuffer1 = subMesh1->GetIndexBuffer(); const NzBuffer* buffer1 = (iBuffer1) ? iBuffer1->GetBuffer() : nullptr; diff --git a/src/Nazara/Graphics/ForwardRenderTechnique.cpp b/src/Nazara/Graphics/ForwardRenderTechnique.cpp index a465f25f4..82809bb38 100644 --- a/src/Nazara/Graphics/ForwardRenderTechnique.cpp +++ b/src/Nazara/Graphics/ForwardRenderTechnique.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -19,106 +20,47 @@ namespace { - class LightManager + static NzIndexBuffer* s_indexBuffer = nullptr; + unsigned int maxLightCount = 3; ///TODO: Constante sur le nombre maximum de lumières + unsigned int s_maxSprites = 8192; + + NzIndexBuffer* BuildIndexBuffer() { - public: - LightManager() = default; - ~LightManager() = default; + std::unique_ptr indexBuffer(new NzIndexBuffer(false, s_maxSprites*6, nzBufferStorage_Hardware, nzBufferUsage_Static)); + indexBuffer->SetPersistent(false); - unsigned int FindClosestLights(const NzLight** lights, unsigned int lightCount, const NzVector3f& position, float squaredRadius) - { - for (Light& light : m_lights) - { - light.light = nullptr; - light.score = std::numeric_limits::max(); // Nous jouons au Golf - } + NzBufferMapper mapper(indexBuffer.get(), nzBufferAccess_WriteOnly); + nzUInt16* indices = static_cast(mapper.GetPointer()); - for (unsigned int i = 0; i < lightCount; ++i) - { - const NzLight* light = *lights; + for (unsigned int i = 0; i < s_maxSprites; ++i) + { + *indices++ = i*4 + 0; + *indices++ = i*4 + 2; + *indices++ = i*4 + 1; - unsigned int score = std::numeric_limits::max(); - switch (light->GetLightType()) - { - case nzLightType_Directional: - score = 0; // Lumière choisie d'office - break; + *indices++ = i*4 + 2; + *indices++ = i*4 + 3; + *indices++ = i*4 + 1; + } - case nzLightType_Point: - { - float lightRadius = light->GetRadius(); - - float squaredDistance = position.SquaredDistance(light->GetPosition()); - if (squaredDistance - squaredRadius <= lightRadius*lightRadius) - score = static_cast(squaredDistance*1000.f); - - break; - } - - case nzLightType_Spot: - { - float lightRadius = light->GetRadius(); - - ///TODO: Attribuer bonus/malus selon l'angle du spot ? - float squaredDistance = position.SquaredDistance(light->GetPosition()); - if (squaredDistance - squaredRadius <= lightRadius*lightRadius) - score = static_cast(squaredDistance*1000.f); - - break; - } - } - - if (score < m_lights[0].score) - { - unsigned int j; - for (j = 1; j < 3; ++j) ///TODO: Constante - { - if (score > m_lights[j].score) - break; - } - - j--; // Position de la nouvelle lumière - - // Décalage - std::memcpy(&m_lights[0], &m_lights[1], j*sizeof(Light)); - - m_lights[j].light = light; - m_lights[j].score = score; - } - - lights++; - } - - unsigned int i; - for (i = 0; i < 3; ++i) ///TODO: Constante - { - if (m_lights[i].light) - break; - } - - return 3-i; ///TODO: Constante - } - - const NzLight* GetLight(unsigned int i) const - { - ///TODO: Constante - return m_lights[3-i-1].light; // Les lumières sont stockées dans l'ordre inverse (De la plus éloignée à la plus proche) - } - - private: - struct Light - { - const NzLight* light; - unsigned int score; - }; - - Light m_lights[3]; ///TODO: Constante - }; + return indexBuffer.release(); + } } NzForwardRenderTechnique::NzForwardRenderTechnique() : -m_maxLightsPerObject(3) // Valeur totalement arbitraire +m_spriteBuffer(NzVertexDeclaration::Get(nzVertexLayout_XYZ_UV), s_maxSprites*4, nzBufferStorage_Hardware, nzBufferUsage_Dynamic), +m_maxLightsPerObject(maxLightCount) { + if (!s_indexBuffer) + s_indexBuffer = BuildIndexBuffer(); + + m_indexBuffer = s_indexBuffer; +} + +NzForwardRenderTechnique::~NzForwardRenderTechnique() +{ + if (m_indexBuffer.Reset()) + s_indexBuffer = nullptr; } void NzForwardRenderTechnique::Clear(const NzScene* scene) @@ -134,247 +76,22 @@ void NzForwardRenderTechnique::Clear(const NzScene* scene) void NzForwardRenderTechnique::Draw(const NzScene* scene) { - ///TODO: Regrouper les activations par méthode - LightManager lightManager; + // Rendu en projection perspective (3D) + m_directionnalLights.SetLights(&m_renderQueue.directionnalLights[0], m_renderQueue.directionnalLights.size()); + m_lights.SetLights(&m_renderQueue.lights[0], m_renderQueue.lights.size()); - const NzCamera* camera = scene->GetActiveCamera(); - const NzShaderProgram* lastProgram = nullptr; + if (!m_renderQueue.opaqueModels.empty()) + DrawOpaqueModels(scene, m_renderQueue.opaqueModels); - NzRenderer::SetMatrix(nzMatrixType_Projection, camera->GetProjectionMatrix()); - NzRenderer::SetMatrix(nzMatrixType_View, camera->GetViewMatrix()); + if (!m_renderQueue.sprites.empty()) + DrawSprites(scene, m_renderQueue.sprites); - // Rendu des modèles opaques - for (auto& matIt : m_renderQueue.opaqueModels) - { - NzForwardRenderQueue::SkeletalMeshContainer& skeletalContainer = std::get<1>(matIt.second); - NzForwardRenderQueue::StaticMeshContainer& staticContainer = std::get<2>(matIt.second); + if (!m_renderQueue.transparentsModels.empty()) + DrawTransparentModels(scene, m_renderQueue.transparentsModels); - if (!skeletalContainer.empty() || !staticContainer.empty()) - { - const NzMaterial* material = matIt.first; - - // La RenderQueue active ou non l'instancing selon le nombre d'instances - bool renderQueueInstancing = std::get<0>(matIt.second); - - // Nous utilisons de l'instancing que lorsqu'aucune lumière (autre que directionnelle) n'est active - // Ceci car l'instancing n'est pas compatible avec la recherche des lumières les plus proches - // (Le deferred shading n'a pas ce problème) - bool instancing = m_instancingEnabled && m_renderQueue.lights.empty() && renderQueueInstancing; - - // On commence par récupérer le programme du matériau - const NzShaderProgram* program = material->GetShaderProgram(nzShaderTarget_Model, (instancing) ? nzShaderFlags_Instancing : 0); - - unsigned int lightCount = 0; - - // Les uniformes sont conservées au sein d'un programme, inutile de les renvoyer tant qu'il ne change pas - if (program != lastProgram) - { - NzRenderer::SetShaderProgram(program); - - // Couleur ambiante de la scène - program->SendColor(program->GetUniformLocation(nzShaderUniform_SceneAmbient), scene->GetAmbientColor()); - // Position de la caméra - program->SendVector(program->GetUniformLocation(nzShaderUniform_CameraPosition), camera->GetPosition()); - - // On envoie les lumières directionnelles s'il y a (Les mêmes pour tous) - lightCount = m_renderQueue.directionnalLights.size(); - for (unsigned int i = 0; i < lightCount; ++i) - m_renderQueue.directionnalLights[i]->Enable(program, i); - - lastProgram = program; - } - - material->Apply(program); - - // Meshs squelettiques - /*if (!skeletalContainer.empty()) - { - NzRenderer::SetVertexBuffer(m_skinningBuffer); // Vertex buffer commun - for (auto& subMeshIt : container) - { - ///TODO - } - }*/ - - // Meshs statiques - for (auto& subMeshIt : staticContainer) - { - const NzSpheref& boundingSphere = subMeshIt.second.first; - const NzStaticMesh* mesh = subMeshIt.first; - std::vector& staticData = subMeshIt.second.second; - - if (!staticData.empty()) - { - const NzIndexBuffer* indexBuffer = mesh->GetIndexBuffer(); - const NzVertexBuffer* vertexBuffer = mesh->GetVertexBuffer(); - - // Gestion du draw call avant la boucle de rendu - std::function drawFunc; - std::function instancedDrawFunc; - unsigned int indexCount; - - if (indexBuffer) - { - drawFunc = NzRenderer::DrawIndexedPrimitives; - indexCount = indexBuffer->GetIndexCount(); - instancedDrawFunc = NzRenderer::DrawIndexedPrimitivesInstanced; - } - else - { - drawFunc = NzRenderer::DrawPrimitives; - indexCount = vertexBuffer->GetVertexCount(); - instancedDrawFunc = NzRenderer::DrawPrimitivesInstanced; - } - - NzRenderer::SetIndexBuffer(indexBuffer); - NzRenderer::SetVertexBuffer(vertexBuffer); - - nzPrimitiveMode primitiveMode = mesh->GetPrimitiveMode(); - if (instancing) - { - NzVertexBuffer* instanceBuffer = NzRenderer::GetInstanceBuffer(); - - instanceBuffer->SetVertexDeclaration(NzVertexDeclaration::Get(nzVertexLayout_Matrix4)); - - unsigned int stride = instanceBuffer->GetStride(); - - const NzForwardRenderQueue::StaticData* data = &staticData[0]; - unsigned int instanceCount = staticData.size(); - unsigned int maxInstanceCount = instanceBuffer->GetVertexCount(); - - while (instanceCount > 0) - { - unsigned int renderedInstanceCount = std::min(instanceCount, maxInstanceCount); - instanceCount -= renderedInstanceCount; - - NzBufferMapper mapper(instanceBuffer, nzBufferAccess_DiscardAndWrite, 0, renderedInstanceCount); - nzUInt8* ptr = reinterpret_cast(mapper.GetPointer()); - - for (unsigned int i = 0; i < renderedInstanceCount; ++i) - { - std::memcpy(ptr, data->transformMatrix, sizeof(float)*16); - - data++; - ptr += stride; - } - - mapper.Unmap(); - - instancedDrawFunc(renderedInstanceCount, primitiveMode, 0, indexCount); - } - } - else - { - for (const NzForwardRenderQueue::StaticData& data : staticData) - { - // Calcul des lumières les plus proches - if (lightCount < m_maxLightsPerObject && !m_renderQueue.lights.empty()) - { - unsigned int count = lightManager.FindClosestLights(&m_renderQueue.lights[0], m_renderQueue.lights.size(), data.transformMatrix.GetTranslation() + boundingSphere.GetPosition(), boundingSphere.radius); - count -= lightCount; - - for (unsigned int i = 0; i < count; ++i) - lightManager.GetLight(i)->Enable(program, lightCount++); - } - - for (unsigned int i = lightCount; i < 3; ++i) ///TODO: Constante sur le nombre maximum de lumières - NzLight::Disable(program, i); - - NzRenderer::SetMatrix(nzMatrixType_World, data.transformMatrix); - drawFunc(primitiveMode, 0, indexCount); - } - } - staticData.clear(); - } - } - } - - // Et on remet à zéro l'instancing - std::get<0>(matIt.second) = false; - } - - for (const std::pair& pair : m_renderQueue.transparentsModels) - { - // Matériau - NzMaterial* material = (pair.second) ? - m_renderQueue.transparentStaticModels[pair.first].material : - m_renderQueue.transparentSkeletalModels[pair.first].material; - - // On commence par récupérer le shader du matériau - const NzShaderProgram* program = material->GetShaderProgram(nzShaderTarget_Model, 0); - - unsigned int lightCount = 0; - - // Les uniformes sont conservées au sein du shader, inutile de les renvoyer tant que le shader reste le même - if (program != lastProgram) - { - NzRenderer::SetShaderProgram(program); - - // Couleur ambiante de la scène - program->SendColor(program->GetUniformLocation(nzShaderUniform_SceneAmbient), scene->GetAmbientColor()); - // Position de la caméra - program->SendVector(program->GetUniformLocation(nzShaderUniform_CameraPosition), camera->GetPosition()); - - // On envoie les lumières directionnelles s'il y a (Les mêmes pour tous) - lightCount = m_renderQueue.directionnalLights.size(); - for (unsigned int i = 0; i < lightCount; ++i) - m_renderQueue.directionnalLights[i]->Enable(program, i); - - lastProgram = program; - } - - material->Apply(program); - - // Mesh - if (pair.second) - { - NzForwardRenderQueue::TransparentStaticModel& staticModel = m_renderQueue.transparentStaticModels[pair.first]; - - const NzMatrix4f& matrix = staticModel.transformMatrix; - NzStaticMesh* mesh = staticModel.mesh; - - const NzIndexBuffer* indexBuffer = mesh->GetIndexBuffer(); - const NzVertexBuffer* vertexBuffer = mesh->GetVertexBuffer(); - - // Gestion du draw call avant la boucle de rendu - std::function drawFunc; - unsigned int indexCount; - - if (indexBuffer) - { - drawFunc = NzRenderer::DrawIndexedPrimitives; - indexCount = indexBuffer->GetIndexCount(); - } - else - { - drawFunc = NzRenderer::DrawPrimitives; - indexCount = vertexBuffer->GetVertexCount(); - } - - NzRenderer::SetIndexBuffer(indexBuffer); - NzRenderer::SetVertexBuffer(vertexBuffer); - - // Calcul des lumières les plus proches - if (lightCount < m_maxLightsPerObject && !m_renderQueue.lights.empty()) - { - unsigned int count = lightManager.FindClosestLights(&m_renderQueue.lights[0], m_renderQueue.lights.size(), matrix.GetTranslation() + staticModel.boundingSphere.GetPosition(), staticModel.boundingSphere.radius); - count -= lightCount; - - for (unsigned int i = 0; i < count; ++i) - lightManager.GetLight(i)->Enable(program, lightCount++); - } - - for (unsigned int i = lightCount; i < 3; ++i) ///TODO: Constante sur le nombre maximum de lumières - NzLight::Disable(program, i); - - NzRenderer::SetMatrix(nzMatrixType_World, matrix); - drawFunc(mesh->GetPrimitiveMode(), 0, indexCount); - } - else - { - ///TODO - } - } + // Les autres drawables (Exemple: Terrain) + for (const NzDrawable* drawable : m_renderQueue.otherDrawables) + drawable->Draw(); // Les billboards /*if (!m_renderQueue.billboards.empty()) @@ -420,10 +137,6 @@ void NzForwardRenderTechnique::Draw(const NzScene* scene) billboards.clear(); } }*/ - - // Les autres drawables (Exemple: Terrain) - for (const NzDrawable* drawable : m_renderQueue.otherDrawables) - drawable->Draw(); } unsigned int NzForwardRenderTechnique::GetMaxLightsPerObject() const @@ -438,5 +151,340 @@ NzAbstractRenderQueue* NzForwardRenderTechnique::GetRenderQueue() void NzForwardRenderTechnique::SetMaxLightsPerObject(unsigned int lightCount) { - m_maxLightsPerObject = lightCount; ///TODO: Vérifier par rapport à la constante + #if NAZARA_GRAPHICS_SAFE + if (lightCount > maxLightCount) + { + NazaraError("Light count is over maximum light count (" + NzString::Number(lightCount) + " > " + NzString::Number(lightCount) + ')'); + return; + } + #endif + + m_maxLightsPerObject = lightCount; +} + +void NzForwardRenderTechnique::DrawOpaqueModels(const NzScene* scene, NzForwardRenderQueue::BatchedModelContainer& opaqueModels) +{ + NzAbstractViewer* viewer = scene->GetViewer(); + const NzShaderProgram* lastProgram = nullptr; + + for (auto& matIt : opaqueModels) + { + bool& used = std::get<0>(matIt.second); + if (used) + { + bool& renderQueueInstancing = std::get<1>(matIt.second); + NzForwardRenderQueue::BatchedSkeletalMeshContainer& skeletalContainer = std::get<2>(matIt.second); + NzForwardRenderQueue::BatchedStaticMeshContainer& staticContainer = std::get<3>(matIt.second); + + if (!skeletalContainer.empty() || !staticContainer.empty()) + { + const NzMaterial* material = matIt.first; + + // Nous utilisons de l'instancing que lorsqu'aucune lumière (autre que directionnelle) n'est active + // Ceci car l'instancing n'est pas compatible avec la recherche des lumières les plus proches + // (Le deferred shading n'a pas ce problème) + bool instancing = m_instancingEnabled && m_lights.IsEmpty() && renderQueueInstancing; + + // On commence par récupérer le programme du matériau + const NzShaderProgram* program = material->GetShaderProgram(nzShaderTarget_Model, (instancing) ? nzShaderFlags_Instancing : 0); + + unsigned int lightCount = 0; + + // Les uniformes sont conservées au sein d'un programme, inutile de les renvoyer tant qu'il ne change pas + if (program != lastProgram) + { + NzRenderer::SetShaderProgram(program); + + // Couleur ambiante de la scène + program->SendColor(program->GetUniformLocation(nzShaderUniform_SceneAmbient), scene->GetAmbientColor()); + // Position de la caméra + program->SendVector(program->GetUniformLocation(nzShaderUniform_EyePosition), viewer->GetEyePosition()); + + // On envoie les lumières directionnelles s'il y a (Les mêmes pour tous) + lightCount = std::min(m_directionnalLights.GetLightCount(), 3U); + for (unsigned int i = 0; i < lightCount; ++i) + m_directionnalLights.GetLight(i)->Enable(program, i); + + lastProgram = program; + } + + material->Apply(program); + + // Meshs squelettiques + /*if (!skeletalContainer.empty()) + { + NzRenderer::SetVertexBuffer(m_skinningBuffer); // Vertex buffer commun + for (auto& subMeshIt : container) + { + ///TODO + } + }*/ + + // Meshs statiques + for (auto& subMeshIt : staticContainer) + { + const NzSpheref& boundingSphere = subMeshIt.second.first; + const NzStaticMesh* mesh = subMeshIt.first; + std::vector& staticData = subMeshIt.second.second; + + if (!staticData.empty()) + { + const NzIndexBuffer* indexBuffer = mesh->GetIndexBuffer(); + const NzVertexBuffer* vertexBuffer = mesh->GetVertexBuffer(); + + // Gestion du draw call avant la boucle de rendu + std::function DrawFunc; + std::function InstancedDrawFunc; + unsigned int indexCount; + + if (indexBuffer) + { + DrawFunc = NzRenderer::DrawIndexedPrimitives; + InstancedDrawFunc = NzRenderer::DrawIndexedPrimitivesInstanced; + indexCount = indexBuffer->GetIndexCount(); + } + else + { + DrawFunc = NzRenderer::DrawPrimitives; + InstancedDrawFunc = NzRenderer::DrawPrimitivesInstanced; + indexCount = vertexBuffer->GetVertexCount(); + } + + NzRenderer::SetIndexBuffer(indexBuffer); + NzRenderer::SetVertexBuffer(vertexBuffer); + + nzPrimitiveMode primitiveMode = mesh->GetPrimitiveMode(); + if (instancing) + { + NzVertexBuffer* instanceBuffer = NzRenderer::GetInstanceBuffer(); + + instanceBuffer->SetVertexDeclaration(NzVertexDeclaration::Get(nzVertexLayout_Matrix4)); + + unsigned int stride = instanceBuffer->GetStride(); + + const NzForwardRenderQueue::StaticData* data = &staticData[0]; + unsigned int instanceCount = staticData.size(); + unsigned int maxInstanceCount = instanceBuffer->GetVertexCount(); + + while (instanceCount > 0) + { + unsigned int renderedInstanceCount = std::min(instanceCount, maxInstanceCount); + instanceCount -= renderedInstanceCount; + + NzBufferMapper mapper(instanceBuffer, nzBufferAccess_DiscardAndWrite, 0, renderedInstanceCount); + nzUInt8* ptr = reinterpret_cast(mapper.GetPointer()); + + for (unsigned int i = 0; i < renderedInstanceCount; ++i) + { + std::memcpy(ptr, data->transformMatrix, sizeof(float)*16); + + data++; + ptr += stride; + } + + mapper.Unmap(); + + InstancedDrawFunc(renderedInstanceCount, primitiveMode, 0, indexCount); + } + } + else + { + for (const NzForwardRenderQueue::StaticData& data : staticData) + { + // Calcul des lumières les plus proches + if (lightCount < m_maxLightsPerObject && !m_lights.IsEmpty()) + { + unsigned int count = m_lights.ComputeClosestLights(data.transformMatrix.GetTranslation() + boundingSphere.GetPosition(), boundingSphere.radius, maxLightCount); + count -= lightCount; + + for (unsigned int i = 0; i < count; ++i) + m_lights.GetResult(i)->Enable(program, lightCount++); + } + + for (unsigned int i = lightCount; i < maxLightCount; ++i) + NzLight::Disable(program, i); + + NzRenderer::SetMatrix(nzMatrixType_World, data.transformMatrix); + DrawFunc(primitiveMode, 0, indexCount); + } + } + staticData.clear(); + } + } + } + + // Et on remet à zéro les données + renderQueueInstancing = false; + used = false; + } + } +} + +void NzForwardRenderTechnique::DrawSprites(const NzScene* scene, NzForwardRenderQueue::BatchedSpriteContainer& sprites) +{ + NzAbstractViewer* viewer = scene->GetViewer(); + const NzShaderProgram* lastProgram = nullptr; + + NzRenderer::SetIndexBuffer(m_indexBuffer); + NzRenderer::SetMatrix(nzMatrixType_World, NzMatrix4f::Identity()); + NzRenderer::SetVertexBuffer(&m_spriteBuffer); + + for (auto& matIt : sprites) + { + const NzMaterial* material = matIt.first; + auto& spriteVector = matIt.second; + + unsigned int spriteCount = spriteVector.size(); + if (spriteCount > 0) + { + // On commence par récupérer le programme du matériau + const NzShaderProgram* program = material->GetShaderProgram(nzShaderTarget_Sprite, 0); + + // Les uniformes sont conservées au sein du shader, inutile de les renvoyer tant que le shader reste le même + if (program != lastProgram) + { + NzRenderer::SetShaderProgram(program); + + // Couleur ambiante de la scène + program->SendColor(program->GetUniformLocation(nzShaderUniform_SceneAmbient), scene->GetAmbientColor()); + // Position de la caméra + program->SendVector(program->GetUniformLocation(nzShaderUniform_EyePosition), viewer->GetEyePosition()); + + lastProgram = program; + } + + material->Apply(program); + + const NzSprite** spritePtr = &spriteVector[0]; + do + { + unsigned int renderedSpriteCount = std::min(spriteCount, 64U); + spriteCount -= renderedSpriteCount; + + NzBufferMapper vertexMapper(m_spriteBuffer, nzBufferAccess_DiscardAndWrite, 0, renderedSpriteCount*4); + NzVertexStruct_XYZ_UV* vertices = reinterpret_cast(vertexMapper.GetPointer()); + + for (unsigned int i = 0; i < renderedSpriteCount; ++i) + { + const NzSprite* sprite = *spritePtr++; + const NzRectf& textureCoords = sprite->GetTextureCoords(); + const NzVector2f& halfSize = sprite->GetSize()*0.5f; + NzVector3f center = sprite->GetPosition(); + NzQuaternionf rotation = sprite->GetRotation(); + + vertices->position = center + rotation * NzVector3f(-halfSize.x, -halfSize.y, 0.f); + vertices->uv.Set(textureCoords.x, textureCoords.y + textureCoords.height); + vertices++; + + vertices->position = center + rotation * NzVector3f(halfSize.x, -halfSize.y, 0.f); + vertices->uv.Set(textureCoords.width, textureCoords.y + textureCoords.height); + vertices++; + + vertices->position = center + rotation * NzVector3f(-halfSize.x, halfSize.y, 0.f); + vertices->uv.Set(textureCoords.x, textureCoords.y); + vertices++; + + vertices->position = center + rotation * NzVector3f(halfSize.x, halfSize.y, 0.f); + vertices->uv.Set(textureCoords.width, textureCoords.y); + vertices++; + } + + vertexMapper.Unmap(); + + NzRenderer::DrawIndexedPrimitives(nzPrimitiveMode_TriangleList, 0, renderedSpriteCount*6); + } + while (spriteCount > 0); + + spriteVector.clear(); + } + } +} + +void NzForwardRenderTechnique::DrawTransparentModels(const NzScene* scene, NzForwardRenderQueue::TransparentModelContainer& transparentModels) +{ + NzAbstractViewer* viewer = scene->GetViewer(); + const NzShaderProgram* lastProgram = nullptr; + + for (const std::pair& pair : transparentModels) + { + // Matériau + NzMaterial* material = (pair.second) ? + m_renderQueue.transparentStaticModels[pair.first].material : + m_renderQueue.transparentSkeletalModels[pair.first].material; + + // On commence par récupérer le shader du matériau + const NzShaderProgram* program = material->GetShaderProgram(nzShaderTarget_Model, 0); + + unsigned int lightCount = 0; + + // Les uniformes sont conservées au sein du shader, inutile de les renvoyer tant que le shader reste le même + if (program != lastProgram) + { + NzRenderer::SetShaderProgram(program); + + // Couleur ambiante de la scène + program->SendColor(program->GetUniformLocation(nzShaderUniform_SceneAmbient), scene->GetAmbientColor()); + // Position de la caméra + program->SendVector(program->GetUniformLocation(nzShaderUniform_EyePosition), viewer->GetEyePosition()); + + // On envoie les lumières directionnelles s'il y a (Les mêmes pour tous) + lightCount = std::min(m_directionnalLights.GetLightCount(), 3U); + for (unsigned int i = 0; i < lightCount; ++i) + m_directionnalLights.GetLight(i)->Enable(program, i); + + lastProgram = program; + } + + material->Apply(program); + + // Mesh + if (pair.second) + { + NzForwardRenderQueue::TransparentStaticModel& staticModel = m_renderQueue.transparentStaticModels[pair.first]; + + const NzMatrix4f& matrix = staticModel.transformMatrix; + NzStaticMesh* mesh = staticModel.mesh; + + const NzIndexBuffer* indexBuffer = mesh->GetIndexBuffer(); + const NzVertexBuffer* vertexBuffer = mesh->GetVertexBuffer(); + + // Gestion du draw call avant la boucle de rendu + std::function DrawFunc; + unsigned int indexCount; + + if (indexBuffer) + { + DrawFunc = NzRenderer::DrawIndexedPrimitives; + indexCount = indexBuffer->GetIndexCount(); + } + else + { + DrawFunc = NzRenderer::DrawPrimitives; + indexCount = vertexBuffer->GetVertexCount(); + } + + NzRenderer::SetIndexBuffer(indexBuffer); + NzRenderer::SetVertexBuffer(vertexBuffer); + + // Calcul des lumières les plus proches + if (lightCount < m_maxLightsPerObject && !m_lights.IsEmpty()) + { + unsigned int count = m_lights.ComputeClosestLights(matrix.GetTranslation() + staticModel.boundingSphere.GetPosition(), staticModel.boundingSphere.radius, maxLightCount); + count -= lightCount; + + for (unsigned int i = 0; i < count; ++i) + m_lights.GetResult(i)->Enable(program, lightCount++); + } + + for (unsigned int i = lightCount; i < maxLightCount; ++i) + NzLight::Disable(program, i); + + NzRenderer::SetMatrix(nzMatrixType_World, matrix); + DrawFunc(mesh->GetPrimitiveMode(), 0, indexCount); + } + else + { + ///TODO + } + } } diff --git a/src/Nazara/Graphics/Light.cpp b/src/Nazara/Graphics/Light.cpp index 0a6a79548..570ccc8eb 100644 --- a/src/Nazara/Graphics/Light.cpp +++ b/src/Nazara/Graphics/Light.cpp @@ -225,6 +225,31 @@ void NzLight::Disable(const NzShaderProgram* program, unsigned int lightUnit) program->SendInteger(program->GetUniformLocation("Lights[" + NzString::Number(lightUnit) + "].type"), -1); } +bool NzLight::FrustumCull(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_boundingVolumeUpdated) + UpdateBoundingVolume(); + + return frustum.Contains(m_boundingVolume); + } + + NazaraError("Invalid light type (0x" + NzString::Number(m_type, 16) + ')'); + return false; +} + void NzLight::Invalidate() { NzSceneNode::Invalidate(); @@ -307,28 +332,3 @@ void NzLight::UpdateBoundingVolume() const m_boundingVolumeUpdated = true; } - -bool NzLight::VisibilityTest(const NzCamera* camera) -{ - 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 camera->GetFrustum().Contains(NzSpheref(m_derivedPosition, m_radius)); - - case nzLightType_Spot: - if (!m_boundingVolumeUpdated) - UpdateBoundingVolume(); - - return camera->GetFrustum().Contains(m_boundingVolume); - } - - NazaraError("Invalid light type (0x" + NzString::Number(m_type, 16) + ')'); - return false; -} diff --git a/src/Nazara/Graphics/LightManager.cpp b/src/Nazara/Graphics/LightManager.cpp new file mode 100644 index 000000000..85eceb6a1 --- /dev/null +++ b/src/Nazara/Graphics/LightManager.cpp @@ -0,0 +1,160 @@ +// 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 +#include +#include + +NzLightManager::NzLightManager() : +m_lightCount(0) +{ +} + +NzLightManager::NzLightManager(const NzLight** lights, unsigned int lightCount) +{ + SetLights(lights, lightCount); +} + +void NzLightManager::AddLights(const NzLight** lights, unsigned int lightCount) +{ + m_lights.push_back(std::make_pair(lights, lightCount)); + m_lightCount += lightCount; +} + +void NzLightManager::Clear() +{ + m_lights.clear(); + m_lightCount = 0; +} + +unsigned int NzLightManager::ComputeClosestLights(const NzVector3f& position, float squaredRadius, unsigned int maxResults) +{ + m_results.resize(maxResults); + for (Light& light : m_results) + { + light.light = nullptr; + light.score = std::numeric_limits::max(); // Nous jouons au Golf + } + + for (unsigned int i = 0; i < m_lights.size(); ++i) + { + const NzLight** lights = m_lights[i].first; + unsigned int lightCount = m_lights[i].second; + + for (unsigned int j = 0; j < lightCount; ++j) + { + const NzLight* light = *lights++; + + unsigned int score = std::numeric_limits::max(); + switch (light->GetLightType()) + { + case nzLightType_Directional: + score = 0; // Lumière choisie d'office + break; + + case nzLightType_Point: + { + float lightRadius = light->GetRadius(); + + float squaredDistance = position.SquaredDistance(light->GetPosition()); + if (squaredDistance - squaredRadius <= lightRadius*lightRadius) + score = static_cast(squaredDistance*1000.f); + + break; + } + + case nzLightType_Spot: + { + float lightRadius = light->GetRadius(); + + ///TODO: Attribuer bonus/malus selon l'angle du spot ? + float squaredDistance = position.SquaredDistance(light->GetPosition()); + if (squaredDistance - squaredRadius <= lightRadius*lightRadius) + score = static_cast(squaredDistance*1000.f); + + break; + } + } + + if (score < m_results[0].score) + { + unsigned int k; + for (k = 1; k < maxResults; ++k) + { + if (score > m_results[k].score) + break; + } + + k--; // Position de la nouvelle lumière + + // Décalage + std::memcpy(&m_results[0], &m_results[1], k*sizeof(Light)); + + m_results[k].light = light; + m_results[k].score = score; + } + } + } + + unsigned int i; + for (i = 0; i < maxResults; ++i) + { + if (m_results[i].light) + break; + } + + return maxResults-i; +} + +const NzLight* NzLightManager::GetLight(unsigned int index) const +{ + #if NAZARA_GRAPHICS_SAFE + if (index >= m_lightCount) + { + NazaraError("Light index out of range (" + NzString::Number(index) + " >= " + NzString::Number(m_lightCount) + ')'); + return nullptr; + } + #endif + + for (unsigned int i = 0; i < m_lights.size(); ++i) + { + unsigned int lightCount = m_lights[i].second; + if (index > lightCount) + index -= lightCount; + else + { + const NzLight** lights = m_lights[i].first; + return lights[i]; + } + } + + #if NAZARA_GRAPHICS_SAFE + NazaraInternalError("Light not found"); + #else + NazaraError("Light not found"); + #endif + + return nullptr; +} + +unsigned int NzLightManager::GetLightCount() const +{ + return m_lightCount; +} + +const NzLight* NzLightManager::GetResult(unsigned int i) const +{ + return m_results[i].light; +} + +bool NzLightManager::IsEmpty() const +{ + return m_lightCount == 0; +} + +void NzLightManager::SetLights(const NzLight** lights, unsigned int lightCount) +{ + Clear(); + AddLights(lights, lightCount); +} diff --git a/src/Nazara/Graphics/Model.cpp b/src/Nazara/Graphics/Model.cpp index 23a777445..c785acff0 100644 --- a/src/Nazara/Graphics/Model.cpp +++ b/src/Nazara/Graphics/Model.cpp @@ -630,6 +630,25 @@ NzModel& NzModel::operator=(NzModel&& node) return *this; } +bool NzModel::FrustumCull(const NzFrustumf& frustum) +{ + #if NAZARA_GRAPHICS_SAFE + if (!IsDrawable()) + { + NazaraError("Model is not drawable"); + return false; + } + #endif + + if (!m_drawEnabled) + return false; + + if (!m_boundingVolumeUpdated) + UpdateBoundingVolume(); + + return frustum.Contains(m_boundingVolume); +} + void NzModel::Invalidate() { NzSceneNode::Invalidate(); @@ -671,23 +690,4 @@ void NzModel::UpdateBoundingVolume() const m_boundingVolumeUpdated = true; } -bool NzModel::VisibilityTest(const NzCamera* camera) -{ - #if NAZARA_GRAPHICS_SAFE - if (!IsDrawable()) - { - NazaraError("Model is not drawable"); - return false; - } - #endif - - if (!m_drawEnabled) - return false; - - if (!m_boundingVolumeUpdated) - UpdateBoundingVolume(); - - return camera->GetFrustum().Contains(m_boundingVolume); -} - NzModelLoader::LoaderList NzModel::s_loaders; diff --git a/src/Nazara/Graphics/Scene.cpp b/src/Nazara/Graphics/Scene.cpp index 0ecd18436..b60e64275 100644 --- a/src/Nazara/Graphics/Scene.cpp +++ b/src/Nazara/Graphics/Scene.cpp @@ -30,7 +30,7 @@ struct NzSceneImpl NzClock updateClock; NzColor ambientColor = NzColor(25,25,25); NzSceneRoot root; - NzCamera* activeCamera; + NzAbstractViewer* viewer; bool update; float frameTime; float updateTime; @@ -64,9 +64,9 @@ void NzScene::AddToVisibilityList(NzUpdatable* object) void NzScene::Cull() { #if NAZARA_GRAPHICS_SAFE - if (!m_impl->activeCamera) + if (!m_impl->viewer) { - NazaraError("No active camera"); + NazaraError("No viewer"); return; } #endif @@ -76,8 +76,8 @@ void NzScene::Cull() m_impl->visibleUpdateList.clear(); - // Frustum culling/Viewport culling - RecursiveCameraCull(m_impl->renderTechnique->GetRenderQueue(), m_impl->activeCamera, &m_impl->root); + // Frustum culling + RecursiveFrustumCull(m_impl->renderTechnique->GetRenderQueue(), m_impl->viewer->GetFrustum(), &m_impl->root); ///TODO: Occlusion culling @@ -87,22 +87,18 @@ void NzScene::Cull() void NzScene::Draw() { #if NAZARA_GRAPHICS_SAFE - if (!m_impl->activeCamera) + if (!m_impl->viewer) { - NazaraError("No active camera"); + NazaraError("No viewer"); return; } #endif m_impl->renderTechnique->Clear(this); + m_impl->viewer->ApplyView(); m_impl->renderTechnique->Draw(this); } -NzCamera* NzScene::GetActiveCamera() const -{ - return m_impl->activeCamera; -} - NzColor NzScene::GetAmbientColor() const { return m_impl->ambientColor; @@ -123,6 +119,11 @@ NzSceneNode& NzScene::GetRoot() const return m_impl->root; } +NzAbstractViewer* NzScene::GetViewer() const +{ + return m_impl->viewer; +} + float NzScene::GetUpdateTime() const { return m_impl->updateTime; @@ -161,6 +162,11 @@ void NzScene::SetRenderTechnique(NzAbstractRenderTechnique* renderTechnique) m_impl->renderTechnique.reset(renderTechnique); } +void NzScene::SetViewer(NzAbstractViewer* viewer) +{ + m_impl->viewer = viewer; +} + void NzScene::SetUpdatePerSecond(unsigned int updatePerSecond) { m_impl->updatePerSecond = updatePerSecond; @@ -209,7 +215,7 @@ NzScene::operator const NzSceneNode&() const return m_impl->root; } -void NzScene::RecursiveCameraCull(NzAbstractRenderQueue* renderQueue, const NzCamera* camera, NzNode* node) +void NzScene::RecursiveFrustumCull(NzAbstractRenderQueue* renderQueue, const NzFrustumf& frustum, NzNode* node) { for (NzNode* child : node->GetChilds()) { @@ -218,17 +224,12 @@ void NzScene::RecursiveCameraCull(NzAbstractRenderQueue* renderQueue, const NzCa NzSceneNode* sceneNode = static_cast(child); ///TODO: Empêcher le rendu des enfants si le parent est cullé selon un flag - sceneNode->UpdateVisibility(camera); + sceneNode->UpdateVisibility(frustum); if (sceneNode->IsVisible()) sceneNode->AddToRenderQueue(renderQueue); } if (child->HasChilds()) - RecursiveCameraCull(renderQueue, camera, child); + RecursiveFrustumCull(renderQueue, frustum, child); } } - -void NzScene::SetActiveCamera(NzCamera* camera) -{ - m_impl->activeCamera = camera; -} diff --git a/src/Nazara/Graphics/SceneNode.cpp b/src/Nazara/Graphics/SceneNode.cpp index c16cef292..d089d95f4 100644 --- a/src/Nazara/Graphics/SceneNode.cpp +++ b/src/Nazara/Graphics/SceneNode.cpp @@ -97,11 +97,11 @@ void NzSceneNode::Update() { } -void NzSceneNode::UpdateVisibility(const NzCamera* camera) +void NzSceneNode::UpdateVisibility(const NzFrustumf& frustum) { bool wasVisible = m_visible; - m_visible = VisibilityTest(camera); + m_visible = FrustumCull(frustum); if (m_visible != wasVisible) OnVisibilityChange(m_visible); diff --git a/src/Nazara/Graphics/SceneRoot.cpp b/src/Nazara/Graphics/SceneRoot.cpp index 0102a5d9f..70504b13e 100644 --- a/src/Nazara/Graphics/SceneRoot.cpp +++ b/src/Nazara/Graphics/SceneRoot.cpp @@ -31,6 +31,13 @@ nzSceneNodeType NzSceneRoot::GetSceneNodeType() const return nzSceneNodeType_Root; } +bool NzSceneRoot::FrustumCull(const NzFrustumf& frustum) +{ + NazaraUnused(frustum); + + return true; // Toujours visible +} + void NzSceneRoot::Register() { NazaraInternalError("SceneNode::Register() called on SceneRoot"); @@ -40,10 +47,3 @@ void NzSceneRoot::Unregister() { NazaraInternalError("SceneNode::Unregister() called on SceneRoot"); } - -bool NzSceneRoot::VisibilityTest(const NzCamera* camera) -{ - NazaraUnused(camera); - - return true; // Toujours visible -} diff --git a/src/Nazara/Graphics/SkyboxBackground.cpp b/src/Nazara/Graphics/SkyboxBackground.cpp index 86e2de4d5..75e15044f 100644 --- a/src/Nazara/Graphics/SkyboxBackground.cpp +++ b/src/Nazara/Graphics/SkyboxBackground.cpp @@ -198,15 +198,15 @@ void NzSkyboxBackground::Draw(const NzScene* scene) const s_program->SendInteger(s_skyboxLocation, 0); - NzCamera* camera = scene->GetActiveCamera(); + NzAbstractViewer* viewer = scene->GetViewer(); - NzMatrix4f skyboxMatrix(camera->GetViewMatrix()); + NzMatrix4f skyboxMatrix(viewer->GetViewMatrix()); skyboxMatrix.SetTranslation(NzVector3f::Zero()); NzRenderer::SetIndexBuffer(m_indexBuffer); - NzRenderer::SetMatrix(nzMatrixType_Projection, camera->GetProjectionMatrix()); + NzRenderer::SetMatrix(nzMatrixType_Projection, viewer->GetProjectionMatrix()); NzRenderer::SetMatrix(nzMatrixType_View, skyboxMatrix); - NzRenderer::SetMatrix(nzMatrixType_World, NzMatrix4f::Scale(NzVector3f(camera->GetZNear()))); + NzRenderer::SetMatrix(nzMatrixType_World, NzMatrix4f::Scale(NzVector3f(viewer->GetZNear()))); NzRenderer::SetRenderStates(states); NzRenderer::SetShaderProgram(s_program); NzRenderer::SetTexture(0, m_texture); diff --git a/src/Nazara/Graphics/Sprite.cpp b/src/Nazara/Graphics/Sprite.cpp new file mode 100644 index 000000000..0357ec81b --- /dev/null +++ b/src/Nazara/Graphics/Sprite.cpp @@ -0,0 +1,114 @@ +// 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 +#include + +NzSprite::NzSprite() : +m_textureCoords(0.f, 0.f, 1.f, 1.f), +m_size(64.f, 64.f) +{ +} + +NzSprite::NzSprite(const NzSprite& sprite) : +NzSceneNode(sprite), +m_textureCoords(sprite.m_textureCoords), +m_size(sprite.m_size), +m_material(sprite.m_material) +{ +} + +NzSprite::NzSprite(NzSprite&& sprite) : +NzSceneNode(sprite), +m_textureCoords(sprite.m_textureCoords), +m_size(sprite.m_size), +m_material(std::move(sprite.m_material)) +{ +} + +NzSprite::~NzSprite() = default; + +void NzSprite::AddToRenderQueue(NzAbstractRenderQueue* renderQueue) const +{ + renderQueue->AddSprite(this); +} + +const NzBoundingVolumef& NzSprite::GetBoundingVolume() const +{ + static NzBoundingVolumef infinity(NzBoundingVolumef::Infinite()); + return infinity; +} + +NzMaterial* NzSprite::GetMaterial() const +{ + return m_material; +} + +nzSceneNodeType NzSprite::GetSceneNodeType() const +{ + return nzSceneNodeType_Sprite; +} + +const NzVector2f& NzSprite::GetSize() const +{ + return m_size; +} + +const NzRectf& NzSprite::GetTextureCoords() const +{ + return m_textureCoords; +} + +void NzSprite::SetMaterial(NzMaterial* material) +{ + m_material = material; +} + +void NzSprite::SetSize(const NzVector2f& size) +{ + m_size = size; +} + +void NzSprite::SetTextureCoords(const NzRectf& coords) +{ + m_textureCoords = coords; +} + +void NzSprite::SetTextureRect(const NzRectui& rect) +{ + #if NAZARA_GRAPHICS_SAFE + if (!m_material) + { + NazaraError("Sprite has no material"); + return; + } + + if (!m_material->HasDiffuseMap()) + { + NazaraError("Sprite material has no diffuse map"); + return; + } + #endif + + NzTexture* diffuseMap = m_material->GetDiffuseMap(); + + float invWidth = 1.f/diffuseMap->GetWidth(); + float invHeight = 1.f/diffuseMap->GetHeight(); + + SetTextureCoords(NzRectf(invWidth*rect.x, invHeight*rect.y, invWidth*rect.width, invHeight*rect.height)); +} + +bool NzSprite::FrustumCull(const NzFrustumf& frustum) +{ + ///TODO: Effectuer un vrai test + return true; +} + +void NzSprite::Register() +{ +} + +void NzSprite::Unregister() +{ +} diff --git a/src/Nazara/Graphics/View.cpp b/src/Nazara/Graphics/View.cpp new file mode 100644 index 000000000..fa55f2baf --- /dev/null +++ b/src/Nazara/Graphics/View.cpp @@ -0,0 +1,279 @@ +// 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 +#include +#include +#include +#include + +NzView::NzView() : +m_targetRegion(0.f, 0.f, 1.f, 1.f), +m_target(nullptr), +m_frustumUpdated(false), +m_projectionMatrixUpdated(false), +m_viewMatrixUpdated(false), +m_viewportUpdated(false), +m_zFar(1.f), +m_zNear(-1.f) +{ +} + +NzView::~NzView() +{ + if (m_target) + m_target->RemoveListener(this); +} + +void NzView::EnsureFrustumUpdate() const +{ + if (!m_frustumUpdated) + UpdateFrustum(); +} + +void NzView::EnsureProjectionMatrixUpdate() const +{ + if (!m_projectionMatrixUpdated) + UpdateProjectionMatrix(); +} + +void NzView::EnsureViewMatrixUpdate() const +{ + if (!m_viewMatrixUpdated) + UpdateViewMatrix(); +} + +void NzView::EnsureViewportUpdate() const +{ + if (!m_viewportUpdated) + UpdateViewport(); +} + +float NzView::GetAspectRatio() const +{ + return 1.f; +} + +NzVector3f NzView::GetEyePosition() const +{ + return GetPosition(nzCoordSys_Global); +} + +const NzFrustumf& NzView::GetFrustum() const +{ + if (!m_frustumUpdated) + UpdateFrustum(); + + return m_frustum; +} + +const NzMatrix4f& NzView::GetProjectionMatrix() const +{ + if (!m_projectionMatrixUpdated) + UpdateProjectionMatrix(); + + return m_projectionMatrix; +} + +const NzRenderTarget* NzView::GetTarget() const +{ + return m_target; +} + +const NzRectf& NzView::GetTargetRegion() const +{ + return m_targetRegion; +} + +const NzMatrix4f& NzView::GetViewMatrix() const +{ + if (!m_viewMatrixUpdated) + UpdateViewMatrix(); + + return m_viewMatrix; +} + +const NzRectui& NzView::GetViewport() const +{ + #if NAZARA_GRAPHICS_SAFE + if (!m_target) + { + NazaraError("Camera has no render target"); + return m_viewport; + } + #endif + + if (!m_viewportUpdated) + UpdateViewport(); + + return m_viewport; +} + +float NzView::GetZFar() const +{ + return m_zFar; +} + +float NzView::GetZNear() const +{ + return m_zNear; +} + +void NzView::SetTarget(const NzRenderTarget* renderTarget) +{ + if (m_target) + m_target->RemoveListener(this); + + m_target = renderTarget; + if (m_target) + m_target->AddListener(this); +} + +void NzView::SetTarget(const NzRenderTarget& renderTarget) +{ + SetTarget(&renderTarget); +} + +void NzView::SetTargetRegion(const NzRectf& region) +{ + m_targetRegion = region; + + m_frustumUpdated = false; + m_projectionMatrixUpdated = false; + m_viewportUpdated = false; +} + +void NzView::SetViewport(const NzRectui& viewport) +{ + #if NAZARA_GRAPHICS_SAFE + if (!m_target) + { + NazaraError("Camera has no render target"); + return; + } + #endif + + // On calcule la région nécessaire pour produire ce viewport avec la taille actuelle de la cible + float invWidth = 1.f/m_target->GetWidth(); + float invHeight = 1.f/m_target->GetHeight(); + + SetTargetRegion(NzRectf(invWidth * viewport.x, invHeight * viewport.y, invWidth * viewport.width, invHeight * viewport.height)); +} + +void NzView::SetZFar(float zFar) +{ + m_zFar = zFar; + + m_frustumUpdated = false; + m_projectionMatrixUpdated = false; +} + +void NzView::SetZNear(float zNear) +{ + m_zNear = zNear; + + m_frustumUpdated = false; + m_projectionMatrixUpdated = false; +} + +void NzView::ApplyView() const +{ + #if NAZARA_GRAPHICS_SAFE + if (!m_target) + { + NazaraError("Camera has no render target"); + return; + } + #endif + + if (!m_projectionMatrixUpdated) + UpdateProjectionMatrix(); + + if (!m_viewMatrixUpdated) + UpdateViewMatrix(); + + if (!m_viewportUpdated) + UpdateViewport(); + + NzRenderer::SetMatrix(nzMatrixType_Projection, m_projectionMatrix); + NzRenderer::SetMatrix(nzMatrixType_View, m_viewMatrix); + NzRenderer::SetViewport(m_viewport); +} + +void NzView::Invalidate() +{ + NzNode::Invalidate(); + + // Le frustum et la view matrix dépendent des paramètres du node, invalidons-les + m_frustumUpdated = false; + m_viewMatrixUpdated = false; +} + +void NzView::OnRenderTargetReleased(const NzRenderTarget* renderTarget, void* userdata) +{ + NazaraUnused(userdata); + + if (renderTarget == m_target) + m_target = nullptr; + else + NazaraInternalError("Not listening to " + NzString::Pointer(renderTarget)); +} + +bool NzView::OnRenderTargetSizeChange(const NzRenderTarget* renderTarget, void* userdata) +{ + NazaraUnused(userdata); + + if (renderTarget == m_target) + { + m_frustumUpdated = false; + m_projectionMatrixUpdated = false; + m_viewportUpdated = false; + } + else + NazaraInternalError("Not listening to " + NzString::Pointer(renderTarget)); + + return true; +} + +void NzView::UpdateFrustum() const +{ + if (!m_projectionMatrixUpdated) + UpdateProjectionMatrix(); + + if (!m_viewMatrixUpdated) + UpdateViewMatrix(); + + m_frustum.Extract(m_viewMatrix, m_projectionMatrix); + m_frustumUpdated = true; +} + +void NzView::UpdateProjectionMatrix() const +{ + if (!m_viewportUpdated) + UpdateViewport(); + + m_projectionMatrix.MakeOrtho(m_viewport.x, m_viewport.x + m_viewport.width, m_viewport.y, m_viewport.y + m_viewport.height, m_zNear, m_zFar); + m_projectionMatrixUpdated = true; +} + +void NzView::UpdateViewMatrix() const +{ + if (!m_derivedUpdated) + UpdateDerived(); + + m_viewMatrix.MakeViewMatrix(m_derivedPosition, m_derivedRotation); + m_viewMatrixUpdated = true; +} + +void NzView::UpdateViewport() const +{ + unsigned int width = m_target->GetWidth(); + unsigned int height = std::max(m_target->GetHeight(), 1U); + + m_viewport.x = width * m_targetRegion.x; + m_viewport.y = height * m_targetRegion.y; + m_viewport.width = width * m_targetRegion.width; + m_viewport.height = height * m_targetRegion.height; + m_viewportUpdated = true; +} diff --git a/src/Nazara/Renderer/GLSLProgram.cpp b/src/Nazara/Renderer/GLSLProgram.cpp index 221c36f48..48f415a38 100644 --- a/src/Nazara/Renderer/GLSLProgram.cpp +++ b/src/Nazara/Renderer/GLSLProgram.cpp @@ -605,7 +605,7 @@ bool NzGLSLProgram::PostLinkage() // Pour éviter de se tromper entre le nom et la constante #define CacheUniform(name) m_uniformLocations[nzShaderUniform_##name] = GetUniformLocation(#name) - CacheUniform(CameraPosition); + CacheUniform(EyePosition); CacheUniform(InvTargetSize); CacheUniform(MaterialAlphaMap); CacheUniform(MaterialAlphaThreshold); diff --git a/src/Nazara/Renderer/Material.cpp b/src/Nazara/Renderer/Material.cpp index b765056c2..77e1b9a11 100644 --- a/src/Nazara/Renderer/Material.cpp +++ b/src/Nazara/Renderer/Material.cpp @@ -163,11 +163,17 @@ void NzMaterial::Enable(nzRendererParameter renderParameter, bool enable) void NzMaterial::EnableAlphaTest(bool alphaTest) { m_alphaTestEnabled = alphaTest; + + InvalidatePrograms(nzShaderTarget_FullscreenQuad); + InvalidatePrograms(nzShaderTarget_Model); + InvalidatePrograms(nzShaderTarget_Sprite); } void NzMaterial::EnableLighting(bool lighting) { m_lightingEnabled = lighting; + + InvalidatePrograms(nzShaderTarget_Model); } NzTexture* NzMaterial::GetAlphaMap() const @@ -410,7 +416,9 @@ void NzMaterial::SetAlphaMap(NzTexture* map) { m_alphaMap = map; + InvalidatePrograms(nzShaderTarget_FullscreenQuad); InvalidatePrograms(nzShaderTarget_Model); + InvalidatePrograms(nzShaderTarget_Sprite); } void NzMaterial::SetAlphaThreshold(float alphaThreshold) @@ -454,7 +462,9 @@ void NzMaterial::SetDiffuseMap(NzTexture* map) { m_diffuseMap = map; + InvalidatePrograms(nzShaderTarget_FullscreenQuad); InvalidatePrograms(nzShaderTarget_Model); + InvalidatePrograms(nzShaderTarget_Sprite); } void NzMaterial::SetDiffuseSampler(const NzTextureSampler& sampler) @@ -706,6 +716,12 @@ void NzMaterial::GenerateProgram(nzShaderTarget target, nzUInt32 flags) const case nzShaderTarget_None: break; + + case nzShaderTarget_Sprite: + params.sprite.alphaMapping = m_alphaMap.IsValid(); + params.sprite.alphaTest = m_alphaTestEnabled; + params.sprite.diffuseMapping = m_diffuseMap.IsValid(); + break; } m_programs[target][flags].program = NzShaderProgramManager::Get(params); diff --git a/src/Nazara/Renderer/RenderWindow.cpp b/src/Nazara/Renderer/RenderWindow.cpp index 37a30a8af..00e9f7fac 100644 --- a/src/Nazara/Renderer/RenderWindow.cpp +++ b/src/Nazara/Renderer/RenderWindow.cpp @@ -310,3 +310,9 @@ void NzRenderWindow::OnWindowDestroy() m_context = nullptr; } } + +void NzRenderWindow::OnWindowResized() +{ + NotifySizeChange(); +} + diff --git a/src/Nazara/Renderer/ShaderProgramManager.cpp b/src/Nazara/Renderer/ShaderProgramManager.cpp index e7ee06d87..42c8a59fd 100644 --- a/src/Nazara/Renderer/ShaderProgramManager.cpp +++ b/src/Nazara/Renderer/ShaderProgramManager.cpp @@ -3,6 +3,9 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include +#include +#include #include #include #include @@ -24,9 +27,9 @@ namespace switch (params.target) { case nzShaderTarget_FullscreenQuad: - h |= (params.fullscreenQuad.alphaMapping << 10) | // 1 bit - (params.fullscreenQuad.alphaTest << 11) | // 1 bit - (params.fullscreenQuad.diffuseMapping << 12); // 1 bit + h |= (params.fullscreenQuad.alphaMapping << 10) | // 1 bit + (params.fullscreenQuad.alphaTest << 11) | // 1 bit + (params.fullscreenQuad.diffuseMapping << 12); // 1 bit break; case nzShaderTarget_Model: @@ -42,6 +45,12 @@ namespace case nzShaderTarget_None: break; + + case nzShaderTarget_Sprite: + h |= (params.sprite.alphaMapping << 10) | // 1 bit + (params.sprite.alphaTest << 11) | // 1 bit + (params.sprite.diffuseMapping << 12); // 1 bit + break; } return h; @@ -65,6 +74,9 @@ namespace case nzShaderTarget_None: return true; + + case nzShaderTarget_Sprite: + return std::memcmp(&first.sprite, &second.sprite, sizeof(NzShaderProgramManagerParams::Sprite)) == 0; } return false; @@ -72,12 +84,15 @@ namespace }; std::unordered_map s_programs; + NzString s_cacheDirectory("shaderCache"); NzString s_inKW; NzString s_outKW; NzString s_fragmentColorKW; NzString s_textureLookupKW; + bool s_cacheEnabled = false; bool s_earlyFragmentTest; bool s_glsl140; + //bool s_loadCachedPrograms = true; unsigned int s_glslVersion; } @@ -86,8 +101,68 @@ const NzShaderProgram* NzShaderProgramManager::Get(const NzShaderProgramManagerP auto it = s_programs.find(params); if (it == s_programs.end()) { - // Alors nous gébérons le programme - NzShaderProgram* program = GenerateProgram(params); + // Alors nous générons le programme + std::unique_ptr program; + + if (s_cacheEnabled) + { + NzString programFileName = NzNumberToString(ParamsHash()(params), 36) + ".nsb"; // Nazara Shader Binary, très original, je sais + NazaraDebug("Checking cache for program file \"" + programFileName + "\"..."); + + NzFile shaderFile(s_cacheDirectory + NAZARA_DIRECTORY_SEPARATOR + programFileName); + if (shaderFile.Open(NzFile::ReadOnly)) + { + NazaraDebug("File found"); + + unsigned int size = shaderFile.GetSize(); + + NzByteArray binary; + binary.Resize(size); + + if (shaderFile.Read(&binary[0], size) != size) + { + NazaraError("Failed to read program binary"); + return false; + } + + shaderFile.Close(); + + program.reset(new NzShaderProgram); + if (!program->LoadFromBinary(binary)) + { + NazaraWarning("Program \"" + programFileName + "\" loading failed, this is mostly due to a driver/video card " + "update or a file corruption, regenerating program..."); + + program.reset(GenerateProgram(params)); + } + } + else + { + if (shaderFile.Exists()) + NazaraWarning("Program file exists but couldn't be opened"); + + program.reset(GenerateProgram(params)); + if (program) + { + if (program->IsBinaryRetrievable()) + { + NazaraDebug("Program \"" + programFileName + "\" (re)generated, saving it into program cache directory..."); + + NzByteArray programBinary = program->GetBinary(); + if (!programBinary.IsEmpty()) + { + if (!shaderFile.Open(NzFile::Truncate | NzFile::WriteOnly) || !shaderFile.Write(programBinary)) + NazaraWarning("Failed to save program binary to file \"" + programFileName + "\": " + NzGetLastSystemError()); + } + else + NazaraWarning("Failed to retrieve shader program binary"); + } + } + } + } + else + program.reset(GenerateProgram(params)); + if (!program) { NazaraWarning("Failed to build program, using default one..."); @@ -96,12 +171,10 @@ const NzShaderProgram* NzShaderProgramManager::Get(const NzShaderProgramManagerP defaultParams.flags = params.flags; defaultParams.target = nzShaderTarget_None; - program = s_programs[defaultParams]; // Shader par défaut + program.reset(s_programs[defaultParams]); // Shader par défaut } - s_programs[params] = program; - - return program; + return program.release(); } else return it->second; @@ -125,13 +198,14 @@ NzString NzShaderProgramManager::BuildFragmentCode(const NzShaderProgramManagerP source += NzString::Number(s_glslVersion); source += "\n\n"; - if (s_earlyFragmentTest) - source += "layout(early_fragment_tests) in;" "\n\n"; - switch (params.target) { case nzShaderTarget_FullscreenQuad: { + // "discard" ne s'entend pas bien avec les early fragment tests + if (s_earlyFragmentTest && !params.fullscreenQuad.alphaMapping) + source += "layout(early_fragment_tests) in;" "\n\n"; + /********************Entrant********************/ if (params.fullscreenQuad.alphaMapping || params.fullscreenQuad.diffuseMapping) source += s_inKW + " vec2 vTexCoord;" "\n\n"; @@ -162,23 +236,17 @@ NzString NzShaderProgramManager::BuildFragmentCode(const NzShaderProgramManagerP if (params.fullscreenQuad.diffuseMapping) source += '*' + s_textureLookupKW + "(MaterialDiffuseMap, vTexCoord)"; - source += ";" "\n" - "\t" "float fragmentAlpha = "; - - if (params.fullscreenQuad.diffuseMapping) - source += "fragmentColor.a"; - else - source += "MaterialDiffuse.a"; + source += ";" "\n"; if (params.fullscreenQuad.alphaMapping) - source += '*' + s_textureLookupKW + "(MaterialAlphaMap, vTexCoord).r"; + source += "fragmentColor.a *= " + s_textureLookupKW + "(MaterialAlphaMap, vTexCoord).r"; source += ";" "\n"; if (params.fullscreenQuad.alphaMapping) { - source += "if (fragmentAlpha < MaterialAlphaThreshold)" "\n" - "\t" "discard;" "\n"; + source += "\t" "if (fragmentColor.a < MaterialAlphaThreshold)" "\n" + "\t\t" "discard;" "\n"; } source += "\t" "RenderTarget0 = fragmentColor;" "\n" @@ -191,6 +259,10 @@ NzString NzShaderProgramManager::BuildFragmentCode(const NzShaderProgramManagerP //bool parallaxMapping = (params.model.lighting && params.model.parallaxMapping); bool uvMapping = (params.model.alphaMapping || params.model.diffuseMapping || params.model.normalMapping || params.model.specularMapping); + // "discard" ne s'entend pas bien avec les early fragment tests + if (s_earlyFragmentTest && !params.model.alphaMapping) + source += "layout(early_fragment_tests) in;" "\n\n"; + if (params.model.lighting) { source += "#define LIGHT_DIRECTIONAL 0" "\n" @@ -235,7 +307,7 @@ NzString NzShaderProgramManager::BuildFragmentCode(const NzShaderProgramManagerP "\t" "vec2 parameters3;" "\n" "};" "\n\n" - "uniform vec3 CameraPosition;" "\n" + "uniform vec3 EyePosition;" "\n" "uniform Light Lights[3];" "\n" "uniform vec4 MaterialAmbient;" "\n"; } @@ -298,8 +370,8 @@ NzString NzShaderProgramManager::BuildFragmentCode(const NzShaderProgramManagerP if (params.model.alphaTest) { - source += "if (diffuseColor.a < MaterialAlphaThreshold)" "\n" - "\t" "discard;" "\n"; + source += "\t" "if (fragmentColor.a < MaterialAlphaThreshold)" "\n" + "\t\t" "discard;" "\n"; } if (params.model.lighting) @@ -318,7 +390,7 @@ NzString NzShaderProgramManager::BuildFragmentCode(const NzShaderProgramManagerP source += "\t" "if (MaterialShininess > 0.0)" "\n" "\t" "{" "\n" - "\t\t" "vec3 eyeVec = normalize(CameraPosition - vWorldPos);" "\n\n" + "\t\t" "vec3 eyeVec = normalize(EyePosition - vWorldPos);" "\n\n" "\t\t" "for (int i = 0; i < 3; ++i)" "\n" "\t\t" "{" "\n"; @@ -551,6 +623,9 @@ NzString NzShaderProgramManager::BuildFragmentCode(const NzShaderProgramManagerP case nzShaderTarget_None: { + if (s_earlyFragmentTest) + source += "layout(early_fragment_tests) in;" "\n\n"; + /********************Sortant********************/ if (s_glsl140) source += "out vec4 RenderTarget0;" "\n\n"; @@ -565,6 +640,60 @@ NzString NzShaderProgramManager::BuildFragmentCode(const NzShaderProgramManagerP source += "}" "\n"; break; } + + case nzShaderTarget_Sprite: + { + // "discard" ne s'entend pas bien avec les early fragment tests + if (s_earlyFragmentTest && !params.sprite.alphaMapping) + source += "layout(early_fragment_tests) in;" "\n\n"; + + /********************Entrant********************/ + if (params.sprite.alphaMapping || params.sprite.diffuseMapping) + source += s_inKW + " vec2 vTexCoord;" "\n\n"; + + /********************Sortant********************/ + if (s_glsl140) + source += "out vec4 RenderTarget0;" "\n\n"; + + /********************Uniformes********************/ + if (params.sprite.alphaMapping) + source += "uniform sampler2D MaterialAlphaMap;" "\n"; + + if (params.sprite.alphaTest) + source += "uniform float MaterialAlphaThreshold;" "\n"; + + source += "uniform vec4 MaterialDiffuse;" "\n"; + + if (params.sprite.diffuseMapping) + source += "uniform sampler2D MaterialDiffuseMap;" "\n"; + + source += '\n'; + + /********************Fonctions********************/ + source += "void main()" "\n" + "{" "\n"; + + source += "\t" "vec4 fragmentColor = MaterialDiffuse"; + if (params.sprite.diffuseMapping) + source += '*' + s_textureLookupKW + "(MaterialDiffuseMap, vTexCoord)"; + + source += ";" "\n"; + + if (params.sprite.alphaMapping) + source += "fragmentColor.a *= " + s_textureLookupKW + "(MaterialAlphaMap, vTexCoord).r"; + + source += ";" "\n"; + + if (params.sprite.alphaMapping) + { + source += "\t" "if (fragmentColor.a < MaterialAlphaThreshold)" "\n" + "\t\t" "discard;" "\n"; + } + + source += "\t" "RenderTarget0 = fragmentColor;" "\n" + "}" "\n"; + break; + } } return source; @@ -754,6 +883,54 @@ NzString NzShaderProgramManager::BuildVertexCode(const NzShaderProgramManagerPar source += "}" "\n"; break; } + + case nzShaderTarget_Sprite: + { + bool uvMapping = (params.fullscreenQuad.alphaMapping || params.fullscreenQuad.diffuseMapping); + + /********************Entrant********************/ + if (params.flags & nzShaderFlags_Instancing) + source += s_inKW + " mat4 InstanceData0;" "\n"; + + source += s_inKW + " vec3 VertexPosition;" "\n"; + + if (uvMapping) + source += s_inKW + " vec2 VertexTexCoord;" "\n"; + + source += '\n'; + + /********************Sortant********************/ + if (uvMapping) + source += s_outKW + " vec2 vTexCoord;" "\n\n"; + + /********************Uniformes********************/ + if (params.flags & nzShaderFlags_Instancing) + source += "uniform mat4 ViewProjMatrix;" "\n"; + else + source += "uniform mat4 WorldViewProjMatrix;" "\n"; + + source += '\n'; + + /********************Code********************/ + source += "void main()" "\n" + "{" "\n"; + + if (params.flags & nzShaderFlags_Instancing) + source += "\t" "gl_Position = ViewProjMatrix * InstanceData0 * vec4(VertexPosition, 1.0);" "\n"; + else + source += "\t" "gl_Position = WorldViewProjMatrix * vec4(VertexPosition, 1.0);" "\n"; + + if (uvMapping) + { + if (params.flags & nzShaderFlags_FlipUVs) + source += "\t" "vTexCoord = vec2(VertexTexCoord.x, 1.0 - VertexTexCoord.y);" "\n"; + else + source += "\t" "vTexCoord = VertexTexCoord;" "\n"; + } + + source += "}" "\n"; + break; + } } return source; @@ -825,6 +1002,54 @@ bool NzShaderProgramManager::Initialize() s_programs[params] = program; } + /*if (s_loadCachedPrograms) + { + NzDirectory cacheDirectory(s_cacheDirectory); + cacheDirectory.SetPattern("*.nsb"); + + if (cacheDirectory.Open()) + { + while (cacheDirectory.NextResult(true)) + { + long long hash; + if (cacheDirectory.GetResultName().SubStringTo(".nsb", -1, true, false).ToInteger(&hash, 32)) + { + std::size_t hashCode = static_cast(hash); + + if (s_programs.find(hashCode) == s_programs.end()) + { + NzFile shaderFile(cacheDirectory.GetResultPath()); + if (shaderFile.Open(NzFile::ReadOnly)) + { + unsigned int size = cacheDirectory.GetResultSize(); + + NzByteArray binary; + binary.Resize(size); + + if (shaderFile.Read(&binary[0], size) != size) + { + NazaraError("Failed to read program binary"); + return false; + } + + shaderFile.Close(); + + std::unique_ptr program(new NzShaderProgram); + if (program->LoadFromBinary(binary)) + s_programs[hashCode] = binary.release(); + else + NazaraWarning("Program binary \"" + cacheDirectory.GetResultName() + "\" loading failed, this is mostly due to a driver/video card " + "update or a file corruption, regenerating program..."); } + } + } + else + NazaraWarning("Failed to parse program file name (" + cacheDirectory.GetResultName() + ')'); + } + } + else if (cacheDirectory.Exists()) + NazaraWarning("Failed to open shader cache directory"); + }*/ + return true; }