Merge branch 'Graphics-Update'

Former-commit-id: 9855f235dd21d47d1cd9da8c3c0b4ede41e74472
This commit is contained in:
Lynix 2013-08-21 20:05:50 +02:00
commit 6556acfa32
39 changed files with 1772 additions and 554 deletions

View File

@ -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) // 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); 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 // 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. // 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(); updateClock.Restart();
} }
// Rendu de la scène // Rendu de la scène:
// On active la caméra (Qui s'occupera de préparer la fenêtre au rendu)
camera.Activate();
// On procède maintenant au rendu de la scène en elle-même, celui-ci se décompose en quatre étapes distinctes // 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) // pour la mise à jour globale (Scene::RegisterForUpdate)
scene.Update(); scene.Update();

View File

@ -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 Nazara Engine - Graphics module
@ -32,6 +32,7 @@
#include <Nazara/Graphics/AbstractBackground.hpp> #include <Nazara/Graphics/AbstractBackground.hpp>
#include <Nazara/Graphics/AbstractRenderQueue.hpp> #include <Nazara/Graphics/AbstractRenderQueue.hpp>
#include <Nazara/Graphics/AbstractRenderTechnique.hpp> #include <Nazara/Graphics/AbstractRenderTechnique.hpp>
#include <Nazara/Graphics/AbstractViewer.hpp>
#include <Nazara/Graphics/Camera.hpp> #include <Nazara/Graphics/Camera.hpp>
#include <Nazara/Graphics/ColorBackground.hpp> #include <Nazara/Graphics/ColorBackground.hpp>
#include <Nazara/Graphics/Config.hpp> #include <Nazara/Graphics/Config.hpp>
@ -41,11 +42,16 @@
#include <Nazara/Graphics/ForwardRenderTechnique.hpp> #include <Nazara/Graphics/ForwardRenderTechnique.hpp>
#include <Nazara/Graphics/Graphics.hpp> #include <Nazara/Graphics/Graphics.hpp>
#include <Nazara/Graphics/Light.hpp> #include <Nazara/Graphics/Light.hpp>
#include <Nazara/Graphics/LightManager.hpp>
#include <Nazara/Graphics/Model.hpp> #include <Nazara/Graphics/Model.hpp>
#include <Nazara/Graphics/Scene.hpp> #include <Nazara/Graphics/Scene.hpp>
#include <Nazara/Graphics/SceneLayer.hpp>
#include <Nazara/Graphics/SceneNode.hpp> #include <Nazara/Graphics/SceneNode.hpp>
#include <Nazara/Graphics/SceneRoot.hpp> #include <Nazara/Graphics/SceneRoot.hpp>
#include <Nazara/Graphics/ScreenNode.hpp>
#include <Nazara/Graphics/SkyboxBackground.hpp> #include <Nazara/Graphics/SkyboxBackground.hpp>
#include <Nazara/Graphics/Sprite.hpp>
#include <Nazara/Graphics/TextureBackground.hpp> #include <Nazara/Graphics/TextureBackground.hpp>
#include <Nazara/Graphics/View.hpp>
#endif // NAZARA_GLOBAL_GRAPHICS_HPP #endif // NAZARA_GLOBAL_GRAPHICS_HPP

View File

@ -13,6 +13,7 @@
class NzDrawable; class NzDrawable;
class NzLight; class NzLight;
class NzModel; class NzModel;
class NzSprite;
class NAZARA_API NzAbstractRenderQueue : NzNonCopyable class NAZARA_API NzAbstractRenderQueue : NzNonCopyable
{ {
@ -23,6 +24,7 @@ class NAZARA_API NzAbstractRenderQueue : NzNonCopyable
virtual void AddDrawable(const NzDrawable* drawable) = 0; virtual void AddDrawable(const NzDrawable* drawable) = 0;
virtual void AddLight(const NzLight* light) = 0; virtual void AddLight(const NzLight* light) = 0;
virtual void AddModel(const NzModel* model) = 0; virtual void AddModel(const NzModel* model) = 0;
virtual void AddSprite(const NzSprite* sprite) = 0;
virtual void Clear(bool fully) = 0; virtual void Clear(bool fully) = 0;
}; };

View File

@ -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 <Nazara/Prerequesites.hpp>
#include <Nazara/Graphics/Enums.hpp>
#include <Nazara/Math/Frustum.hpp>
#include <Nazara/Math/Matrix4.hpp>
#include <Nazara/Math/Rect.hpp>
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

View File

@ -8,6 +8,7 @@
#define NAZARA_CAMERA_HPP #define NAZARA_CAMERA_HPP
#include <Nazara/Prerequesites.hpp> #include <Nazara/Prerequesites.hpp>
#include <Nazara/Graphics/AbstractViewer.hpp>
#include <Nazara/Graphics/SceneNode.hpp> #include <Nazara/Graphics/SceneNode.hpp>
#include <Nazara/Math/Frustum.hpp> #include <Nazara/Math/Frustum.hpp>
#include <Nazara/Math/Matrix4.hpp> #include <Nazara/Math/Matrix4.hpp>
@ -15,24 +16,22 @@
#include <Nazara/Math/Vector3.hpp> #include <Nazara/Math/Vector3.hpp>
#include <Nazara/Renderer/RenderTarget.hpp> #include <Nazara/Renderer/RenderTarget.hpp>
class NAZARA_API NzCamera : public NzSceneNode, NzRenderTarget::Listener class NAZARA_API NzCamera : public NzAbstractViewer, public NzNode, NzRenderTarget::Listener
{ {
public: public:
NzCamera(); NzCamera();
~NzCamera(); ~NzCamera();
void Activate();
void EnsureFrustumUpdate() const; void EnsureFrustumUpdate() const;
void EnsureProjectionMatrixUpdate() const; void EnsureProjectionMatrixUpdate() const;
void EnsureViewMatrixUpdate() const; void EnsureViewMatrixUpdate() const;
void EnsureViewportUpdate() const;
float GetAspectRatio() const; float GetAspectRatio() const;
const NzBoundingVolumef& GetBoundingVolume() const override; NzVector3f GetEyePosition() const;
float GetFOV() const; float GetFOV() const;
const NzFrustumf& GetFrustum() const; const NzFrustumf& GetFrustum() const;
const NzMatrix4f& GetProjectionMatrix() const; const NzMatrix4f& GetProjectionMatrix() const;
nzSceneNodeType GetSceneNodeType() const override;
const NzRenderTarget* GetTarget() const; const NzRenderTarget* GetTarget() const;
const NzRectf& GetTargetRegion() const; const NzRectf& GetTargetRegion() const;
const NzMatrix4f& GetViewMatrix() const; const NzMatrix4f& GetViewMatrix() const;
@ -49,22 +48,17 @@ class NAZARA_API NzCamera : public NzSceneNode, NzRenderTarget::Listener
void SetZNear(float zNear); void SetZNear(float zNear);
private: private:
void AddToRenderQueue(NzAbstractRenderQueue* renderQueue) const override; void ApplyView() const override;
void Invalidate(); void Invalidate() override;
void OnRenderTargetReleased(const NzRenderTarget* renderTarget, void* userdata) override; void OnRenderTargetReleased(const NzRenderTarget* renderTarget, void* userdata) override;
bool OnRenderTargetSizeChange(const NzRenderTarget* renderTarget, void* userdata) override; bool OnRenderTargetSizeChange(const NzRenderTarget* renderTarget, void* userdata) override;
void Register() override;
void Unregister() override;
void UpdateFrustum() const; void UpdateFrustum() const;
void UpdateProjectionMatrix() const; void UpdateProjectionMatrix() const;
void UpdateViewMatrix() const; void UpdateViewMatrix() const;
void UpdateViewport() const; void UpdateViewport() const;
bool VisibilityTest(const NzCamera* camera) override;
mutable NzFrustumf m_frustum; mutable NzFrustumf m_frustum;
mutable NzMatrix4f m_projectionMatrix; mutable NzMatrix4f m_projectionMatrix;
mutable NzMatrix4f m_viewMatrix; mutable NzMatrix4f m_viewMatrix;

View File

@ -28,10 +28,10 @@ enum nzLightType
enum nzSceneNodeType enum nzSceneNodeType
{ {
nzSceneNodeType_Camera, // NzCamera
nzSceneNodeType_Light, // NzLight nzSceneNodeType_Light, // NzLight
nzSceneNodeType_Model, // NzModel nzSceneNodeType_Model, // NzModel
nzSceneNodeType_Root, // NzSceneRoot nzSceneNodeType_Root, // NzSceneRoot
nzSceneNodeType_Sprite, // NzSprite
nzSceneNodeType_User, nzSceneNodeType_User,
nzSceneNodeType_Max = nzSceneNodeType_User nzSceneNodeType_Max = nzSceneNodeType_User

View File

@ -32,6 +32,7 @@ class NAZARA_API NzForwardRenderQueue : public NzAbstractRenderQueue, NzResource
void AddDrawable(const NzDrawable* drawable); void AddDrawable(const NzDrawable* drawable);
void AddLight(const NzLight* light); void AddLight(const NzLight* light);
void AddModel(const NzModel* model); void AddModel(const NzModel* model);
void AddSprite(const NzSprite* sprite);
void Clear(bool fully); void Clear(bool fully);
@ -40,32 +41,17 @@ class NAZARA_API NzForwardRenderQueue : public NzAbstractRenderQueue, NzResource
private: private:
bool OnResourceDestroy(const NzResource* resource, int index) override; bool OnResourceDestroy(const NzResource* resource, int index) override;
struct ModelMaterialComparator
{
bool operator()(const NzMaterial* mat1, const NzMaterial* mat2);
};
struct SkeletalData struct SkeletalData
{ {
///TODO ///TODO
NzMatrix4f transformMatrix; NzMatrix4f transformMatrix;
}; };
struct SkeletalMeshComparator
{
bool operator()(const NzSkeletalMesh* subMesh1, const NzSkeletalMesh* subMesh2);
};
struct StaticData struct StaticData
{ {
NzMatrix4f transformMatrix; NzMatrix4f transformMatrix;
}; };
struct StaticMeshComparator
{
bool operator()(const NzStaticMesh* subMesh1, const NzStaticMesh* subMesh2);
};
struct TransparentModel struct TransparentModel
{ {
NzMatrix4f transformMatrix; NzMatrix4f transformMatrix;
@ -83,17 +69,42 @@ class NAZARA_API NzForwardRenderQueue : public NzAbstractRenderQueue, NzResource
NzStaticMesh* mesh; NzStaticMesh* mesh;
}; };
typedef std::map<const NzSkeletalMesh*, std::vector<SkeletalData>, SkeletalMeshComparator> SkeletalMeshContainer;
typedef std::map<const NzStaticMesh*, std::pair<NzSpheref, std::vector<StaticData>>, StaticMeshComparator> StaticMeshContainer;
typedef std::map<const NzMaterial*, std::tuple<bool, SkeletalMeshContainer, StaticMeshContainer>, ModelMaterialComparator> MeshContainer;
MeshContainer opaqueModels; struct BatchedModelMaterialComparator
std::vector<std::pair<unsigned int, bool>> transparentsModels; {
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<const NzSkeletalMesh*, std::vector<SkeletalData>, BatchedSkeletalMeshComparator> BatchedSkeletalMeshContainer;
typedef std::map<const NzStaticMesh*, std::pair<NzSpheref, std::vector<StaticData>>, BatchedStaticMeshComparator> BatchedStaticMeshContainer;
typedef std::map<const NzMaterial*, std::tuple<bool, bool, BatchedSkeletalMeshContainer, BatchedStaticMeshContainer>, BatchedModelMaterialComparator> BatchedModelContainer;
typedef std::map<const NzMaterial*, std::vector<const NzSprite*>> BatchedSpriteContainer;
typedef std::vector<const NzLight*> LightContainer;
typedef std::vector<std::pair<unsigned int, bool>> TransparentModelContainer;
BatchedModelContainer opaqueModels;
BatchedSpriteContainer sprites;
TransparentModelContainer transparentsModels;
std::vector<TransparentSkeletalModel> transparentSkeletalModels; std::vector<TransparentSkeletalModel> transparentSkeletalModels;
std::vector<TransparentStaticModel> transparentStaticModels; std::vector<TransparentStaticModel> transparentStaticModels;
std::vector<const NzDrawable*> otherDrawables; std::vector<const NzDrawable*> otherDrawables;
std::vector<const NzLight*> directionnalLights; LightContainer directionnalLights;
std::vector<const NzLight*> lights; LightContainer lights;
}; };
#endif // NAZARA_FORWARDRENDERQUEUE_HPP #endif // NAZARA_FORWARDRENDERQUEUE_HPP

View File

@ -10,12 +10,17 @@
#include <Nazara/Prerequesites.hpp> #include <Nazara/Prerequesites.hpp>
#include <Nazara/Graphics/AbstractRenderTechnique.hpp> #include <Nazara/Graphics/AbstractRenderTechnique.hpp>
#include <Nazara/Graphics/ForwardRenderQueue.hpp> #include <Nazara/Graphics/ForwardRenderQueue.hpp>
#include <Nazara/Graphics/LightManager.hpp>
#include <Nazara/Utility/IndexBuffer.hpp>
#include <Nazara/Utility/VertexBuffer.hpp>
class NzLightManager;
class NAZARA_API NzForwardRenderTechnique : public NzAbstractRenderTechnique class NAZARA_API NzForwardRenderTechnique : public NzAbstractRenderTechnique
{ {
public: public:
NzForwardRenderTechnique(); NzForwardRenderTechnique();
~NzForwardRenderTechnique() = default; ~NzForwardRenderTechnique();
void Clear(const NzScene* scene); void Clear(const NzScene* scene);
void Draw(const NzScene* scene); void Draw(const NzScene* scene);
@ -26,7 +31,15 @@ class NAZARA_API NzForwardRenderTechnique : public NzAbstractRenderTechnique
void SetMaxLightsPerObject(unsigned int lightCount); void SetMaxLightsPerObject(unsigned int lightCount);
private: 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; NzForwardRenderQueue m_renderQueue;
NzIndexBufferRef m_indexBuffer;
NzLightManager m_directionnalLights;
NzLightManager m_lights;
NzVertexBuffer m_spriteBuffer;
unsigned int m_maxLightsPerObject; unsigned int m_maxLightsPerObject;
}; };

View File

@ -49,11 +49,11 @@ class NAZARA_API NzLight : public NzSceneNode
static void Disable(const NzShaderProgram* program, unsigned int lightUnit); static void Disable(const NzShaderProgram* program, unsigned int lightUnit);
private: private:
void Invalidate(); bool FrustumCull(const NzFrustumf& frustum) override;
void Register(); void Invalidate() override;
void Unregister(); void Register() override;
void Unregister() override;
void UpdateBoundingVolume() const; void UpdateBoundingVolume() const;
bool VisibilityTest(const NzCamera* camera) override;
nzLightType m_type; nzLightType m_type;
mutable NzBoundingVolumef m_boundingVolume; mutable NzBoundingVolumef m_boundingVolume;

View File

@ -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 <Nazara/Prerequesites.hpp>
#include <Nazara/Math/Vector3.hpp>
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<std::pair<const NzLight**, unsigned int>> m_lights;
std::vector<Light> m_results;
unsigned int m_lightCount;
};
#endif // NAZARA_LIGHTMANAGER_HPP

View File

@ -88,12 +88,12 @@ class NAZARA_API NzModel : public NzSceneNode, public NzUpdatable
NzModel& operator=(NzModel&& node); NzModel& operator=(NzModel&& node);
private: private:
bool FrustumCull(const NzFrustumf& frustum) override;
void Invalidate() override; void Invalidate() override;
void Register() override; void Register() override;
void Unregister() override; void Unregister() override;
void Update() override; void Update() override;
void UpdateBoundingVolume() const; void UpdateBoundingVolume() const;
bool VisibilityTest(const NzCamera* camera) override;
std::vector<NzMaterialRef> m_materials; std::vector<NzMaterialRef> m_materials;
NzAnimationRef m_animation; NzAnimationRef m_animation;

View File

@ -15,6 +15,7 @@
#include <Nazara/Math/Frustum.hpp> #include <Nazara/Math/Frustum.hpp>
class NzAbstractRenderQueue; class NzAbstractRenderQueue;
class NzAbstractViewer;
class NzCamera; class NzCamera;
class NzLight; class NzLight;
class NzModel; class NzModel;
@ -36,11 +37,11 @@ class NAZARA_API NzScene
void Cull(); void Cull();
void Draw(); void Draw();
NzCamera* GetActiveCamera() const;
NzColor GetAmbientColor() const; NzColor GetAmbientColor() const;
NzAbstractBackground* GetBackground() const; NzAbstractBackground* GetBackground() const;
NzAbstractRenderTechnique* GetRenderTechnique() const; NzAbstractRenderTechnique* GetRenderTechnique() const;
NzSceneNode& GetRoot() const; NzSceneNode& GetRoot() const;
NzAbstractViewer* GetViewer() const;
float GetUpdateTime() const; float GetUpdateTime() const;
unsigned int GetUpdatePerSecond() const; unsigned int GetUpdatePerSecond() const;
@ -49,6 +50,7 @@ class NAZARA_API NzScene
void SetAmbientColor(const NzColor& color); void SetAmbientColor(const NzColor& color);
void SetBackground(NzAbstractBackground* background); void SetBackground(NzAbstractBackground* background);
void SetRenderTechnique(NzAbstractRenderTechnique* renderTechnique); void SetRenderTechnique(NzAbstractRenderTechnique* renderTechnique);
void SetViewer(NzAbstractViewer* viewer);
void SetUpdatePerSecond(unsigned int updatePerSecond); void SetUpdatePerSecond(unsigned int updatePerSecond);
void UnregisterForUpdate(NzUpdatable* object); void UnregisterForUpdate(NzUpdatable* object);
@ -59,8 +61,7 @@ class NAZARA_API NzScene
operator const NzSceneNode&() const; operator const NzSceneNode&() const;
private: private:
void RecursiveCameraCull(NzAbstractRenderQueue* renderQueue, const NzCamera* camera, NzNode* node); void RecursiveFrustumCull(NzAbstractRenderQueue* renderQueue, const NzFrustumf& frustum, NzNode* node);
void SetActiveCamera(NzCamera* camera);
NzSceneImpl* m_impl; NzSceneImpl* m_impl;
}; };

View File

@ -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 <Nazara/Prerequesites.hpp>
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

View File

@ -35,18 +35,18 @@ class NAZARA_API NzSceneNode : public NzNode
protected: protected:
virtual void OnParenting(const NzNode* parent) override; virtual void OnParenting(const NzNode* parent) override;
virtual void OnVisibilityChange(bool visibility); virtual void OnVisibilityChange(bool visibility);
virtual bool FrustumCull(const NzFrustumf& frustum) = 0;
void RecursiveSetScene(NzScene* scene, NzNode* node); void RecursiveSetScene(NzScene* scene, NzNode* node);
virtual void Register(); virtual void Register();
void SetScene(NzScene* scene); void SetScene(NzScene* scene);
virtual void Unregister(); virtual void Unregister();
virtual void Update(); virtual void Update();
virtual bool VisibilityTest(const NzCamera* camera) = 0;
NzScene* m_scene; NzScene* m_scene;
bool m_visible; bool m_visible;
private: private:
void UpdateVisibility(const NzCamera* camera); void UpdateVisibility(const NzFrustumf& frustum);
}; };
#endif // NAZARA_SCENENODE_HPP #endif // NAZARA_SCENENODE_HPP

View File

@ -24,9 +24,9 @@ class NAZARA_API NzSceneRoot : public NzSceneNode
NzSceneRoot(NzScene* scene); NzSceneRoot(NzScene* scene);
virtual ~NzSceneRoot(); virtual ~NzSceneRoot();
bool FrustumCull(const NzFrustumf& frustum) override;
void Register(); void Register();
void Unregister(); void Unregister();
bool VisibilityTest(const NzCamera* camera) override;
}; };
#endif // NAZARA_SCENEROOT_HPP #endif // NAZARA_SCENEROOT_HPP

View File

@ -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 <Nazara/Prerequesites.hpp>
#include <Nazara/Graphics/Enums.hpp>
#include <Nazara/Graphics/Scene.hpp>
#include <Nazara/Math/BoundingVolume.hpp>
#include <Nazara/Math/Frustum.hpp>
#include <Nazara/Utility/Node.hpp>
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

View File

@ -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 <Nazara/Prerequesites.hpp>
#include <Nazara/Graphics/SceneNode.hpp>
#include <Nazara/Renderer/Material.hpp>
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

View File

@ -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 <Nazara/Prerequesites.hpp>
#include <Nazara/Graphics/AbstractViewer.hpp>
#include <Nazara/Graphics/SceneNode.hpp>
#include <Nazara/Math/Frustum.hpp>
#include <Nazara/Math/Matrix4.hpp>
#include <Nazara/Math/Rect.hpp>
#include <Nazara/Math/Vector3.hpp>
#include <Nazara/Renderer/RenderTarget.hpp>
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

View File

@ -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 Nazara Engine - Renderer module
@ -38,12 +38,14 @@
#include <Nazara/Renderer/OcclusionQuery.hpp> #include <Nazara/Renderer/OcclusionQuery.hpp>
#include <Nazara/Renderer/OpenGL.hpp> #include <Nazara/Renderer/OpenGL.hpp>
#include <Nazara/Renderer/Renderer.hpp> #include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Renderer/RenderStates.hpp>
#include <Nazara/Renderer/RenderTarget.hpp> #include <Nazara/Renderer/RenderTarget.hpp>
#include <Nazara/Renderer/RenderTargetParameters.hpp> #include <Nazara/Renderer/RenderTargetParameters.hpp>
#include <Nazara/Renderer/RenderTexture.hpp> #include <Nazara/Renderer/RenderTexture.hpp>
#include <Nazara/Renderer/RenderWindow.hpp> #include <Nazara/Renderer/RenderWindow.hpp>
#include <Nazara/Renderer/ShaderProgram.hpp> #include <Nazara/Renderer/ShaderProgram.hpp>
#include <Nazara/Renderer/ShaderProgramManager.hpp> #include <Nazara/Renderer/ShaderProgramManager.hpp>
#include <Nazara/Renderer/ShaderProgramManagerParams.hpp>
#include <Nazara/Renderer/Texture.hpp> #include <Nazara/Renderer/Texture.hpp>
#include <Nazara/Renderer/TextureSampler.hpp> #include <Nazara/Renderer/TextureSampler.hpp>

View File

@ -179,13 +179,14 @@ enum nzShaderTarget
nzShaderTarget_FullscreenQuad, nzShaderTarget_FullscreenQuad,
nzShaderTarget_Model, nzShaderTarget_Model,
nzShaderTarget_None, nzShaderTarget_None,
nzShaderTarget_Sprite,
nzShaderTarget_Max = nzShaderTarget_None nzShaderTarget_Max = nzShaderTarget_Sprite
}; };
enum nzShaderUniform enum nzShaderUniform
{ {
nzShaderUniform_CameraPosition, nzShaderUniform_EyePosition,
nzShaderUniform_InvTargetSize, nzShaderUniform_InvTargetSize,
nzShaderUniform_MaterialAlphaMap, nzShaderUniform_MaterialAlphaMap,
nzShaderUniform_MaterialAlphaThreshold, nzShaderUniform_MaterialAlphaThreshold,

View File

@ -58,6 +58,7 @@ class NAZARA_API NzRenderWindow : public NzRenderTarget, public NzWindow
private: private:
bool OnWindowCreated() override; bool OnWindowCreated() override;
void OnWindowDestroy() override; void OnWindowDestroy() override;
void OnWindowResized() override;
NzClock m_clock; NzClock m_clock;
NzContextParameters m_parameters; NzContextParameters m_parameters;

View File

@ -30,6 +30,13 @@ struct NzShaderProgramManagerParams
bool specularMapping; bool specularMapping;
}; };
struct Sprite
{
bool alphaMapping;
bool alphaTest;
bool diffuseMapping;
};
nzShaderTarget target; nzShaderTarget target;
nzUInt32 flags; nzUInt32 flags;
@ -37,6 +44,7 @@ struct NzShaderProgramManagerParams
{ {
FullscreenQuad fullscreenQuad; FullscreenQuad fullscreenQuad;
Model model; Model model;
Sprite sprite;
}; };
}; };

View File

@ -0,0 +1,8 @@
// Copyright (C) 2013 Jérôme Leclercq
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Graphics/AbstractViewer.hpp>
#include <Nazara/Graphics/Debug.hpp>
NzAbstractViewer::~NzAbstractViewer() = default;

View File

@ -9,7 +9,7 @@
#include <Nazara/Graphics/Debug.hpp> #include <Nazara/Graphics/Debug.hpp>
NzCamera::NzCamera() : 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_target(nullptr),
m_frustumUpdated(false), m_frustumUpdated(false),
m_projectionMatrixUpdated(false), m_projectionMatrixUpdated(false),
@ -28,26 +28,6 @@ NzCamera::~NzCamera()
m_target->RemoveListener(this); 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 void NzCamera::EnsureFrustumUpdate() const
{ {
if (!m_frustumUpdated) if (!m_frustumUpdated)
@ -66,16 +46,20 @@ void NzCamera::EnsureViewMatrixUpdate() const
UpdateViewMatrix(); UpdateViewMatrix();
} }
void NzCamera::EnsureViewportUpdate() const
{
if (!m_viewportUpdated)
UpdateViewport();
}
float NzCamera::GetAspectRatio() const float NzCamera::GetAspectRatio() const
{ {
return m_aspectRatio; return m_aspectRatio;
} }
const NzBoundingVolumef& NzCamera::GetBoundingVolume() const NzVector3f NzCamera::GetEyePosition() const
{ {
///TODO: Remplacer par le bounding volume du Frustum ? return GetPosition(nzCoordSys_Global);
static NzBoundingVolumef dummy(nzExtend_Null);
return dummy;
} }
float NzCamera::GetFOV() const float NzCamera::GetFOV() const
@ -99,11 +83,6 @@ const NzMatrix4f& NzCamera::GetProjectionMatrix() const
return m_projectionMatrix; return m_projectionMatrix;
} }
nzSceneNodeType NzCamera::GetSceneNodeType() const
{
return nzSceneNodeType_Camera;
}
const NzRenderTarget* NzCamera::GetTarget() const const NzRenderTarget* NzCamera::GetTarget() const
{ {
return m_target; return m_target;
@ -174,6 +153,9 @@ void NzCamera::SetTarget(const NzRenderTarget& renderTarget)
void NzCamera::SetTargetRegion(const NzRectf& region) void NzCamera::SetTargetRegion(const NzRectf& region)
{ {
m_targetRegion = region; m_targetRegion = region;
m_frustumUpdated = false;
m_projectionMatrixUpdated = false;
m_viewportUpdated = false; m_viewportUpdated = false;
} }
@ -210,16 +192,33 @@ void NzCamera::SetZNear(float zNear)
m_projectionMatrixUpdated = false; 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() void NzCamera::Invalidate()
{ {
NzSceneNode::Invalidate(); NzNode::Invalidate();
// Le frustum et la view matrix dépendent des paramètres du node, invalidons-les // Le frustum et la view matrix dépendent des paramètres du node, invalidons-les
m_frustumUpdated = false; m_frustumUpdated = false;
@ -241,21 +240,17 @@ bool NzCamera::OnRenderTargetSizeChange(const NzRenderTarget* renderTarget, void
NazaraUnused(userdata); NazaraUnused(userdata);
if (renderTarget == m_target) if (renderTarget == m_target)
{
m_frustumUpdated = false;
m_projectionMatrixUpdated = false;
m_viewportUpdated = false; m_viewportUpdated = false;
}
else else
NazaraInternalError("Not listening to " + NzString::Pointer(renderTarget)); NazaraInternalError("Not listening to " + NzString::Pointer(renderTarget));
return true; return true;
} }
void NzCamera::Register()
{
}
void NzCamera::Unregister()
{
}
void NzCamera::UpdateFrustum() const void NzCamera::UpdateFrustum() const
{ {
if (!m_projectionMatrixUpdated) if (!m_projectionMatrixUpdated)
@ -270,6 +265,9 @@ void NzCamera::UpdateFrustum() const
void NzCamera::UpdateProjectionMatrix() 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_projectionMatrix.MakePerspective(m_fov, m_aspectRatio, m_zNear, m_zFar);
m_projectionMatrixUpdated = true; m_projectionMatrixUpdated = true;
} }
@ -288,8 +286,8 @@ void NzCamera::UpdateViewport() const
unsigned int width = m_target->GetWidth(); unsigned int width = m_target->GetWidth();
unsigned int height = std::max(m_target->GetHeight(), 1U); unsigned int height = std::max(m_target->GetHeight(), 1U);
float vWidth = width * m_viewport.width; float vWidth = width * m_targetRegion.width;
float vHeight = height * m_viewport.height; float vHeight = height * m_targetRegion.height;
float aspectRatio = vWidth/vHeight; float aspectRatio = vWidth/vHeight;
if (!NzNumberEquals(m_aspectRatio, aspectRatio, 0.001f)) if (!NzNumberEquals(m_aspectRatio, aspectRatio, 0.001f))
@ -305,10 +303,3 @@ void NzCamera::UpdateViewport() const
m_viewport.height = vHeight; m_viewport.height = vHeight;
m_viewportUpdated = true; m_viewportUpdated = true;
} }
bool NzCamera::VisibilityTest(const NzCamera* camera)
{
NazaraUnused(camera);
//NazaraInternalError("SceneNode::IsVisible() called on Camera");
return false;
}

View File

@ -6,6 +6,7 @@
#include <Nazara/Graphics/Camera.hpp> #include <Nazara/Graphics/Camera.hpp>
#include <Nazara/Graphics/Light.hpp> #include <Nazara/Graphics/Light.hpp>
#include <Nazara/Graphics/Model.hpp> #include <Nazara/Graphics/Model.hpp>
#include <Nazara/Graphics/Sprite.hpp>
#include <Nazara/Renderer/Material.hpp> #include <Nazara/Renderer/Material.hpp>
#include <Nazara/Utility/SkeletalMesh.hpp> #include <Nazara/Utility/SkeletalMesh.hpp>
#include <Nazara/Utility/StaticMesh.hpp> #include <Nazara/Utility/StaticMesh.hpp>
@ -131,13 +132,18 @@ void NzForwardRenderQueue::AddModel(const NzModel* model)
} }
else 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) if (pair.second)
material->AddResourceListener(this, ResourceType_Material); 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) if (pair2.second)
{ {
staticMesh->AddResourceListener(this, ResourceType_StaticMesh); 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é ? // 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) 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); staticDataContainer.resize(instanceCount);
StaticData& data = staticDataContainer.back(); 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) void NzForwardRenderQueue::Clear(bool fully)
{ {
directionnalLights.clear(); directionnalLights.clear();
@ -176,7 +195,10 @@ void NzForwardRenderQueue::Clear(bool fully)
transparentStaticModels.clear(); transparentStaticModels.clear();
if (fully) if (fully)
{
opaqueModels.clear(); opaqueModels.clear();
sprites.clear();
}
} }
void NzForwardRenderQueue::Sort(const NzCamera& camera) void NzForwardRenderQueue::Sort(const NzCamera& camera)
@ -219,7 +241,7 @@ bool NzForwardRenderQueue::OnResourceDestroy(const NzResource* resource, int ind
case ResourceType_SkeletalMesh: case ResourceType_SkeletalMesh:
{ {
for (auto& pair : opaqueModels) for (auto& pair : opaqueModels)
std::get<1>(pair.second).erase(static_cast<const NzSkeletalMesh*>(resource)); std::get<2>(pair.second).erase(static_cast<const NzSkeletalMesh*>(resource));
break; break;
} }
@ -227,7 +249,7 @@ bool NzForwardRenderQueue::OnResourceDestroy(const NzResource* resource, int ind
case ResourceType_StaticMesh: case ResourceType_StaticMesh:
{ {
for (auto& pair : opaqueModels) for (auto& pair : opaqueModels)
std::get<2>(pair.second).erase(static_cast<const NzStaticMesh*>(resource)); std::get<3>(pair.second).erase(static_cast<const NzStaticMesh*>(resource));
break; 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 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) for (unsigned int i = 0; i <= nzShaderFlags_Max; ++i)
{ {
@ -255,7 +277,26 @@ bool NzForwardRenderQueue::ModelMaterialComparator::operator()(const NzMaterial*
return mat1 < mat2; 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 NzIndexBuffer* iBuffer1 = subMesh1->GetIndexBuffer();
const NzBuffer* buffer1 = (iBuffer1) ? iBuffer1->GetBuffer() : nullptr; const NzBuffer* buffer1 = (iBuffer1) ? iBuffer1->GetBuffer() : nullptr;
@ -269,7 +310,7 @@ bool NzForwardRenderQueue::SkeletalMeshComparator::operator()(const NzSkeletalMe
return buffer2 < buffer2; 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 NzIndexBuffer* iBuffer1 = subMesh1->GetIndexBuffer();
const NzBuffer* buffer1 = (iBuffer1) ? iBuffer1->GetBuffer() : nullptr; const NzBuffer* buffer1 = (iBuffer1) ? iBuffer1->GetBuffer() : nullptr;

View File

@ -7,6 +7,7 @@
#include <Nazara/Graphics/Camera.hpp> #include <Nazara/Graphics/Camera.hpp>
#include <Nazara/Graphics/Drawable.hpp> #include <Nazara/Graphics/Drawable.hpp>
#include <Nazara/Graphics/Light.hpp> #include <Nazara/Graphics/Light.hpp>
#include <Nazara/Graphics/Sprite.hpp>
#include <Nazara/Renderer/Config.hpp> #include <Nazara/Renderer/Config.hpp>
#include <Nazara/Renderer/Material.hpp> #include <Nazara/Renderer/Material.hpp>
#include <Nazara/Renderer/Renderer.hpp> #include <Nazara/Renderer/Renderer.hpp>
@ -19,106 +20,47 @@
namespace 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: std::unique_ptr<NzIndexBuffer> indexBuffer(new NzIndexBuffer(false, s_maxSprites*6, nzBufferStorage_Hardware, nzBufferUsage_Static));
LightManager() = default; indexBuffer->SetPersistent(false);
~LightManager() = default;
unsigned int FindClosestLights(const NzLight** lights, unsigned int lightCount, const NzVector3f& position, float squaredRadius) NzBufferMapper<NzIndexBuffer> mapper(indexBuffer.get(), nzBufferAccess_WriteOnly);
{ nzUInt16* indices = static_cast<nzUInt16*>(mapper.GetPointer());
for (Light& light : m_lights)
{
light.light = nullptr;
light.score = std::numeric_limits<unsigned int>::max(); // Nous jouons au Golf
}
for (unsigned int i = 0; i < lightCount; ++i) for (unsigned int i = 0; i < s_maxSprites; ++i)
{ {
const NzLight* light = *lights; *indices++ = i*4 + 0;
*indices++ = i*4 + 2;
*indices++ = i*4 + 1;
unsigned int score = std::numeric_limits<unsigned int>::max(); *indices++ = i*4 + 2;
switch (light->GetLightType()) *indices++ = i*4 + 3;
{ *indices++ = i*4 + 1;
case nzLightType_Directional: }
score = 0; // Lumière choisie d'office
break;
case nzLightType_Point: return indexBuffer.release();
{ }
float lightRadius = light->GetRadius();
float squaredDistance = position.SquaredDistance(light->GetPosition());
if (squaredDistance - squaredRadius <= lightRadius*lightRadius)
score = static_cast<unsigned int>(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<unsigned int>(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
};
} }
NzForwardRenderTechnique::NzForwardRenderTechnique() : 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) void NzForwardRenderTechnique::Clear(const NzScene* scene)
@ -134,247 +76,22 @@ void NzForwardRenderTechnique::Clear(const NzScene* scene)
void NzForwardRenderTechnique::Draw(const NzScene* scene) void NzForwardRenderTechnique::Draw(const NzScene* scene)
{ {
///TODO: Regrouper les activations par méthode // Rendu en projection perspective (3D)
LightManager lightManager; 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(); if (!m_renderQueue.opaqueModels.empty())
const NzShaderProgram* lastProgram = nullptr; DrawOpaqueModels(scene, m_renderQueue.opaqueModels);
NzRenderer::SetMatrix(nzMatrixType_Projection, camera->GetProjectionMatrix()); if (!m_renderQueue.sprites.empty())
NzRenderer::SetMatrix(nzMatrixType_View, camera->GetViewMatrix()); DrawSprites(scene, m_renderQueue.sprites);
// Rendu des modèles opaques if (!m_renderQueue.transparentsModels.empty())
for (auto& matIt : m_renderQueue.opaqueModels) DrawTransparentModels(scene, m_renderQueue.transparentsModels);
{
NzForwardRenderQueue::SkeletalMeshContainer& skeletalContainer = std::get<1>(matIt.second);
NzForwardRenderQueue::StaticMeshContainer& staticContainer = std::get<2>(matIt.second);
if (!skeletalContainer.empty() || !staticContainer.empty()) // Les autres drawables (Exemple: Terrain)
{ for (const NzDrawable* drawable : m_renderQueue.otherDrawables)
const NzMaterial* material = matIt.first; drawable->Draw();
// 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<NzForwardRenderQueue::StaticData>& 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<void(nzPrimitiveMode, unsigned int, unsigned int)> drawFunc;
std::function<void(unsigned int, nzPrimitiveMode, unsigned int, unsigned int)> 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<NzVertexBuffer> mapper(instanceBuffer, nzBufferAccess_DiscardAndWrite, 0, renderedInstanceCount);
nzUInt8* ptr = reinterpret_cast<nzUInt8*>(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<unsigned int, bool>& 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<void(nzPrimitiveMode, unsigned int, unsigned int)> 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 billboards // Les billboards
/*if (!m_renderQueue.billboards.empty()) /*if (!m_renderQueue.billboards.empty())
@ -420,10 +137,6 @@ void NzForwardRenderTechnique::Draw(const NzScene* scene)
billboards.clear(); billboards.clear();
} }
}*/ }*/
// Les autres drawables (Exemple: Terrain)
for (const NzDrawable* drawable : m_renderQueue.otherDrawables)
drawable->Draw();
} }
unsigned int NzForwardRenderTechnique::GetMaxLightsPerObject() const unsigned int NzForwardRenderTechnique::GetMaxLightsPerObject() const
@ -438,5 +151,340 @@ NzAbstractRenderQueue* NzForwardRenderTechnique::GetRenderQueue()
void NzForwardRenderTechnique::SetMaxLightsPerObject(unsigned int lightCount) 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<NzForwardRenderQueue::StaticData>& 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<void(nzPrimitiveMode, unsigned int, unsigned int)> DrawFunc;
std::function<void(unsigned int, nzPrimitiveMode, unsigned int, unsigned int)> 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<NzVertexBuffer> mapper(instanceBuffer, nzBufferAccess_DiscardAndWrite, 0, renderedInstanceCount);
nzUInt8* ptr = reinterpret_cast<nzUInt8*>(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<NzVertexBuffer> vertexMapper(m_spriteBuffer, nzBufferAccess_DiscardAndWrite, 0, renderedSpriteCount*4);
NzVertexStruct_XYZ_UV* vertices = reinterpret_cast<NzVertexStruct_XYZ_UV*>(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<unsigned int, bool>& 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<void(nzPrimitiveMode, unsigned int, unsigned int)> 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
}
}
} }

View File

@ -225,6 +225,31 @@ void NzLight::Disable(const NzShaderProgram* program, unsigned int lightUnit)
program->SendInteger(program->GetUniformLocation("Lights[" + NzString::Number(lightUnit) + "].type"), -1); 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() void NzLight::Invalidate()
{ {
NzSceneNode::Invalidate(); NzSceneNode::Invalidate();
@ -307,28 +332,3 @@ void NzLight::UpdateBoundingVolume() const
m_boundingVolumeUpdated = true; 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;
}

View File

@ -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 <Nazara/Graphics/LightManager.hpp>
#include <Nazara/Graphics/Light.hpp>
#include <Nazara/Graphics/Debug.hpp>
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<unsigned int>::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<unsigned int>::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<unsigned int>(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<unsigned int>(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);
}

View File

@ -630,6 +630,25 @@ NzModel& NzModel::operator=(NzModel&& node)
return *this; 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() void NzModel::Invalidate()
{ {
NzSceneNode::Invalidate(); NzSceneNode::Invalidate();
@ -671,23 +690,4 @@ void NzModel::UpdateBoundingVolume() const
m_boundingVolumeUpdated = true; 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; NzModelLoader::LoaderList NzModel::s_loaders;

View File

@ -30,7 +30,7 @@ struct NzSceneImpl
NzClock updateClock; NzClock updateClock;
NzColor ambientColor = NzColor(25,25,25); NzColor ambientColor = NzColor(25,25,25);
NzSceneRoot root; NzSceneRoot root;
NzCamera* activeCamera; NzAbstractViewer* viewer;
bool update; bool update;
float frameTime; float frameTime;
float updateTime; float updateTime;
@ -64,9 +64,9 @@ void NzScene::AddToVisibilityList(NzUpdatable* object)
void NzScene::Cull() void NzScene::Cull()
{ {
#if NAZARA_GRAPHICS_SAFE #if NAZARA_GRAPHICS_SAFE
if (!m_impl->activeCamera) if (!m_impl->viewer)
{ {
NazaraError("No active camera"); NazaraError("No viewer");
return; return;
} }
#endif #endif
@ -76,8 +76,8 @@ void NzScene::Cull()
m_impl->visibleUpdateList.clear(); m_impl->visibleUpdateList.clear();
// Frustum culling/Viewport culling // Frustum culling
RecursiveCameraCull(m_impl->renderTechnique->GetRenderQueue(), m_impl->activeCamera, &m_impl->root); RecursiveFrustumCull(m_impl->renderTechnique->GetRenderQueue(), m_impl->viewer->GetFrustum(), &m_impl->root);
///TODO: Occlusion culling ///TODO: Occlusion culling
@ -87,22 +87,18 @@ void NzScene::Cull()
void NzScene::Draw() void NzScene::Draw()
{ {
#if NAZARA_GRAPHICS_SAFE #if NAZARA_GRAPHICS_SAFE
if (!m_impl->activeCamera) if (!m_impl->viewer)
{ {
NazaraError("No active camera"); NazaraError("No viewer");
return; return;
} }
#endif #endif
m_impl->renderTechnique->Clear(this); m_impl->renderTechnique->Clear(this);
m_impl->viewer->ApplyView();
m_impl->renderTechnique->Draw(this); m_impl->renderTechnique->Draw(this);
} }
NzCamera* NzScene::GetActiveCamera() const
{
return m_impl->activeCamera;
}
NzColor NzScene::GetAmbientColor() const NzColor NzScene::GetAmbientColor() const
{ {
return m_impl->ambientColor; return m_impl->ambientColor;
@ -123,6 +119,11 @@ NzSceneNode& NzScene::GetRoot() const
return m_impl->root; return m_impl->root;
} }
NzAbstractViewer* NzScene::GetViewer() const
{
return m_impl->viewer;
}
float NzScene::GetUpdateTime() const float NzScene::GetUpdateTime() const
{ {
return m_impl->updateTime; return m_impl->updateTime;
@ -161,6 +162,11 @@ void NzScene::SetRenderTechnique(NzAbstractRenderTechnique* renderTechnique)
m_impl->renderTechnique.reset(renderTechnique); m_impl->renderTechnique.reset(renderTechnique);
} }
void NzScene::SetViewer(NzAbstractViewer* viewer)
{
m_impl->viewer = viewer;
}
void NzScene::SetUpdatePerSecond(unsigned int updatePerSecond) void NzScene::SetUpdatePerSecond(unsigned int updatePerSecond)
{ {
m_impl->updatePerSecond = updatePerSecond; m_impl->updatePerSecond = updatePerSecond;
@ -209,7 +215,7 @@ NzScene::operator const NzSceneNode&() const
return m_impl->root; 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()) for (NzNode* child : node->GetChilds())
{ {
@ -218,17 +224,12 @@ void NzScene::RecursiveCameraCull(NzAbstractRenderQueue* renderQueue, const NzCa
NzSceneNode* sceneNode = static_cast<NzSceneNode*>(child); NzSceneNode* sceneNode = static_cast<NzSceneNode*>(child);
///TODO: Empêcher le rendu des enfants si le parent est cullé selon un flag ///TODO: Empêcher le rendu des enfants si le parent est cullé selon un flag
sceneNode->UpdateVisibility(camera); sceneNode->UpdateVisibility(frustum);
if (sceneNode->IsVisible()) if (sceneNode->IsVisible())
sceneNode->AddToRenderQueue(renderQueue); sceneNode->AddToRenderQueue(renderQueue);
} }
if (child->HasChilds()) if (child->HasChilds())
RecursiveCameraCull(renderQueue, camera, child); RecursiveFrustumCull(renderQueue, frustum, child);
} }
} }
void NzScene::SetActiveCamera(NzCamera* camera)
{
m_impl->activeCamera = camera;
}

View File

@ -97,11 +97,11 @@ void NzSceneNode::Update()
{ {
} }
void NzSceneNode::UpdateVisibility(const NzCamera* camera) void NzSceneNode::UpdateVisibility(const NzFrustumf& frustum)
{ {
bool wasVisible = m_visible; bool wasVisible = m_visible;
m_visible = VisibilityTest(camera); m_visible = FrustumCull(frustum);
if (m_visible != wasVisible) if (m_visible != wasVisible)
OnVisibilityChange(m_visible); OnVisibilityChange(m_visible);

View File

@ -31,6 +31,13 @@ nzSceneNodeType NzSceneRoot::GetSceneNodeType() const
return nzSceneNodeType_Root; return nzSceneNodeType_Root;
} }
bool NzSceneRoot::FrustumCull(const NzFrustumf& frustum)
{
NazaraUnused(frustum);
return true; // Toujours visible
}
void NzSceneRoot::Register() void NzSceneRoot::Register()
{ {
NazaraInternalError("SceneNode::Register() called on SceneRoot"); NazaraInternalError("SceneNode::Register() called on SceneRoot");
@ -40,10 +47,3 @@ void NzSceneRoot::Unregister()
{ {
NazaraInternalError("SceneNode::Unregister() called on SceneRoot"); NazaraInternalError("SceneNode::Unregister() called on SceneRoot");
} }
bool NzSceneRoot::VisibilityTest(const NzCamera* camera)
{
NazaraUnused(camera);
return true; // Toujours visible
}

View File

@ -198,15 +198,15 @@ void NzSkyboxBackground::Draw(const NzScene* scene) const
s_program->SendInteger(s_skyboxLocation, 0); 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()); skyboxMatrix.SetTranslation(NzVector3f::Zero());
NzRenderer::SetIndexBuffer(m_indexBuffer); NzRenderer::SetIndexBuffer(m_indexBuffer);
NzRenderer::SetMatrix(nzMatrixType_Projection, camera->GetProjectionMatrix()); NzRenderer::SetMatrix(nzMatrixType_Projection, viewer->GetProjectionMatrix());
NzRenderer::SetMatrix(nzMatrixType_View, skyboxMatrix); 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::SetRenderStates(states);
NzRenderer::SetShaderProgram(s_program); NzRenderer::SetShaderProgram(s_program);
NzRenderer::SetTexture(0, m_texture); NzRenderer::SetTexture(0, m_texture);

View File

@ -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 <Nazara/Graphics/Sprite.hpp>
#include <Nazara/Graphics/Debug.hpp>
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()
{
}

View File

@ -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 <Nazara/Graphics/View.hpp>
#include <Nazara/Graphics/Scene.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Renderer/RenderTarget.hpp>
#include <Nazara/Graphics/Debug.hpp>
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;
}

View File

@ -605,7 +605,7 @@ bool NzGLSLProgram::PostLinkage()
// Pour éviter de se tromper entre le nom et la constante // Pour éviter de se tromper entre le nom et la constante
#define CacheUniform(name) m_uniformLocations[nzShaderUniform_##name] = GetUniformLocation(#name) #define CacheUniform(name) m_uniformLocations[nzShaderUniform_##name] = GetUniformLocation(#name)
CacheUniform(CameraPosition); CacheUniform(EyePosition);
CacheUniform(InvTargetSize); CacheUniform(InvTargetSize);
CacheUniform(MaterialAlphaMap); CacheUniform(MaterialAlphaMap);
CacheUniform(MaterialAlphaThreshold); CacheUniform(MaterialAlphaThreshold);

View File

@ -163,11 +163,17 @@ void NzMaterial::Enable(nzRendererParameter renderParameter, bool enable)
void NzMaterial::EnableAlphaTest(bool alphaTest) void NzMaterial::EnableAlphaTest(bool alphaTest)
{ {
m_alphaTestEnabled = alphaTest; m_alphaTestEnabled = alphaTest;
InvalidatePrograms(nzShaderTarget_FullscreenQuad);
InvalidatePrograms(nzShaderTarget_Model);
InvalidatePrograms(nzShaderTarget_Sprite);
} }
void NzMaterial::EnableLighting(bool lighting) void NzMaterial::EnableLighting(bool lighting)
{ {
m_lightingEnabled = lighting; m_lightingEnabled = lighting;
InvalidatePrograms(nzShaderTarget_Model);
} }
NzTexture* NzMaterial::GetAlphaMap() const NzTexture* NzMaterial::GetAlphaMap() const
@ -410,7 +416,9 @@ void NzMaterial::SetAlphaMap(NzTexture* map)
{ {
m_alphaMap = map; m_alphaMap = map;
InvalidatePrograms(nzShaderTarget_FullscreenQuad);
InvalidatePrograms(nzShaderTarget_Model); InvalidatePrograms(nzShaderTarget_Model);
InvalidatePrograms(nzShaderTarget_Sprite);
} }
void NzMaterial::SetAlphaThreshold(float alphaThreshold) void NzMaterial::SetAlphaThreshold(float alphaThreshold)
@ -454,7 +462,9 @@ void NzMaterial::SetDiffuseMap(NzTexture* map)
{ {
m_diffuseMap = map; m_diffuseMap = map;
InvalidatePrograms(nzShaderTarget_FullscreenQuad);
InvalidatePrograms(nzShaderTarget_Model); InvalidatePrograms(nzShaderTarget_Model);
InvalidatePrograms(nzShaderTarget_Sprite);
} }
void NzMaterial::SetDiffuseSampler(const NzTextureSampler& sampler) void NzMaterial::SetDiffuseSampler(const NzTextureSampler& sampler)
@ -706,6 +716,12 @@ void NzMaterial::GenerateProgram(nzShaderTarget target, nzUInt32 flags) const
case nzShaderTarget_None: case nzShaderTarget_None:
break; 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); m_programs[target][flags].program = NzShaderProgramManager::Get(params);

View File

@ -310,3 +310,9 @@ void NzRenderWindow::OnWindowDestroy()
m_context = nullptr; m_context = nullptr;
} }
} }
void NzRenderWindow::OnWindowResized()
{
NotifySizeChange();
}

View File

@ -3,6 +3,9 @@
// For conditions of distribution and use, see copyright notice in Config.hpp // For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Core/Log.hpp> #include <Nazara/Core/Log.hpp>
#include <Nazara/Core/Clock.hpp>
#include <Nazara/Core/Directory.hpp>
#include <Nazara/Core/File.hpp>
#include <Nazara/Renderer/OpenGL.hpp> #include <Nazara/Renderer/OpenGL.hpp>
#include <Nazara/Renderer/ShaderProgramManager.hpp> #include <Nazara/Renderer/ShaderProgramManager.hpp>
#include <cstring> #include <cstring>
@ -24,9 +27,9 @@ namespace
switch (params.target) switch (params.target)
{ {
case nzShaderTarget_FullscreenQuad: case nzShaderTarget_FullscreenQuad:
h |= (params.fullscreenQuad.alphaMapping << 10) | // 1 bit h |= (params.fullscreenQuad.alphaMapping << 10) | // 1 bit
(params.fullscreenQuad.alphaTest << 11) | // 1 bit (params.fullscreenQuad.alphaTest << 11) | // 1 bit
(params.fullscreenQuad.diffuseMapping << 12); // 1 bit (params.fullscreenQuad.diffuseMapping << 12); // 1 bit
break; break;
case nzShaderTarget_Model: case nzShaderTarget_Model:
@ -42,6 +45,12 @@ namespace
case nzShaderTarget_None: case nzShaderTarget_None:
break; 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; return h;
@ -65,6 +74,9 @@ namespace
case nzShaderTarget_None: case nzShaderTarget_None:
return true; return true;
case nzShaderTarget_Sprite:
return std::memcmp(&first.sprite, &second.sprite, sizeof(NzShaderProgramManagerParams::Sprite)) == 0;
} }
return false; return false;
@ -72,12 +84,15 @@ namespace
}; };
std::unordered_map<NzShaderProgramManagerParams, NzShaderProgramRef, ParamsHash, ParamsEquality> s_programs; std::unordered_map<NzShaderProgramManagerParams, NzShaderProgramRef, ParamsHash, ParamsEquality> s_programs;
NzString s_cacheDirectory("shaderCache");
NzString s_inKW; NzString s_inKW;
NzString s_outKW; NzString s_outKW;
NzString s_fragmentColorKW; NzString s_fragmentColorKW;
NzString s_textureLookupKW; NzString s_textureLookupKW;
bool s_cacheEnabled = false;
bool s_earlyFragmentTest; bool s_earlyFragmentTest;
bool s_glsl140; bool s_glsl140;
//bool s_loadCachedPrograms = true;
unsigned int s_glslVersion; unsigned int s_glslVersion;
} }
@ -86,8 +101,68 @@ const NzShaderProgram* NzShaderProgramManager::Get(const NzShaderProgramManagerP
auto it = s_programs.find(params); auto it = s_programs.find(params);
if (it == s_programs.end()) if (it == s_programs.end())
{ {
// Alors nous gébérons le programme // Alors nous générons le programme
NzShaderProgram* program = GenerateProgram(params); std::unique_ptr<NzShaderProgram> 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) if (!program)
{ {
NazaraWarning("Failed to build program, using default one..."); NazaraWarning("Failed to build program, using default one...");
@ -96,12 +171,10 @@ const NzShaderProgram* NzShaderProgramManager::Get(const NzShaderProgramManagerP
defaultParams.flags = params.flags; defaultParams.flags = params.flags;
defaultParams.target = nzShaderTarget_None; 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.release();
return program;
} }
else else
return it->second; return it->second;
@ -125,13 +198,14 @@ NzString NzShaderProgramManager::BuildFragmentCode(const NzShaderProgramManagerP
source += NzString::Number(s_glslVersion); source += NzString::Number(s_glslVersion);
source += "\n\n"; source += "\n\n";
if (s_earlyFragmentTest)
source += "layout(early_fragment_tests) in;" "\n\n";
switch (params.target) switch (params.target)
{ {
case nzShaderTarget_FullscreenQuad: 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********************/ /********************Entrant********************/
if (params.fullscreenQuad.alphaMapping || params.fullscreenQuad.diffuseMapping) if (params.fullscreenQuad.alphaMapping || params.fullscreenQuad.diffuseMapping)
source += s_inKW + " vec2 vTexCoord;" "\n\n"; source += s_inKW + " vec2 vTexCoord;" "\n\n";
@ -162,23 +236,17 @@ NzString NzShaderProgramManager::BuildFragmentCode(const NzShaderProgramManagerP
if (params.fullscreenQuad.diffuseMapping) if (params.fullscreenQuad.diffuseMapping)
source += '*' + s_textureLookupKW + "(MaterialDiffuseMap, vTexCoord)"; source += '*' + s_textureLookupKW + "(MaterialDiffuseMap, vTexCoord)";
source += ";" "\n" source += ";" "\n";
"\t" "float fragmentAlpha = ";
if (params.fullscreenQuad.diffuseMapping)
source += "fragmentColor.a";
else
source += "MaterialDiffuse.a";
if (params.fullscreenQuad.alphaMapping) if (params.fullscreenQuad.alphaMapping)
source += '*' + s_textureLookupKW + "(MaterialAlphaMap, vTexCoord).r"; source += "fragmentColor.a *= " + s_textureLookupKW + "(MaterialAlphaMap, vTexCoord).r";
source += ";" "\n"; source += ";" "\n";
if (params.fullscreenQuad.alphaMapping) if (params.fullscreenQuad.alphaMapping)
{ {
source += "if (fragmentAlpha < MaterialAlphaThreshold)" "\n" source += "\t" "if (fragmentColor.a < MaterialAlphaThreshold)" "\n"
"\t" "discard;" "\n"; "\t\t" "discard;" "\n";
} }
source += "\t" "RenderTarget0 = fragmentColor;" "\n" source += "\t" "RenderTarget0 = fragmentColor;" "\n"
@ -191,6 +259,10 @@ NzString NzShaderProgramManager::BuildFragmentCode(const NzShaderProgramManagerP
//bool parallaxMapping = (params.model.lighting && params.model.parallaxMapping); //bool parallaxMapping = (params.model.lighting && params.model.parallaxMapping);
bool uvMapping = (params.model.alphaMapping || params.model.diffuseMapping || params.model.normalMapping || params.model.specularMapping); 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) if (params.model.lighting)
{ {
source += "#define LIGHT_DIRECTIONAL 0" "\n" source += "#define LIGHT_DIRECTIONAL 0" "\n"
@ -235,7 +307,7 @@ NzString NzShaderProgramManager::BuildFragmentCode(const NzShaderProgramManagerP
"\t" "vec2 parameters3;" "\n" "\t" "vec2 parameters3;" "\n"
"};" "\n\n" "};" "\n\n"
"uniform vec3 CameraPosition;" "\n" "uniform vec3 EyePosition;" "\n"
"uniform Light Lights[3];" "\n" "uniform Light Lights[3];" "\n"
"uniform vec4 MaterialAmbient;" "\n"; "uniform vec4 MaterialAmbient;" "\n";
} }
@ -298,8 +370,8 @@ NzString NzShaderProgramManager::BuildFragmentCode(const NzShaderProgramManagerP
if (params.model.alphaTest) if (params.model.alphaTest)
{ {
source += "if (diffuseColor.a < MaterialAlphaThreshold)" "\n" source += "\t" "if (fragmentColor.a < MaterialAlphaThreshold)" "\n"
"\t" "discard;" "\n"; "\t\t" "discard;" "\n";
} }
if (params.model.lighting) if (params.model.lighting)
@ -318,7 +390,7 @@ NzString NzShaderProgramManager::BuildFragmentCode(const NzShaderProgramManagerP
source += "\t" "if (MaterialShininess > 0.0)" "\n" source += "\t" "if (MaterialShininess > 0.0)" "\n"
"\t" "{" "\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" "for (int i = 0; i < 3; ++i)" "\n"
"\t\t" "{" "\n"; "\t\t" "{" "\n";
@ -551,6 +623,9 @@ NzString NzShaderProgramManager::BuildFragmentCode(const NzShaderProgramManagerP
case nzShaderTarget_None: case nzShaderTarget_None:
{ {
if (s_earlyFragmentTest)
source += "layout(early_fragment_tests) in;" "\n\n";
/********************Sortant********************/ /********************Sortant********************/
if (s_glsl140) if (s_glsl140)
source += "out vec4 RenderTarget0;" "\n\n"; source += "out vec4 RenderTarget0;" "\n\n";
@ -565,6 +640,60 @@ NzString NzShaderProgramManager::BuildFragmentCode(const NzShaderProgramManagerP
source += "}" "\n"; source += "}" "\n";
break; 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; return source;
@ -754,6 +883,54 @@ NzString NzShaderProgramManager::BuildVertexCode(const NzShaderProgramManagerPar
source += "}" "\n"; source += "}" "\n";
break; 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; return source;
@ -825,6 +1002,54 @@ bool NzShaderProgramManager::Initialize()
s_programs[params] = program; 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<std::size_t>(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<NzShaderProgram> 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; return true;
} }