Big 3D update (WIP)
Former-commit-id: 9f55dae0521bded91640a7ea2d223a49a378c97c
This commit is contained in:
parent
a20818d66b
commit
ab1fc99fcd
|
|
@ -1,4 +1,4 @@
|
||||||
// This file was automatically generated by Nazara
|
// This file was automatically generated on 20 Feb 2013 at 17:02:32
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Nazara Engine - 3D module
|
Nazara Engine - 3D module
|
||||||
|
|
@ -30,9 +30,13 @@
|
||||||
#define NAZARA_GLOBAL_3D_HPP
|
#define NAZARA_GLOBAL_3D_HPP
|
||||||
|
|
||||||
#include <Nazara/3D/3D.hpp>
|
#include <Nazara/3D/3D.hpp>
|
||||||
|
#include <Nazara/3D/Camera.hpp>
|
||||||
#include <Nazara/3D/Config.hpp>
|
#include <Nazara/3D/Config.hpp>
|
||||||
#include <Nazara/3D/Enums.hpp>
|
#include <Nazara/3D/Enums.hpp>
|
||||||
|
#include <Nazara/3D/Light.hpp>
|
||||||
#include <Nazara/3D/Model.hpp>
|
#include <Nazara/3D/Model.hpp>
|
||||||
|
#include <Nazara/3D/Scene.hpp>
|
||||||
#include <Nazara/3D/SceneNode.hpp>
|
#include <Nazara/3D/SceneNode.hpp>
|
||||||
|
#include <Nazara/3D/SceneRoot.hpp>
|
||||||
|
|
||||||
#endif // NAZARA_GLOBAL_3D_HPP
|
#endif // NAZARA_GLOBAL_3D_HPP
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
// Copyright (C) 2012 Jérôme Leclercq
|
||||||
|
// This file is part of the "Nazara Engine - 3D Module"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef NAZARA_CAMERA_HPP
|
||||||
|
#define NAZARA_CAMERA_HPP
|
||||||
|
|
||||||
|
#include <Nazara/Prerequesites.hpp>
|
||||||
|
#include <Nazara/3D/SceneNode.hpp>
|
||||||
|
#include <Nazara/Math/Frustum.hpp>
|
||||||
|
#include <Nazara/Math/Matrix4.hpp>
|
||||||
|
#include <Nazara/Math/Rect.hpp>
|
||||||
|
#include <Nazara/Math/Vector3.hpp>
|
||||||
|
|
||||||
|
class NzRenderTarget;
|
||||||
|
|
||||||
|
class NAZARA_API NzCamera : public NzSceneNode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NzCamera();
|
||||||
|
~NzCamera();
|
||||||
|
|
||||||
|
void Activate() const;
|
||||||
|
|
||||||
|
void EnsureFrustumUpdate() const;
|
||||||
|
void EnsureProjectionMatrixUpdate() const;
|
||||||
|
void EnsureViewMatrixUpdate() const;
|
||||||
|
|
||||||
|
float GetAspectRatio() const;
|
||||||
|
const NzBoundingBoxf& GetBoundingBox() const override;
|
||||||
|
float GetFOV() const;
|
||||||
|
const NzFrustumf& GetFrustum() const;
|
||||||
|
const NzMatrix4f& GetProjectionMatrix() const;
|
||||||
|
nzSceneNodeType GetSceneNodeType() const override;
|
||||||
|
const NzVector3f& GetUpVector() const;
|
||||||
|
const NzMatrix4f& GetViewMatrix() const;
|
||||||
|
const NzRectf& GetViewport() const;
|
||||||
|
float GetZFar() const;
|
||||||
|
float GetZNear() const;
|
||||||
|
|
||||||
|
void SetFOV(float fov);
|
||||||
|
void SetUpVector(const NzVector3f& upVector);
|
||||||
|
void SetViewport(const NzRectf& viewport);
|
||||||
|
void SetZFar(float zFar);
|
||||||
|
void SetZNear(float zNear);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void AddToRenderQueue(NzRenderQueue& renderQueue) const;
|
||||||
|
void Invalidate();
|
||||||
|
bool IsVisible(const NzFrustumf& frustum) const override;
|
||||||
|
void Register();
|
||||||
|
void Unregister();
|
||||||
|
void UpdateFrustum() const;
|
||||||
|
void UpdateProjectionMatrix() const;
|
||||||
|
void UpdateViewMatrix() const;
|
||||||
|
|
||||||
|
mutable NzFrustumf m_frustum;
|
||||||
|
mutable NzMatrix4f m_projectionMatrix;
|
||||||
|
mutable NzMatrix4f m_viewMatrix;
|
||||||
|
NzRectf m_viewport;
|
||||||
|
NzVector3f m_upVector;
|
||||||
|
mutable bool m_frustumUpdated;
|
||||||
|
mutable bool m_projectionMatrixUpdated;
|
||||||
|
mutable bool m_viewMatrixUpdated;
|
||||||
|
mutable float m_aspectRatio;
|
||||||
|
float m_fov;
|
||||||
|
float m_zFar;
|
||||||
|
float m_zNear;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NAZARA_CAMERA_HPP
|
||||||
|
|
@ -18,10 +18,13 @@ enum nzLightType
|
||||||
|
|
||||||
enum nzSceneNodeType
|
enum nzSceneNodeType
|
||||||
{
|
{
|
||||||
|
nzSceneNodeType_Camera,
|
||||||
nzSceneNodeType_Light,
|
nzSceneNodeType_Light,
|
||||||
nzSceneNodeType_Model,
|
nzSceneNodeType_Model,
|
||||||
|
nzSceneNodeType_Root,
|
||||||
|
nzSceneNodeType_User,
|
||||||
|
|
||||||
nzSceneNodeType_Max = nzSceneNodeType_Model
|
nzSceneNodeType_Max = nzSceneNodeType_User
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // NAZARA_ENUMS_3D_HPP
|
#endif // NAZARA_ENUMS_3D_HPP
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@
|
||||||
#include <Nazara/3D/SceneNode.hpp>
|
#include <Nazara/3D/SceneNode.hpp>
|
||||||
#include <Nazara/Core/Color.hpp>
|
#include <Nazara/Core/Color.hpp>
|
||||||
|
|
||||||
|
class NzShader;
|
||||||
|
|
||||||
class NAZARA_API NzLight : public NzSceneNode
|
class NAZARA_API NzLight : public NzSceneNode
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -19,9 +21,11 @@ class NAZARA_API NzLight : public NzSceneNode
|
||||||
NzLight(const NzLight& light);
|
NzLight(const NzLight& light);
|
||||||
~NzLight();
|
~NzLight();
|
||||||
|
|
||||||
void Apply(unsigned int unit) const;
|
void AddToRenderQueue(NzRenderQueue& renderQueue) const;
|
||||||
|
|
||||||
const NzAxisAlignedBox& GetAABB() const;
|
void Apply(const NzShader* shader, unsigned int lightUnit) const;
|
||||||
|
|
||||||
|
const NzBoundingBoxf& GetBoundingBox() const;
|
||||||
NzColor GetAmbientColor() const;
|
NzColor GetAmbientColor() const;
|
||||||
float GetAttenuation() const;
|
float GetAttenuation() const;
|
||||||
NzColor GetDiffuseColor() const;
|
NzColor GetDiffuseColor() const;
|
||||||
|
|
@ -32,6 +36,8 @@ class NAZARA_API NzLight : public NzSceneNode
|
||||||
nzSceneNodeType GetSceneNodeType() const;
|
nzSceneNodeType GetSceneNodeType() const;
|
||||||
NzColor GetSpecularColor() const;
|
NzColor GetSpecularColor() const;
|
||||||
|
|
||||||
|
bool IsVisible(const NzFrustumf& frustum) const;
|
||||||
|
|
||||||
void SetAmbientColor(const NzColor& ambient);
|
void SetAmbientColor(const NzColor& ambient);
|
||||||
void SetAttenuation(float attenuation);
|
void SetAttenuation(float attenuation);
|
||||||
void SetDiffuseColor(const NzColor& diffuse);
|
void SetDiffuseColor(const NzColor& diffuse);
|
||||||
|
|
@ -43,10 +49,16 @@ class NAZARA_API NzLight : public NzSceneNode
|
||||||
NzLight& operator=(const NzLight& light);
|
NzLight& operator=(const NzLight& light);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void Register();
|
||||||
|
void Unregister();
|
||||||
|
void UpdateFrustum();
|
||||||
|
|
||||||
nzLightType m_type;
|
nzLightType m_type;
|
||||||
|
NzBoundingBoxf m_boundingBox;
|
||||||
NzColor m_ambientColor;
|
NzColor m_ambientColor;
|
||||||
NzColor m_diffuseColor;
|
NzColor m_diffuseColor;
|
||||||
NzColor m_specularColor;
|
NzColor m_specularColor;
|
||||||
|
bool m_boundingBoxUpdated;
|
||||||
float m_attenuation;
|
float m_attenuation;
|
||||||
float m_innerAngle;
|
float m_innerAngle;
|
||||||
float m_outerAngle;
|
float m_outerAngle;
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include <Nazara/Prerequesites.hpp>
|
#include <Nazara/Prerequesites.hpp>
|
||||||
#include <Nazara/3D/SceneNode.hpp>
|
#include <Nazara/3D/SceneNode.hpp>
|
||||||
|
#include <Nazara/Core/Updatable.hpp>
|
||||||
#include <Nazara/Renderer/Material.hpp>
|
#include <Nazara/Renderer/Material.hpp>
|
||||||
#include <Nazara/Utility/Animation.hpp>
|
#include <Nazara/Utility/Animation.hpp>
|
||||||
#include <Nazara/Utility/Mesh.hpp>
|
#include <Nazara/Utility/Mesh.hpp>
|
||||||
|
|
@ -21,18 +22,27 @@ struct NzModelParameters
|
||||||
NzMaterialParams materialParams;
|
NzMaterialParams materialParams;
|
||||||
};
|
};
|
||||||
|
|
||||||
class NAZARA_API NzModel : public NzSceneNode
|
class NAZARA_API NzModel : public NzSceneNode, public NzUpdatable
|
||||||
{
|
{
|
||||||
|
friend class NzScene;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
NzModel();
|
NzModel();
|
||||||
NzModel(const NzModel& model);
|
NzModel(const NzModel& model);
|
||||||
~NzModel();
|
~NzModel();
|
||||||
|
|
||||||
|
void AddToRenderQueue(NzRenderQueue& renderQueue) const;
|
||||||
|
void AdvanceAnimation(float elapsedTime);
|
||||||
|
|
||||||
|
void EnableAnimation(bool animation);
|
||||||
|
void EnableDraw(bool draw);
|
||||||
|
|
||||||
NzAnimation* GetAnimation() const;
|
NzAnimation* GetAnimation() const;
|
||||||
const NzAxisAlignedBox& GetAABB() const;
|
const NzBoundingBoxf& GetBoundingBox() const;
|
||||||
NzMaterial* GetMaterial(unsigned int matIndex) const;
|
NzMaterial* GetMaterial(unsigned int matIndex) const;
|
||||||
NzMaterial* GetMaterial(unsigned int skinIndex, unsigned int matIndex) const;
|
NzMaterial* GetMaterial(unsigned int skinIndex, unsigned int matIndex) const;
|
||||||
unsigned int GetMaterialCount() const;
|
unsigned int GetMaterialCount() const;
|
||||||
|
unsigned int GetSkin() const;
|
||||||
unsigned int GetSkinCount() const;
|
unsigned int GetSkinCount() const;
|
||||||
NzMesh* GetMesh() const;
|
NzMesh* GetMesh() const;
|
||||||
nzSceneNodeType GetSceneNodeType() const override;
|
nzSceneNodeType GetSceneNodeType() const override;
|
||||||
|
|
@ -41,6 +51,10 @@ class NAZARA_API NzModel : public NzSceneNode
|
||||||
|
|
||||||
bool HasAnimation() const;
|
bool HasAnimation() const;
|
||||||
|
|
||||||
|
bool IsAnimationEnabled() const;
|
||||||
|
bool IsDrawEnabled() const;
|
||||||
|
bool IsVisible(const NzFrustumf& frustum) const override;
|
||||||
|
|
||||||
bool LoadFromFile(const NzString& meshPath, const NzMeshParams& meshParameters = NzMeshParams(), const NzModelParameters& modelParameters = NzModelParameters());
|
bool LoadFromFile(const NzString& meshPath, const NzMeshParams& meshParameters = NzMeshParams(), const NzModelParameters& modelParameters = NzModelParameters());
|
||||||
bool LoadFromMemory(const void* data, std::size_t size, const NzMeshParams& meshParameters = NzMeshParams(), const NzModelParameters& modelParameters = NzModelParameters());
|
bool LoadFromMemory(const void* data, std::size_t size, const NzMeshParams& meshParameters = NzMeshParams(), const NzModelParameters& modelParameters = NzModelParameters());
|
||||||
bool LoadFromStream(NzInputStream& stream, const NzMeshParams& meshParameters = NzMeshParams(), const NzModelParameters& modelParameters = NzModelParameters());
|
bool LoadFromStream(NzInputStream& stream, const NzMeshParams& meshParameters = NzMeshParams(), const NzModelParameters& modelParameters = NzModelParameters());
|
||||||
|
|
@ -51,22 +65,32 @@ class NAZARA_API NzModel : public NzSceneNode
|
||||||
void SetMaterial(unsigned int matIndex, NzMaterial* material);
|
void SetMaterial(unsigned int matIndex, NzMaterial* material);
|
||||||
void SetMaterial(unsigned int skinIndex, unsigned int matIndex, NzMaterial* material);
|
void SetMaterial(unsigned int skinIndex, unsigned int matIndex, NzMaterial* material);
|
||||||
void SetMesh(NzMesh* mesh, const NzModelParameters& parameters = NzModelParameters());
|
void SetMesh(NzMesh* mesh, const NzModelParameters& parameters = NzModelParameters());
|
||||||
|
void SetSkin(unsigned int skin);
|
||||||
void SetSkinCount(unsigned int skinCount);
|
void SetSkinCount(unsigned int skinCount);
|
||||||
bool SetSequence(const NzString& sequenceName);
|
bool SetSequence(const NzString& sequenceName);
|
||||||
void SetSequence(unsigned int sequenceIndex);
|
void SetSequence(unsigned int sequenceIndex);
|
||||||
|
|
||||||
void Update(float elapsedTime);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void Invalidate() override;
|
||||||
|
void Register() override;
|
||||||
|
void Unregister() override;
|
||||||
|
void Update() override;
|
||||||
|
void UpdateBoundingBox() const;
|
||||||
|
|
||||||
std::vector<NzMaterial*> m_materials;
|
std::vector<NzMaterial*> m_materials;
|
||||||
|
mutable NzBoundingBoxf m_boundingBox;
|
||||||
NzSkeleton m_skeleton; // Uniquement pour les animations squelettiques
|
NzSkeleton m_skeleton; // Uniquement pour les animations squelettiques
|
||||||
NzAnimation* m_animation;
|
NzAnimation* m_animation;
|
||||||
NzMesh* m_mesh;
|
NzMesh* m_mesh;
|
||||||
const NzSequence* m_currentSequence;
|
const NzSequence* m_currentSequence;
|
||||||
|
bool m_animationEnabled;
|
||||||
|
mutable bool m_boundingBoxUpdated;
|
||||||
|
bool m_drawEnabled;
|
||||||
float m_interpolation;
|
float m_interpolation;
|
||||||
unsigned int m_currentFrame;
|
unsigned int m_currentFrame;
|
||||||
unsigned int m_matCount;
|
unsigned int m_matCount;
|
||||||
unsigned int m_nextFrame;
|
unsigned int m_nextFrame;
|
||||||
|
unsigned int m_skin;
|
||||||
unsigned int m_skinCount;
|
unsigned int m_skinCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
// Copyright (C) 2012 Jérôme Leclercq
|
||||||
|
// This file is part of the "Nazara Engine - 3D Module"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#ifndef NAZARA_RENDERQUEUE_HPP
|
||||||
|
#define NAZARA_RENDERQUEUE_HPP
|
||||||
|
|
||||||
|
#include <Nazara/Prerequesites.hpp>
|
||||||
|
#include <Nazara/Math/Matrix4.hpp>
|
||||||
|
#include <Nazara/Utility/Mesh.hpp>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class NzDrawable;
|
||||||
|
class NzLight;
|
||||||
|
class NzMaterial;
|
||||||
|
class NzModel;
|
||||||
|
class NzSkeletalMesh;
|
||||||
|
class NzStaticMesh;
|
||||||
|
|
||||||
|
class NAZARA_API NzRenderQueue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct MaterialComparator
|
||||||
|
{
|
||||||
|
bool operator()(const NzMaterial* mat1, const NzMaterial* mat2);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SkeletalMeshComparator
|
||||||
|
{
|
||||||
|
bool operator()(const NzSkeletalMesh* subMesh1, const NzSkeletalMesh* subMesh2);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StaticMeshComparator
|
||||||
|
{
|
||||||
|
bool operator()(const NzStaticMesh* subMesh1, const NzStaticMesh* subMesh2);
|
||||||
|
};
|
||||||
|
|
||||||
|
NzRenderQueue() = default;
|
||||||
|
~NzRenderQueue() = default;
|
||||||
|
|
||||||
|
void Clear();
|
||||||
|
|
||||||
|
struct SkeletalData
|
||||||
|
{
|
||||||
|
NzMatrix4f transformMatrix;
|
||||||
|
|
||||||
|
///TODO: Déplacer vers un container séparé qui ne serait pas sujer à Clear();
|
||||||
|
std::vector<NzMeshVertex> skinnedVertices;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::map<NzSkeletalMesh*, std::vector<SkeletalData>, SkeletalMeshComparator> SkeletalMeshContainer;
|
||||||
|
typedef std::map<NzStaticMesh*, std::vector<NzMatrix4f>, StaticMeshComparator> StaticMeshContainer;
|
||||||
|
|
||||||
|
std::map<NzMaterial*, SkeletalMeshContainer, MaterialComparator> visibleSkeletalModels;
|
||||||
|
std::map<NzMaterial*, StaticMeshContainer, MaterialComparator> visibleStaticModels;
|
||||||
|
std::vector<const NzDrawable*> otherDrawables;
|
||||||
|
std::vector<const NzLight*> directionnalLights;
|
||||||
|
std::vector<const NzLight*> visibleLights;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NAZARA_RENDERQUEUE_HPP
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
// Copyright (C) 2012 Jérôme Leclercq
|
||||||
|
// This file is part of the "Nazara Engine - 3D Module"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef NAZARA_SCENE_HPP
|
||||||
|
#define NAZARA_SCENE_HPP
|
||||||
|
|
||||||
|
#include <Nazara/Prerequesites.hpp>
|
||||||
|
#include <Nazara/Core/Color.hpp>
|
||||||
|
#include <Nazara/Core/Updatable.hpp>
|
||||||
|
|
||||||
|
class NzCamera;
|
||||||
|
class NzLight;
|
||||||
|
class NzModel;
|
||||||
|
class NzSceneNode;
|
||||||
|
struct NzSceneImpl;
|
||||||
|
|
||||||
|
class NAZARA_API NzScene
|
||||||
|
{
|
||||||
|
friend NzCamera;
|
||||||
|
|
||||||
|
public:
|
||||||
|
NzScene();
|
||||||
|
~NzScene();
|
||||||
|
|
||||||
|
void Cull();
|
||||||
|
void Draw();
|
||||||
|
|
||||||
|
NzSceneNode& GetRoot() const;
|
||||||
|
float GetUpdateTime() const;
|
||||||
|
unsigned int GetUpdatePerSecond() const;
|
||||||
|
|
||||||
|
void RegisterForUpdate(NzUpdatable* node);
|
||||||
|
|
||||||
|
void SetAmbientColor(const NzColor& color);
|
||||||
|
void SetUpdatePerSecond(unsigned int updatePerSecond);
|
||||||
|
|
||||||
|
void UnregisterForUpdate(NzUpdatable* node);
|
||||||
|
|
||||||
|
void Update();
|
||||||
|
void UpdateVisible();
|
||||||
|
|
||||||
|
operator const NzSceneNode&() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void SetActiveCamera(const NzCamera* camera);
|
||||||
|
|
||||||
|
NzSceneImpl* m_impl;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NAZARA_SCENE_HPP
|
||||||
|
|
@ -9,16 +9,39 @@
|
||||||
|
|
||||||
#include <Nazara/Prerequesites.hpp>
|
#include <Nazara/Prerequesites.hpp>
|
||||||
#include <Nazara/3D/Enums.hpp>
|
#include <Nazara/3D/Enums.hpp>
|
||||||
#include <Nazara/Utility/AxisAlignedBox.hpp>
|
#include <Nazara/3D/RenderQueue.hpp>
|
||||||
|
#include <Nazara/3D/Scene.hpp>
|
||||||
|
#include <Nazara/Math/BoundingBox.hpp>
|
||||||
|
#include <Nazara/Math/Frustum.hpp>
|
||||||
#include <Nazara/Utility/Node.hpp>
|
#include <Nazara/Utility/Node.hpp>
|
||||||
|
|
||||||
class NzSceneNode : public NzNode
|
class NAZARA_API NzSceneNode : public NzNode
|
||||||
{
|
{
|
||||||
|
friend class NzScene;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
NzSceneNode();
|
||||||
|
NzSceneNode(const NzSceneNode& node);
|
||||||
virtual ~NzSceneNode();
|
virtual ~NzSceneNode();
|
||||||
|
|
||||||
virtual const NzAxisAlignedBox& GetAABB() const = 0;
|
virtual void AddToRenderQueue(NzRenderQueue& renderQueue) const = 0;
|
||||||
|
|
||||||
|
virtual const NzBoundingBoxf& GetBoundingBox() const = 0;
|
||||||
|
nzNodeType GetNodeType() const final;
|
||||||
|
NzScene* GetScene() const;
|
||||||
virtual nzSceneNodeType GetSceneNodeType() const = 0;
|
virtual nzSceneNodeType GetSceneNodeType() const = 0;
|
||||||
|
|
||||||
|
virtual bool IsVisible(const NzFrustumf& frustum) const = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void OnParenting(const NzNode* parent) override;
|
||||||
|
virtual void Register();
|
||||||
|
void SetScene(NzScene* scene);
|
||||||
|
virtual bool ShouldUpdateWhenVisible();
|
||||||
|
virtual void Unregister();
|
||||||
|
virtual void Update();
|
||||||
|
|
||||||
|
NzScene* m_scene;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // NAZARA_SCENENODE_HPP
|
#endif // NAZARA_SCENENODE_HPP
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
// Copyright (C) 2012 Jérôme Leclercq
|
||||||
|
// This file is part of the "Nazara Engine - 3D Module"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef NAZARA_SCENEROOT_HPP
|
||||||
|
#define NAZARA_SCENEROOT_HPP
|
||||||
|
|
||||||
|
#include <Nazara/Prerequesites.hpp>
|
||||||
|
#include <Nazara/3D/SceneNode.hpp>
|
||||||
|
|
||||||
|
struct NzSceneImpl;
|
||||||
|
|
||||||
|
class NAZARA_API NzSceneRoot : public NzSceneNode
|
||||||
|
{
|
||||||
|
friend struct NzSceneImpl;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void AddToRenderQueue(NzRenderQueue& renderQueue) const override;
|
||||||
|
|
||||||
|
const NzBoundingBoxf& GetBoundingBox() const override;
|
||||||
|
nzSceneNodeType GetSceneNodeType() const override;
|
||||||
|
|
||||||
|
bool IsVisible(const NzFrustumf& frustum) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
NzSceneRoot(NzScene* scene);
|
||||||
|
virtual ~NzSceneRoot();
|
||||||
|
|
||||||
|
void Register();
|
||||||
|
void Unregister();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NAZARA_SCENEROOT_HPP
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
// 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/3D/3D.hpp>
|
#include <Nazara/3D/3D.hpp>
|
||||||
|
#include <Nazara/2D/2D.hpp>
|
||||||
#include <Nazara/3D/Config.hpp>
|
#include <Nazara/3D/Config.hpp>
|
||||||
#include <Nazara/Core/Error.hpp>
|
#include <Nazara/Core/Error.hpp>
|
||||||
#include <Nazara/Core/Log.hpp>
|
#include <Nazara/Core/Log.hpp>
|
||||||
|
|
@ -15,9 +16,9 @@ bool Nz3D::Initialize()
|
||||||
return true; // Déjà initialisé
|
return true; // Déjà initialisé
|
||||||
|
|
||||||
// Initialisation des dépendances
|
// Initialisation des dépendances
|
||||||
if (!NzRenderer::Initialize())
|
if (!Nz2D::Initialize())
|
||||||
{
|
{
|
||||||
NazaraError("Failed to initialize renderer module");
|
NazaraError("Failed to initialize 2D module");
|
||||||
Uninitialize();
|
Uninitialize();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -52,7 +53,7 @@ void Nz3D::Uninitialize()
|
||||||
NazaraNotice("Uninitialized: 3D module");
|
NazaraNotice("Uninitialized: 3D module");
|
||||||
|
|
||||||
// Libération des dépendances
|
// Libération des dépendances
|
||||||
NzRenderer::Uninitialize();
|
Nz2D::Uninitialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int Nz3D::s_moduleReferenceCounter = 0;
|
unsigned int Nz3D::s_moduleReferenceCounter = 0;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,243 @@
|
||||||
|
// Copyright (C) 2012 Jérôme Leclercq
|
||||||
|
// This file is part of the "Nazara Engine - 3D Module"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#include <Nazara/3D/Camera.hpp>
|
||||||
|
#include <Nazara/3D/Scene.hpp>
|
||||||
|
#include <Nazara/Renderer/Renderer.hpp>
|
||||||
|
#include <Nazara/Renderer/RenderTarget.hpp>
|
||||||
|
#include <Nazara/3D/Debug.hpp>
|
||||||
|
|
||||||
|
NzCamera::NzCamera() :
|
||||||
|
m_viewport(0.f, 0.f, 1.f, 1.f),
|
||||||
|
m_upVector(NzVector3f::Up()),
|
||||||
|
m_frustumUpdated(false),
|
||||||
|
m_projectionMatrixUpdated(false),
|
||||||
|
m_viewMatrixUpdated(false),
|
||||||
|
m_aspectRatio(0.f),
|
||||||
|
m_fov(70.f),
|
||||||
|
m_zFar(100.f),
|
||||||
|
m_zNear(1.f)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
NzCamera::~NzCamera() = default;
|
||||||
|
|
||||||
|
void NzCamera::Activate() const
|
||||||
|
{
|
||||||
|
NzRenderTarget* renderTarget = NzRenderer::GetTarget();
|
||||||
|
|
||||||
|
#ifdef NAZARA_3D_SAFE
|
||||||
|
if (!renderTarget)
|
||||||
|
{
|
||||||
|
NazaraError("No render target !");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
unsigned int width = renderTarget->GetWidth();
|
||||||
|
unsigned int height = std::max(renderTarget->GetHeight(), 1U);
|
||||||
|
|
||||||
|
float vWidth = width * m_viewport.width;
|
||||||
|
float vHeight = height * m_viewport.height;
|
||||||
|
|
||||||
|
NzRectui viewport;
|
||||||
|
viewport.x = width * m_viewport.x;
|
||||||
|
viewport.y = height * m_viewport.x;
|
||||||
|
viewport.width = vWidth;
|
||||||
|
viewport.height = height * m_viewport.height;
|
||||||
|
NzRenderer::SetViewport(viewport);
|
||||||
|
|
||||||
|
float aspectRatio = vWidth/vHeight;
|
||||||
|
|
||||||
|
if (!NzNumberEquals(m_aspectRatio, aspectRatio))
|
||||||
|
{
|
||||||
|
m_aspectRatio = aspectRatio;
|
||||||
|
m_frustumUpdated = false;
|
||||||
|
m_projectionMatrixUpdated = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_projectionMatrixUpdated)
|
||||||
|
UpdateProjectionMatrix();
|
||||||
|
|
||||||
|
if (!m_viewMatrixUpdated)
|
||||||
|
UpdateViewMatrix();
|
||||||
|
|
||||||
|
NzRenderer::SetMatrix(nzMatrixType_Projection, m_projectionMatrix);
|
||||||
|
NzRenderer::SetMatrix(nzMatrixType_View, m_viewMatrix);
|
||||||
|
|
||||||
|
if (m_scene)
|
||||||
|
m_scene->SetActiveCamera(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzCamera::EnsureFrustumUpdate() const
|
||||||
|
{
|
||||||
|
if (!m_frustumUpdated)
|
||||||
|
UpdateFrustum();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzCamera::EnsureProjectionMatrixUpdate() const
|
||||||
|
{
|
||||||
|
if (!m_projectionMatrixUpdated)
|
||||||
|
UpdateProjectionMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzCamera::EnsureViewMatrixUpdate() const
|
||||||
|
{
|
||||||
|
if (!m_viewMatrixUpdated)
|
||||||
|
UpdateViewMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
float NzCamera::GetAspectRatio() const
|
||||||
|
{
|
||||||
|
return m_aspectRatio;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NzBoundingBoxf& NzCamera::GetBoundingBox() const
|
||||||
|
{
|
||||||
|
///TODO: Remplacer par la bounding box du Frustum ?
|
||||||
|
static NzBoundingBoxf dummy(nzExtend_Null);
|
||||||
|
return dummy;
|
||||||
|
}
|
||||||
|
|
||||||
|
float NzCamera::GetFOV() const
|
||||||
|
{
|
||||||
|
return m_fov;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NzFrustumf& NzCamera::GetFrustum() const
|
||||||
|
{
|
||||||
|
if (!m_frustumUpdated)
|
||||||
|
UpdateFrustum();
|
||||||
|
|
||||||
|
return m_frustum;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NzMatrix4f& NzCamera::GetProjectionMatrix() const
|
||||||
|
{
|
||||||
|
if (!m_projectionMatrixUpdated)
|
||||||
|
UpdateProjectionMatrix();
|
||||||
|
|
||||||
|
return m_projectionMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
nzSceneNodeType NzCamera::GetSceneNodeType() const
|
||||||
|
{
|
||||||
|
return nzSceneNodeType_Camera;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NzVector3f& NzCamera::GetUpVector() const
|
||||||
|
{
|
||||||
|
return m_upVector;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NzMatrix4f& NzCamera::GetViewMatrix() const
|
||||||
|
{
|
||||||
|
if (!m_viewMatrixUpdated)
|
||||||
|
UpdateViewMatrix();
|
||||||
|
|
||||||
|
return m_viewMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NzRectf& NzCamera::GetViewport() const
|
||||||
|
{
|
||||||
|
return m_viewport;
|
||||||
|
}
|
||||||
|
|
||||||
|
float NzCamera::GetZFar() const
|
||||||
|
{
|
||||||
|
return m_zFar;
|
||||||
|
}
|
||||||
|
|
||||||
|
float NzCamera::GetZNear() const
|
||||||
|
{
|
||||||
|
return m_zNear;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzCamera::SetFOV(float fov)
|
||||||
|
{
|
||||||
|
m_fov = fov;
|
||||||
|
|
||||||
|
m_frustumUpdated = false;
|
||||||
|
m_projectionMatrixUpdated= false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzCamera::SetUpVector(const NzVector3f& upVector)
|
||||||
|
{
|
||||||
|
m_upVector = upVector;
|
||||||
|
|
||||||
|
m_frustumUpdated = false;
|
||||||
|
m_viewMatrixUpdated = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzCamera::SetViewport(const NzRectf& viewport)
|
||||||
|
{
|
||||||
|
m_viewport = viewport;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzCamera::SetZFar(float zFar)
|
||||||
|
{
|
||||||
|
m_zFar = zFar;
|
||||||
|
|
||||||
|
m_frustumUpdated = false;
|
||||||
|
m_projectionMatrixUpdated = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzCamera::SetZNear(float zNear)
|
||||||
|
{
|
||||||
|
m_zNear = zNear;
|
||||||
|
|
||||||
|
m_frustumUpdated = false;
|
||||||
|
m_projectionMatrixUpdated = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzCamera::AddToRenderQueue(NzRenderQueue& renderQueue) const
|
||||||
|
{
|
||||||
|
NazaraUnused(renderQueue);
|
||||||
|
|
||||||
|
NazaraInternalError("SceneNode::AddToRenderQueue() called on SceneRoot");
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzCamera::Invalidate()
|
||||||
|
{
|
||||||
|
NzSceneNode::Invalidate();
|
||||||
|
|
||||||
|
m_frustumUpdated = false;
|
||||||
|
m_viewMatrixUpdated = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NzCamera::IsVisible(const NzFrustumf& frustum) const
|
||||||
|
{
|
||||||
|
NazaraUnused(frustum);
|
||||||
|
//NazaraInternalError("SceneNode::IsVisible() called on Camera");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzCamera::Register()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzCamera::Unregister()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzCamera::UpdateFrustum() const
|
||||||
|
{
|
||||||
|
m_frustum.Build(m_fov, m_aspectRatio, m_zNear, m_zFar, m_derivedPosition, m_derivedPosition + m_derivedRotation*NzVector3f::Forward(), m_upVector);
|
||||||
|
m_frustumUpdated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzCamera::UpdateProjectionMatrix() const
|
||||||
|
{
|
||||||
|
m_projectionMatrix.MakePerspective(m_fov, m_aspectRatio, m_zNear, m_zFar);
|
||||||
|
m_projectionMatrixUpdated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzCamera::UpdateViewMatrix() const
|
||||||
|
{
|
||||||
|
if (!m_derivedUpdated)
|
||||||
|
UpdateDerived();
|
||||||
|
|
||||||
|
m_viewMatrix.MakeLookAt(m_derivedPosition, m_derivedPosition + m_derivedRotation*NzVector3f::Forward(), m_upVector);
|
||||||
|
m_viewMatrixUpdated = true;
|
||||||
|
}
|
||||||
|
|
@ -17,6 +17,7 @@ m_type(type),
|
||||||
m_ambientColor((type == nzLightType_Directional) ? NzColor(50, 50, 50) : NzColor::Black),
|
m_ambientColor((type == nzLightType_Directional) ? NzColor(50, 50, 50) : NzColor::Black),
|
||||||
m_diffuseColor(NzColor::White),
|
m_diffuseColor(NzColor::White),
|
||||||
m_specularColor(NzColor::White),
|
m_specularColor(NzColor::White),
|
||||||
|
m_boundingBoxUpdated(false),
|
||||||
m_attenuation(0.9f),
|
m_attenuation(0.9f),
|
||||||
m_innerAngle(15.f),
|
m_innerAngle(15.f),
|
||||||
m_outerAngle(45.f),
|
m_outerAngle(45.f),
|
||||||
|
|
@ -24,14 +25,32 @@ m_radius(500.f)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
NzLight::NzLight(const NzLight& light)
|
NzLight::NzLight(const NzLight& light) :
|
||||||
|
NzSceneNode(light)
|
||||||
{
|
{
|
||||||
std::memcpy(this, &light, sizeof(NzLight)); // Aussi simple que ça
|
std::memcpy(this, &light, sizeof(NzLight)); // Aussi simple que ça
|
||||||
}
|
}
|
||||||
|
|
||||||
NzLight::~NzLight() = default;
|
NzLight::~NzLight()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void NzLight::Apply(unsigned int unit) const
|
void NzLight::AddToRenderQueue(NzRenderQueue& renderQueue) const
|
||||||
|
{
|
||||||
|
switch (m_type)
|
||||||
|
{
|
||||||
|
case nzLightType_Directional:
|
||||||
|
renderQueue.directionnalLights.push_back(this);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case nzLightType_Point:
|
||||||
|
case nzLightType_Spot:
|
||||||
|
renderQueue.visibleLights.push_back(this);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzLight::Apply(const NzShader* shader, unsigned int lightUnit) const
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
struct Light
|
struct Light
|
||||||
|
|
@ -58,7 +77,6 @@ void NzLight::Apply(unsigned int unit) const
|
||||||
-P2: vec3 direction + float invRadius
|
-P2: vec3 direction + float invRadius
|
||||||
-P3: float cosInnerAngle + float cosOuterAngle
|
-P3: float cosInnerAngle + float cosOuterAngle
|
||||||
*/
|
*/
|
||||||
const NzShader* shader = NzRenderer::GetShader();
|
|
||||||
|
|
||||||
int typeLocation = shader->GetUniformLocation("Lights[0].type");
|
int typeLocation = shader->GetUniformLocation("Lights[0].type");
|
||||||
int ambientLocation = shader->GetUniformLocation("Lights[0].ambient");
|
int ambientLocation = shader->GetUniformLocation("Lights[0].ambient");
|
||||||
|
|
@ -68,10 +86,10 @@ void NzLight::Apply(unsigned int unit) const
|
||||||
int parameters2Location = shader->GetUniformLocation("Lights[0].parameters2");
|
int parameters2Location = shader->GetUniformLocation("Lights[0].parameters2");
|
||||||
int parameters3Location = shader->GetUniformLocation("Lights[0].parameters3");
|
int parameters3Location = shader->GetUniformLocation("Lights[0].parameters3");
|
||||||
|
|
||||||
if (unit > 0)
|
if (lightUnit > 0)
|
||||||
{
|
{
|
||||||
int type2Location = shader->GetUniformLocation("Lights[1].type");
|
int type2Location = shader->GetUniformLocation("Lights[1].type");
|
||||||
int offset = unit * (type2Location - typeLocation); // type2Location - typeLocation donne la taille de la structure
|
int offset = lightUnit * (type2Location - typeLocation); // type2Location - typeLocation donne la taille de la structure
|
||||||
|
|
||||||
// On applique cet offset
|
// On applique cet offset
|
||||||
typeLocation += offset;
|
typeLocation += offset;
|
||||||
|
|
@ -110,9 +128,10 @@ void NzLight::Apply(unsigned int unit) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const NzAxisAlignedBox& NzLight::GetAABB() const
|
const NzBoundingBoxf& NzLight::GetBoundingBox() const
|
||||||
{
|
{
|
||||||
return NzAxisAlignedBox::Null;
|
static NzBoundingBoxf dummy(nzExtend_Null);
|
||||||
|
return dummy;
|
||||||
}
|
}
|
||||||
|
|
||||||
NzColor NzLight::GetAmbientColor() const
|
NzColor NzLight::GetAmbientColor() const
|
||||||
|
|
@ -160,6 +179,14 @@ NzColor NzLight::GetSpecularColor() const
|
||||||
return m_specularColor;
|
return m_specularColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool NzLight::IsVisible(const NzFrustumf& frustum) const
|
||||||
|
{
|
||||||
|
NazaraUnused(frustum);
|
||||||
|
|
||||||
|
///FIXME: Pour l'instant toujours visible
|
||||||
|
return true; // Toujours visible
|
||||||
|
}
|
||||||
|
|
||||||
void NzLight::SetAmbientColor(const NzColor& ambient)
|
void NzLight::SetAmbientColor(const NzColor& ambient)
|
||||||
{
|
{
|
||||||
m_ambientColor = ambient;
|
m_ambientColor = ambient;
|
||||||
|
|
@ -201,3 +228,11 @@ NzLight& NzLight::operator=(const NzLight& light)
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NzLight::Register()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzLight::Unregister()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,24 +4,40 @@
|
||||||
|
|
||||||
#include <Nazara/3D/Model.hpp>
|
#include <Nazara/3D/Model.hpp>
|
||||||
#include <Nazara/3D/Config.hpp>
|
#include <Nazara/3D/Config.hpp>
|
||||||
|
#include <Nazara/Utility/SkeletalMesh.hpp>
|
||||||
|
#include <Nazara/Utility/StaticMesh.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <tuple>
|
||||||
#include <Nazara/3D/Debug.hpp>
|
#include <Nazara/3D/Debug.hpp>
|
||||||
|
|
||||||
NzModel::NzModel() :
|
NzModel::NzModel() :
|
||||||
m_animation(nullptr),
|
m_animation(nullptr),
|
||||||
m_mesh(nullptr),
|
m_mesh(nullptr),
|
||||||
m_currentSequence(nullptr),
|
m_currentSequence(nullptr),
|
||||||
|
m_animationEnabled(true),
|
||||||
|
m_boundingBoxUpdated(false),
|
||||||
|
m_drawEnabled(true),
|
||||||
m_matCount(0),
|
m_matCount(0),
|
||||||
m_skinCount(0)
|
m_skin(0),
|
||||||
|
m_skinCount(1)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
NzModel::NzModel(const NzModel& model) :
|
NzModel::NzModel(const NzModel& model) :
|
||||||
|
NzSceneNode(model),
|
||||||
m_materials(model.m_materials),
|
m_materials(model.m_materials),
|
||||||
|
m_boundingBox(model.m_boundingBox),
|
||||||
m_animation(model.m_animation),
|
m_animation(model.m_animation),
|
||||||
m_mesh(model.m_mesh),
|
m_mesh(model.m_mesh),
|
||||||
m_currentSequence(model.m_currentSequence),
|
m_currentSequence(model.m_currentSequence),
|
||||||
|
m_animationEnabled(model.m_animationEnabled),
|
||||||
|
m_boundingBoxUpdated(model.m_boundingBoxUpdated),
|
||||||
|
m_drawEnabled(model.m_drawEnabled),
|
||||||
|
m_interpolation(model.m_interpolation),
|
||||||
|
m_currentFrame(model.m_currentFrame),
|
||||||
m_matCount(model.m_matCount),
|
m_matCount(model.m_matCount),
|
||||||
|
m_nextFrame(model.m_nextFrame),
|
||||||
|
m_skin(model.m_skin),
|
||||||
m_skinCount(model.m_skinCount)
|
m_skinCount(model.m_skinCount)
|
||||||
{
|
{
|
||||||
if (m_mesh)
|
if (m_mesh)
|
||||||
|
|
@ -31,6 +47,9 @@ m_skinCount(model.m_skinCount)
|
||||||
|
|
||||||
m_mesh->AddResourceReference();
|
m_mesh->AddResourceReference();
|
||||||
|
|
||||||
|
if (m_mesh->GetAnimationType() == nzAnimationType_Skeletal)
|
||||||
|
m_skeleton = model.m_skeleton;
|
||||||
|
|
||||||
// Nous n'avons des matériaux que si nous avons un mesh
|
// Nous n'avons des matériaux que si nous avons un mesh
|
||||||
for (const NzMaterial* material : m_materials)
|
for (const NzMaterial* material : m_materials)
|
||||||
material->AddResourceReference();
|
material->AddResourceReference();
|
||||||
|
|
@ -42,22 +61,119 @@ NzModel::~NzModel()
|
||||||
Reset();
|
Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NzModel::AddToRenderQueue(NzRenderQueue& renderQueue) const
|
||||||
|
{
|
||||||
|
if (!m_transformMatrixUpdated)
|
||||||
|
UpdateTransformMatrix();
|
||||||
|
|
||||||
|
unsigned int subMeshCount = m_mesh->GetSubMeshCount();
|
||||||
|
for (unsigned int i = 0; i < subMeshCount; ++i)
|
||||||
|
{
|
||||||
|
NzSubMesh* subMesh = m_mesh->GetSubMesh(i);
|
||||||
|
NzMaterial* material = m_materials[m_skin*m_matCount + subMesh->GetMaterialIndex()];
|
||||||
|
|
||||||
|
switch (subMesh->GetAnimationType())
|
||||||
|
{
|
||||||
|
case nzAnimationType_Skeletal:
|
||||||
|
{
|
||||||
|
NzSkeletalMesh* skeletalMesh = static_cast<NzSkeletalMesh*>(subMesh);
|
||||||
|
std::vector<NzRenderQueue::SkeletalData>& data = renderQueue.visibleSkeletalModels[material][skeletalMesh];
|
||||||
|
|
||||||
|
///TODO: Corriger cette abomination
|
||||||
|
data.resize(data.size()+1);
|
||||||
|
NzRenderQueue::SkeletalData& skeletalData = data.back();
|
||||||
|
|
||||||
|
skeletalData.skinnedVertices.resize(skeletalMesh->GetVertexCount());
|
||||||
|
skeletalData.transformMatrix = m_transformMatrix;
|
||||||
|
|
||||||
|
skeletalMesh->Skin(&skeletalData.skinnedVertices[0], &m_skeleton);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case nzAnimationType_Static:
|
||||||
|
{
|
||||||
|
NzStaticMesh* staticMesh = static_cast<NzStaticMesh*>(subMesh);
|
||||||
|
std::vector<NzMatrix4f>& matrices = renderQueue.visibleStaticModels[material][staticMesh];
|
||||||
|
|
||||||
|
matrices.push_back(m_transformMatrix);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzModel::AdvanceAnimation(float elapsedTime)
|
||||||
|
{
|
||||||
|
#if NAZARA_3D_SAFE
|
||||||
|
if (!m_animation)
|
||||||
|
{
|
||||||
|
NazaraError("Model has no animation");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
m_interpolation += m_currentSequence->frameRate * elapsedTime;
|
||||||
|
while (m_interpolation > 1.f)
|
||||||
|
{
|
||||||
|
m_interpolation -= 1.f;
|
||||||
|
|
||||||
|
unsigned lastFrame = m_currentSequence->firstFrame + m_currentSequence->frameCount - 1;
|
||||||
|
if (m_nextFrame+1 > lastFrame)
|
||||||
|
{
|
||||||
|
if (m_animation->IsLoopPointInterpolationEnabled())
|
||||||
|
{
|
||||||
|
m_currentFrame = m_nextFrame;
|
||||||
|
m_nextFrame = m_currentSequence->firstFrame;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_currentFrame = m_currentSequence->firstFrame;
|
||||||
|
m_nextFrame = m_currentFrame+1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_currentFrame = m_nextFrame;
|
||||||
|
m_nextFrame++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_animation->AnimateSkeleton(&m_skeleton, m_currentFrame, m_nextFrame, m_interpolation);
|
||||||
|
m_boundingBox.MakeNull();
|
||||||
|
m_boundingBoxUpdated = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzModel::EnableAnimation(bool animation)
|
||||||
|
{
|
||||||
|
m_animationEnabled = animation;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzModel::EnableDraw(bool draw)
|
||||||
|
{
|
||||||
|
m_drawEnabled = draw;
|
||||||
|
}
|
||||||
|
|
||||||
NzAnimation* NzModel::GetAnimation() const
|
NzAnimation* NzModel::GetAnimation() const
|
||||||
{
|
{
|
||||||
return m_animation;
|
return m_animation;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NzAxisAlignedBox& NzModel::GetAABB() const
|
const NzBoundingBoxf& NzModel::GetBoundingBox() const
|
||||||
{
|
{
|
||||||
#if NAZARA_3D_SAFE
|
#if NAZARA_3D_SAFE
|
||||||
if (!m_mesh)
|
if (!m_mesh)
|
||||||
{
|
{
|
||||||
NazaraError("Model has no mesh");
|
NazaraError("Model has no mesh");
|
||||||
return NzAxisAlignedBox::Null;
|
|
||||||
|
static NzBoundingBoxf dummy(nzExtend_Null);
|
||||||
|
return dummy;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return m_mesh->GetAABB();
|
if (!m_boundingBoxUpdated)
|
||||||
|
UpdateBoundingBox();
|
||||||
|
|
||||||
|
return m_boundingBox;
|
||||||
}
|
}
|
||||||
|
|
||||||
NzMaterial* NzModel::GetMaterial(unsigned int matIndex) const
|
NzMaterial* NzModel::GetMaterial(unsigned int matIndex) const
|
||||||
|
|
@ -70,7 +186,7 @@ NzMaterial* NzModel::GetMaterial(unsigned int matIndex) const
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return m_materials[matIndex];
|
return m_materials[m_skin*m_matCount + matIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
NzMaterial* NzModel::GetMaterial(unsigned int skinIndex, unsigned int matIndex) const
|
NzMaterial* NzModel::GetMaterial(unsigned int skinIndex, unsigned int matIndex) const
|
||||||
|
|
@ -89,7 +205,7 @@ NzMaterial* NzModel::GetMaterial(unsigned int skinIndex, unsigned int matIndex)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return m_materials[matIndex];
|
return m_materials[skinIndex*m_matCount + matIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int NzModel::GetMaterialCount() const
|
unsigned int NzModel::GetMaterialCount() const
|
||||||
|
|
@ -97,11 +213,6 @@ unsigned int NzModel::GetMaterialCount() const
|
||||||
return m_matCount;
|
return m_matCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int NzModel::GetSkinCount() const
|
|
||||||
{
|
|
||||||
return m_skinCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
NzMesh* NzModel::GetMesh() const
|
NzMesh* NzModel::GetMesh() const
|
||||||
{
|
{
|
||||||
return m_mesh;
|
return m_mesh;
|
||||||
|
|
@ -122,11 +233,50 @@ const NzSkeleton* NzModel::GetSkeleton() const
|
||||||
return &m_skeleton;
|
return &m_skeleton;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned int NzModel::GetSkin() const
|
||||||
|
{
|
||||||
|
return m_skin;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int NzModel::GetSkinCount() const
|
||||||
|
{
|
||||||
|
return m_skinCount;
|
||||||
|
}
|
||||||
|
|
||||||
bool NzModel::HasAnimation() const
|
bool NzModel::HasAnimation() const
|
||||||
{
|
{
|
||||||
return m_animation != nullptr;
|
return m_animation != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool NzModel::IsAnimationEnabled() const
|
||||||
|
{
|
||||||
|
return m_animationEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NzModel::IsDrawEnabled() const
|
||||||
|
{
|
||||||
|
return m_drawEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NzModel::IsVisible(const NzFrustumf& frustum) const
|
||||||
|
{
|
||||||
|
#if NAZARA_3D_SAFE
|
||||||
|
if (!m_mesh)
|
||||||
|
{
|
||||||
|
NazaraError("Model has no mesh");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!m_drawEnabled)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!m_boundingBoxUpdated)
|
||||||
|
UpdateBoundingBox();
|
||||||
|
|
||||||
|
return frustum.Contains(m_boundingBox);
|
||||||
|
}
|
||||||
|
|
||||||
bool NzModel::LoadFromFile(const NzString& meshPath, const NzMeshParams& meshParameters, const NzModelParameters& modelParameters)
|
bool NzModel::LoadFromFile(const NzString& meshPath, const NzMeshParams& meshParameters, const NzModelParameters& modelParameters)
|
||||||
{
|
{
|
||||||
///TODO: ResourceManager
|
///TODO: ResourceManager
|
||||||
|
|
@ -175,6 +325,9 @@ bool NzModel::LoadFromStream(NzInputStream& stream, const NzMeshParams& meshPara
|
||||||
|
|
||||||
void NzModel::Reset()
|
void NzModel::Reset()
|
||||||
{
|
{
|
||||||
|
if (m_scene)
|
||||||
|
m_scene->UnregisterForUpdate(this);
|
||||||
|
|
||||||
m_matCount = 0;
|
m_matCount = 0;
|
||||||
m_skinCount = 0;
|
m_skinCount = 0;
|
||||||
|
|
||||||
|
|
@ -238,7 +391,12 @@ bool NzModel::SetAnimation(NzAnimation* animation)
|
||||||
m_interpolation = 0.f;
|
m_interpolation = 0.f;
|
||||||
|
|
||||||
SetSequence(0);
|
SetSequence(0);
|
||||||
|
|
||||||
|
if (m_scene)
|
||||||
|
m_scene->RegisterForUpdate(this);
|
||||||
}
|
}
|
||||||
|
else if (m_scene)
|
||||||
|
m_scene->UnregisterForUpdate(this);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -253,14 +411,16 @@ void NzModel::SetMaterial(unsigned int matIndex, NzMaterial* material)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
m_materials[matIndex]->RemoveResourceReference();
|
unsigned int index = m_skin*m_matCount + matIndex;
|
||||||
|
|
||||||
|
m_materials[index]->RemoveResourceReference();
|
||||||
|
|
||||||
if (material)
|
if (material)
|
||||||
m_materials[matIndex] = material;
|
m_materials[index] = material;
|
||||||
else
|
else
|
||||||
m_materials[matIndex] = NzMaterial::GetDefault();
|
m_materials[index] = NzMaterial::GetDefault();
|
||||||
|
|
||||||
m_materials[matIndex]->AddResourceReference();
|
m_materials[index]->AddResourceReference();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NzModel::SetMaterial(unsigned int skinIndex, unsigned int matIndex, NzMaterial* material)
|
void NzModel::SetMaterial(unsigned int skinIndex, unsigned int matIndex, NzMaterial* material)
|
||||||
|
|
@ -319,6 +479,7 @@ void NzModel::SetMesh(NzMesh* mesh, const NzModelParameters& modelParameters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_boundingBoxUpdated = false;
|
||||||
m_matCount = mesh->GetMaterialCount();
|
m_matCount = mesh->GetMaterialCount();
|
||||||
m_materials.resize(m_matCount, NzMaterial::GetDefault());
|
m_materials.resize(m_matCount, NzMaterial::GetDefault());
|
||||||
if (modelParameters.loadMaterials)
|
if (modelParameters.loadMaterials)
|
||||||
|
|
@ -345,20 +506,6 @@ void NzModel::SetMesh(NzMesh* mesh, const NzModelParameters& modelParameters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NzModel::SetSkinCount(unsigned int skinCount)
|
|
||||||
{
|
|
||||||
#if NAZARA_3D_SAFE
|
|
||||||
if (skinCount == 0)
|
|
||||||
{
|
|
||||||
NazaraError("Skin count must be over 0");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
m_materials.resize(m_matCount*skinCount, NzMaterial::GetDefault());
|
|
||||||
m_skinCount = skinCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NzModel::SetSequence(const NzString& sequenceName)
|
bool NzModel::SetSequence(const NzString& sequenceName)
|
||||||
{
|
{
|
||||||
///TODO: Rendre cette erreur "safe" avec le nouveau système de gestions d'erreur (No-log)
|
///TODO: Rendre cette erreur "safe" avec le nouveau système de gestions d'erreur (No-log)
|
||||||
|
|
@ -406,36 +553,70 @@ void NzModel::SetSequence(unsigned int sequenceIndex)
|
||||||
m_nextFrame = m_currentSequence->firstFrame;
|
m_nextFrame = m_currentSequence->firstFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NzModel::Update(float elapsedTime)
|
void NzModel::SetSkin(unsigned int skin)
|
||||||
{
|
{
|
||||||
if (!m_animation)
|
#if NAZARA_3D_SAFE
|
||||||
|
if (skin >= m_skinCount)
|
||||||
|
{
|
||||||
|
NazaraError("Skin index out of range (" + NzString::Number(skin) + " >= " + NzString::Number(m_skinCount));
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_interpolation += m_currentSequence->frameRate * elapsedTime;
|
|
||||||
while (m_interpolation > 1.f)
|
|
||||||
{
|
|
||||||
m_interpolation -= 1.f;
|
|
||||||
|
|
||||||
unsigned lastFrame = m_currentSequence->firstFrame + m_currentSequence->frameCount - 1;
|
|
||||||
if (m_nextFrame+1 > lastFrame)
|
|
||||||
{
|
|
||||||
if (m_animation->IsLoopPointInterpolationEnabled())
|
|
||||||
{
|
|
||||||
m_currentFrame = m_nextFrame;
|
|
||||||
m_nextFrame = m_currentSequence->firstFrame;
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
m_skin = skin;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzModel::SetSkinCount(unsigned int skinCount)
|
||||||
|
{
|
||||||
|
#if NAZARA_3D_SAFE
|
||||||
|
if (skinCount == 0)
|
||||||
|
{
|
||||||
|
NazaraError("Skin count must be over 0");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
m_materials.resize(m_matCount*skinCount, NzMaterial::GetDefault());
|
||||||
|
m_skinCount = skinCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzModel::Invalidate()
|
||||||
|
{
|
||||||
|
NzSceneNode::Invalidate();
|
||||||
|
|
||||||
|
m_boundingBoxUpdated = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzModel::Register()
|
||||||
|
{
|
||||||
|
if (m_animation)
|
||||||
|
m_scene->RegisterForUpdate(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzModel::Unregister()
|
||||||
|
{
|
||||||
|
m_scene->UnregisterForUpdate(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzModel::Update()
|
||||||
|
{
|
||||||
|
if (m_animationEnabled && m_animation)
|
||||||
|
AdvanceAnimation(m_scene->GetUpdateTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzModel::UpdateBoundingBox() const
|
||||||
|
{
|
||||||
|
if (m_boundingBox.IsNull())
|
||||||
|
{
|
||||||
|
if (m_mesh->GetAnimationType() == nzAnimationType_Skeletal)
|
||||||
|
m_boundingBox.Set(m_skeleton.GetAABB());
|
||||||
else
|
else
|
||||||
{
|
m_boundingBox.Set(m_mesh->GetAABB());
|
||||||
m_currentFrame = m_currentSequence->firstFrame;
|
|
||||||
m_nextFrame = m_currentFrame+1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_currentFrame = m_nextFrame;
|
|
||||||
m_nextFrame++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_animation->AnimateSkeleton(&m_skeleton, m_currentFrame, m_nextFrame, m_interpolation);
|
if (!m_transformMatrixUpdated)
|
||||||
|
UpdateTransformMatrix();
|
||||||
|
|
||||||
|
m_boundingBox.Update(m_transformMatrix);
|
||||||
|
m_boundingBoxUpdated = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,95 @@
|
||||||
|
// Copyright (C) 2012 Jérôme Leclercq
|
||||||
|
// This file is part of the "Nazara Engine - 3D Module"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#include <Nazara/3D/RenderQueue.hpp>
|
||||||
|
#include <Nazara/Renderer/Material.hpp>
|
||||||
|
#include <Nazara/Utility/SkeletalMesh.hpp>
|
||||||
|
#include <Nazara/Utility/StaticMesh.hpp>
|
||||||
|
#include <Nazara/3D/Debug.hpp>
|
||||||
|
|
||||||
|
bool NzRenderQueue::MaterialComparator::operator()(const NzMaterial* mat1, const NzMaterial* mat2)
|
||||||
|
{
|
||||||
|
const NzShader* shader1 = mat1->GetCustomShader();
|
||||||
|
const NzShader* shader2 = mat2->GetCustomShader();
|
||||||
|
|
||||||
|
if (shader1)
|
||||||
|
{
|
||||||
|
if (shader2)
|
||||||
|
return shader1 < shader2;
|
||||||
|
else
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (shader2)
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nzUInt32 shaderFlags1 = mat1->GetShaderFlags();
|
||||||
|
nzUInt32 shaderFlags2 = mat2->GetShaderFlags();
|
||||||
|
|
||||||
|
if (shaderFlags1 == shaderFlags2)
|
||||||
|
return mat1 < mat2;
|
||||||
|
else
|
||||||
|
return shaderFlags1 < shaderFlags2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NzRenderQueue::SkeletalMeshComparator::operator()(const NzSkeletalMesh* subMesh1, const NzSkeletalMesh* subMesh2)
|
||||||
|
{
|
||||||
|
const NzBuffer* buffer1;
|
||||||
|
const NzIndexBuffer* iBuffer1 = subMesh1->GetIndexBuffer();
|
||||||
|
if (iBuffer1)
|
||||||
|
buffer1 = iBuffer1->GetBuffer();
|
||||||
|
else
|
||||||
|
buffer1 = nullptr;
|
||||||
|
|
||||||
|
const NzBuffer* buffer2;
|
||||||
|
const NzIndexBuffer* iBuffer2 = subMesh1->GetIndexBuffer();
|
||||||
|
if (iBuffer2)
|
||||||
|
buffer2 = iBuffer1->GetBuffer();
|
||||||
|
else
|
||||||
|
buffer2 = nullptr;
|
||||||
|
|
||||||
|
if (buffer1 == buffer2)
|
||||||
|
return subMesh1 < subMesh2;
|
||||||
|
else
|
||||||
|
return buffer2 < buffer2;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NzRenderQueue::StaticMeshComparator::operator()(const NzStaticMesh* subMesh1, const NzStaticMesh* subMesh2)
|
||||||
|
{
|
||||||
|
const NzBuffer* buffer1;
|
||||||
|
const NzIndexBuffer* iBuffer1 = subMesh1->GetIndexBuffer();
|
||||||
|
if (iBuffer1)
|
||||||
|
buffer1 = iBuffer1->GetBuffer();
|
||||||
|
else
|
||||||
|
buffer1 = nullptr;
|
||||||
|
|
||||||
|
const NzBuffer* buffer2;
|
||||||
|
const NzIndexBuffer* iBuffer2 = subMesh1->GetIndexBuffer();
|
||||||
|
if (iBuffer2)
|
||||||
|
buffer2 = iBuffer1->GetBuffer();
|
||||||
|
else
|
||||||
|
buffer2 = nullptr;
|
||||||
|
|
||||||
|
if (iBuffer1 == iBuffer2)
|
||||||
|
{
|
||||||
|
buffer1 = subMesh1->GetVertexBuffer()->GetBuffer();
|
||||||
|
buffer2 = subMesh2->GetVertexBuffer()->GetBuffer();
|
||||||
|
|
||||||
|
if (buffer1 == buffer2)
|
||||||
|
return subMesh1 < subMesh2;
|
||||||
|
else
|
||||||
|
return buffer1 < buffer2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return iBuffer1 < iBuffer2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzRenderQueue::Clear()
|
||||||
|
{
|
||||||
|
directionnalLights.clear();
|
||||||
|
visibleLights.clear();
|
||||||
|
visibleSkeletalModels.clear();
|
||||||
|
visibleStaticModels.clear();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,407 @@
|
||||||
|
// Copyright (C) 2012 Jérôme Leclercq
|
||||||
|
// This file is part of the "Nazara Engine - 3D Module"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#include <Nazara/3D/Scene.hpp>
|
||||||
|
#include <Nazara/2D/Drawable.hpp>
|
||||||
|
#include <Nazara/3D/Camera.hpp>
|
||||||
|
#include <Nazara/3D/Light.hpp>
|
||||||
|
#include <Nazara/3D/Model.hpp>
|
||||||
|
#include <Nazara/3D/RenderQueue.hpp>
|
||||||
|
#include <Nazara/3D/SceneRoot.hpp>
|
||||||
|
#include <Nazara/Core/Clock.hpp>
|
||||||
|
#include <Nazara/Core/Error.hpp>
|
||||||
|
#include <Nazara/Renderer/Config.hpp>
|
||||||
|
#include <Nazara/Renderer/Renderer.hpp>
|
||||||
|
#include <Nazara/Renderer/Shader.hpp>
|
||||||
|
#include <Nazara/Renderer/ShaderBuilder.hpp>
|
||||||
|
#include <Nazara/Utility/BufferMapper.hpp>
|
||||||
|
#include <Nazara/Utility/SkeletalMesh.hpp>
|
||||||
|
#include <Nazara/Utility/StaticMesh.hpp>
|
||||||
|
#include <functional>
|
||||||
|
#include <set>
|
||||||
|
#include <vector>
|
||||||
|
#include <Nazara/3D/Debug.hpp>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
const unsigned int maxLights = 8; ///TODO: Config
|
||||||
|
|
||||||
|
struct LightComparator
|
||||||
|
{
|
||||||
|
bool operator()(const NzLight* light1, const NzLight* light2)
|
||||||
|
{
|
||||||
|
return light1->GetPosition().SquaredDistance(pos) < light2->GetPosition().SquaredDistance(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
NzVector3f pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
void RecursiveFrustumCull(NzRenderQueue& renderQueue, const NzFrustumf& frustum, NzSceneNode* node)
|
||||||
|
{
|
||||||
|
for (NzNode* child : node->GetChilds())
|
||||||
|
{
|
||||||
|
if (child->GetNodeType() == nzNodeType_Scene)
|
||||||
|
{
|
||||||
|
NzSceneNode* sceneNode = static_cast<NzSceneNode*>(child);
|
||||||
|
///TODO: Empêcher le rendu des enfants si le parent est cullé selon un flag
|
||||||
|
if (sceneNode->IsVisible(frustum))
|
||||||
|
sceneNode->AddToRenderQueue(renderQueue);
|
||||||
|
|
||||||
|
RecursiveFrustumCull(renderQueue, frustum, sceneNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NzSceneImpl
|
||||||
|
{
|
||||||
|
NzSceneImpl(NzScene* scene) :
|
||||||
|
root(scene)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<NzUpdatable*> updateList;
|
||||||
|
std::vector<NzRenderer::InstancingData> instancingData;
|
||||||
|
NzClock updateClock;
|
||||||
|
NzColor ambientColor = NzColor(25,25,25);
|
||||||
|
NzRenderQueue renderQueue;
|
||||||
|
NzSceneRoot root;
|
||||||
|
const NzCamera* activeCamera;
|
||||||
|
NzVertexBuffer* skinningBuffer;
|
||||||
|
bool update = true;
|
||||||
|
float frameTime;
|
||||||
|
float updateTime;
|
||||||
|
unsigned int updatePerSecond = 60;
|
||||||
|
};
|
||||||
|
|
||||||
|
NzScene::NzScene()
|
||||||
|
{
|
||||||
|
m_impl = new NzSceneImpl(this);
|
||||||
|
m_impl->skinningBuffer = new NzVertexBuffer(NzMesh::GetDeclaration(), 20000, nzBufferStorage_Hardware, nzBufferUsage_Dynamic);
|
||||||
|
|
||||||
|
if (NzRenderer::HasCapability(nzRendererCap_Instancing))
|
||||||
|
m_impl->instancingData.resize(NAZARA_RENDERER_INSTANCING_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
NzScene::~NzScene()
|
||||||
|
{
|
||||||
|
for (NzNode* child : m_impl->root.GetChilds())
|
||||||
|
{
|
||||||
|
if (child->GetNodeType() == nzNodeType_Scene)
|
||||||
|
static_cast<NzSceneNode*>(child)->SetScene(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete m_impl->skinningBuffer;
|
||||||
|
delete m_impl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzScene::Cull()
|
||||||
|
{
|
||||||
|
m_impl->renderQueue.Clear();
|
||||||
|
|
||||||
|
// Frustum culling
|
||||||
|
RecursiveFrustumCull(m_impl->renderQueue, m_impl->activeCamera->GetFrustum(), &m_impl->root);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzScene::Draw()
|
||||||
|
{
|
||||||
|
LightComparator lightComparator;
|
||||||
|
|
||||||
|
// Pour les meshs squelettiques, on utilise un buffer commun
|
||||||
|
NzRenderer::SetVertexBuffer(m_impl->skinningBuffer);
|
||||||
|
for (auto matIt : m_impl->renderQueue.visibleSkeletalModels)
|
||||||
|
{
|
||||||
|
// On applique le shader du matériau
|
||||||
|
nzUInt32 shaderFlags = matIt.first->GetShaderFlags();
|
||||||
|
|
||||||
|
const NzShader* shader = NzShaderBuilder::Get(shaderFlags);
|
||||||
|
|
||||||
|
NzRenderer::SetShader(shader);
|
||||||
|
matIt.first->Apply(shader);
|
||||||
|
|
||||||
|
// Position de la caméra
|
||||||
|
int camPosLocation = shader->GetUniformLocation("CameraPosition");
|
||||||
|
if (camPosLocation != -1)
|
||||||
|
shader->SendVector(camPosLocation, m_impl->activeCamera->GetPosition());
|
||||||
|
|
||||||
|
// Couleur ambiante de la scène
|
||||||
|
int sceneAmbientLocation = shader->GetUniformLocation("SceneAmbient");
|
||||||
|
if (sceneAmbientLocation != -1)
|
||||||
|
shader->SendColor(sceneAmbientLocation, m_impl->ambientColor);
|
||||||
|
|
||||||
|
// Gestion des lumières (D'abord directionnelles)
|
||||||
|
int lightCountLocation = shader->GetUniformLocation("LightCount");
|
||||||
|
|
||||||
|
unsigned int lightIndex = 0;
|
||||||
|
if (lightCountLocation != -1)
|
||||||
|
{
|
||||||
|
for (const NzLight* light : m_impl->renderQueue.directionnalLights)
|
||||||
|
{
|
||||||
|
light->Apply(shader, lightIndex++);
|
||||||
|
if (lightIndex > maxLights)
|
||||||
|
break; // N'arrivera jamais mais pourrait résulter en un bug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto subMeshIt : matIt.second)
|
||||||
|
{
|
||||||
|
const NzSkeletalMesh* skeletalMesh = subMeshIt.first;
|
||||||
|
const NzIndexBuffer* indexBuffer = skeletalMesh->GetIndexBuffer();
|
||||||
|
|
||||||
|
unsigned int vertexCount = skeletalMesh->GetVertexCount();
|
||||||
|
|
||||||
|
// Gestion du draw call avant la boucle de rendu
|
||||||
|
std::function<void(nzPrimitiveType, unsigned int, unsigned int)> drawFunc;
|
||||||
|
nzPrimitiveType primitiveType = skeletalMesh->GetPrimitiveType();
|
||||||
|
unsigned int indexCount;
|
||||||
|
if (indexBuffer)
|
||||||
|
{
|
||||||
|
drawFunc = NzRenderer::DrawIndexedPrimitives;
|
||||||
|
indexCount = indexBuffer->GetIndexCount();
|
||||||
|
NzRenderer::SetIndexBuffer(indexBuffer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
drawFunc = NzRenderer::DrawPrimitives;
|
||||||
|
indexCount = skeletalMesh->GetVertexCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const NzRenderQueue::SkeletalData& data : subMeshIt.second)
|
||||||
|
{
|
||||||
|
// Transfert du résultat du skinning vers notre buffer hardware
|
||||||
|
NzBufferMapper<NzVertexBuffer> outputMapper(m_impl->skinningBuffer, nzBufferAccess_DiscardAndWrite, 0, vertexCount);
|
||||||
|
std::memcpy(outputMapper.GetPointer(), &data.skinnedVertices[0], vertexCount*sizeof(NzMeshVertex));
|
||||||
|
outputMapper.Unmap();
|
||||||
|
|
||||||
|
// Calcul des lumières les plus proches (TODO: LightManager ?)
|
||||||
|
if (lightCountLocation != -1)
|
||||||
|
{
|
||||||
|
auto visibleLights = m_impl->renderQueue.visibleLights;
|
||||||
|
lightComparator.pos = data.transformMatrix.GetTranslation();
|
||||||
|
std::sort(visibleLights.begin(), visibleLights.end(), lightComparator);
|
||||||
|
|
||||||
|
const unsigned int maxLightPerObject = 3; ///TODO: Config
|
||||||
|
unsigned int max = std::min(std::min(maxLights - lightIndex, maxLightPerObject), visibleLights.size());
|
||||||
|
for (unsigned int i = 0; i < max; ++i)
|
||||||
|
visibleLights[i]->Apply(shader, lightIndex + i);
|
||||||
|
|
||||||
|
shader->SendInteger(lightCountLocation, lightIndex + max);
|
||||||
|
}
|
||||||
|
|
||||||
|
NzRenderer::SetMatrix(nzMatrixType_World, data.transformMatrix);
|
||||||
|
|
||||||
|
drawFunc(primitiveType, 0, indexCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pour les meshs statiques, on utilise le buffer du mesh
|
||||||
|
for (auto matIt : m_impl->renderQueue.visibleStaticModels)
|
||||||
|
{
|
||||||
|
// On applique le shader du matériau
|
||||||
|
nzUInt32 shaderFlags = matIt.first->GetShaderFlags();
|
||||||
|
if (NzRenderer::HasCapability(nzRendererCap_Instancing) && m_impl->renderQueue.visibleLights.empty())
|
||||||
|
shaderFlags |= nzShaderFlags_Instancing;
|
||||||
|
|
||||||
|
const NzShader* shader = NzShaderBuilder::Get(shaderFlags);
|
||||||
|
|
||||||
|
NzRenderer::SetShader(shader);
|
||||||
|
matIt.first->Apply(shader);
|
||||||
|
|
||||||
|
bool instancing = shader->GetFlags() & nzShaderFlags_Instancing;
|
||||||
|
|
||||||
|
// Position de la caméra
|
||||||
|
int camPosLocation = shader->GetUniformLocation("CameraPosition");
|
||||||
|
if (camPosLocation != -1)
|
||||||
|
shader->SendVector(camPosLocation, m_impl->activeCamera->GetPosition());
|
||||||
|
|
||||||
|
// Couleur ambiante de la scène
|
||||||
|
int sceneAmbientLocation = shader->GetUniformLocation("SceneAmbient");
|
||||||
|
if (sceneAmbientLocation != -1)
|
||||||
|
shader->SendColor(sceneAmbientLocation, m_impl->ambientColor);
|
||||||
|
|
||||||
|
// Gestion des lumières (D'abord directionnelles)
|
||||||
|
int lightCountLocation = shader->GetUniformLocation("LightCount");
|
||||||
|
|
||||||
|
unsigned int lightIndex = 0;
|
||||||
|
if (lightCountLocation != -1)
|
||||||
|
{
|
||||||
|
for (const NzLight* light : m_impl->renderQueue.directionnalLights)
|
||||||
|
{
|
||||||
|
light->Apply(shader, lightIndex++);
|
||||||
|
if (lightIndex > maxLights)
|
||||||
|
break; // N'arrivera probablement jamais mais pourrait résulter en un bug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto subMeshIt : matIt.second)
|
||||||
|
{
|
||||||
|
NzStaticMesh* staticMesh = subMeshIt.first;
|
||||||
|
|
||||||
|
const NzIndexBuffer* indexBuffer = staticMesh->GetIndexBuffer();
|
||||||
|
const NzVertexBuffer* vertexBuffer = staticMesh->GetVertexBuffer();
|
||||||
|
|
||||||
|
NzRenderer::SetVertexBuffer(vertexBuffer);
|
||||||
|
|
||||||
|
// Gestion du draw call avant la boucle de rendu
|
||||||
|
std::function<void(nzPrimitiveType, unsigned int, unsigned int)> draw;
|
||||||
|
std::function<void(unsigned int, nzPrimitiveType, unsigned int, unsigned int)> instancedDraw;
|
||||||
|
nzPrimitiveType primitiveType = staticMesh->GetPrimitiveType();
|
||||||
|
unsigned int indexCount;
|
||||||
|
if (indexBuffer)
|
||||||
|
{
|
||||||
|
draw = NzRenderer::DrawIndexedPrimitives;
|
||||||
|
indexCount = indexBuffer->GetIndexCount();
|
||||||
|
instancedDraw = NzRenderer::DrawIndexedPrimitivesInstanced;
|
||||||
|
NzRenderer::SetIndexBuffer(indexBuffer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
draw = NzRenderer::DrawPrimitives;
|
||||||
|
indexCount = vertexBuffer->GetVertexCount();
|
||||||
|
instancedDraw = NzRenderer::DrawPrimitivesInstanced;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instancing)
|
||||||
|
{
|
||||||
|
shader->SendInteger(lightCountLocation, lightIndex);
|
||||||
|
|
||||||
|
unsigned int count = 0;
|
||||||
|
for (const NzMatrix4f& matrix : subMeshIt.second)
|
||||||
|
{
|
||||||
|
m_impl->instancingData[count++].worldMatrix = matrix;
|
||||||
|
if (count == m_impl->instancingData.size())
|
||||||
|
{
|
||||||
|
NzRenderer::FillInstancingBuffer(&m_impl->instancingData[0], count);
|
||||||
|
instancedDraw(count, primitiveType, 0, indexCount);
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
NzRenderer::FillInstancingBuffer(&m_impl->instancingData[0], count);
|
||||||
|
instancedDraw(count, primitiveType, 0, indexCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (const NzMatrix4f& matrix : subMeshIt.second)
|
||||||
|
{
|
||||||
|
// Calcul des lumières les plus proches (TODO: LightManager ?)
|
||||||
|
if (lightCountLocation != -1)
|
||||||
|
{
|
||||||
|
std::vector<const NzLight*>& visibleLights = m_impl->renderQueue.visibleLights;
|
||||||
|
lightComparator.pos = matrix.GetTranslation();
|
||||||
|
std::sort(visibleLights.begin(), visibleLights.end(), lightComparator);
|
||||||
|
|
||||||
|
const unsigned int maxLightPerObject = 3; ///TODO: Config
|
||||||
|
unsigned int max = std::min(std::min(maxLights - lightIndex, maxLightPerObject), visibleLights.size());
|
||||||
|
for (unsigned int i = 0; i < max; ++i)
|
||||||
|
visibleLights[i]->Apply(shader, lightIndex + i);
|
||||||
|
|
||||||
|
shader->SendInteger(lightCountLocation, lightIndex + max);
|
||||||
|
}
|
||||||
|
|
||||||
|
NzRenderer::SetMatrix(nzMatrixType_World, matrix);
|
||||||
|
|
||||||
|
draw(primitiveType, 0, indexCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Les autres drawables (Exemple: Terrain)
|
||||||
|
for (const NzDrawable* drawable : m_impl->renderQueue.otherDrawables)
|
||||||
|
drawable->Draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
NzSceneNode& NzScene::GetRoot() const
|
||||||
|
{
|
||||||
|
return m_impl->root;
|
||||||
|
}
|
||||||
|
|
||||||
|
float NzScene::GetUpdateTime() const
|
||||||
|
{
|
||||||
|
return m_impl->updateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int NzScene::GetUpdatePerSecond() const
|
||||||
|
{
|
||||||
|
return m_impl->updatePerSecond;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzScene::RegisterForUpdate(NzUpdatable* object)
|
||||||
|
{
|
||||||
|
#if NAZARA_3D_SAFE
|
||||||
|
if (!object)
|
||||||
|
{
|
||||||
|
NazaraError("Invalid object");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
m_impl->updateList.insert(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzScene::SetAmbientColor(const NzColor& color)
|
||||||
|
{
|
||||||
|
m_impl->ambientColor = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzScene::SetUpdatePerSecond(unsigned int updatePerSecond)
|
||||||
|
{
|
||||||
|
m_impl->updatePerSecond = updatePerSecond;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzScene::UnregisterForUpdate(NzUpdatable* object)
|
||||||
|
{
|
||||||
|
#if NAZARA_3D_SAFE
|
||||||
|
if (!object)
|
||||||
|
{
|
||||||
|
NazaraError("Invalid object");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
m_impl->updateList.erase(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzScene::Update()
|
||||||
|
{
|
||||||
|
if (m_impl->updatePerSecond == 0 || m_impl->updateClock.GetMilliseconds() > 1000/m_impl->updatePerSecond)
|
||||||
|
{
|
||||||
|
m_impl->updateTime = m_impl->updateClock.GetSeconds();
|
||||||
|
m_impl->updateClock.Restart();
|
||||||
|
|
||||||
|
for (NzUpdatable* updatable : m_impl->updateList)
|
||||||
|
{
|
||||||
|
///TODO: Multihreading
|
||||||
|
updatable->Update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzScene::UpdateVisible()
|
||||||
|
{/*
|
||||||
|
if (m_impl->update)
|
||||||
|
{
|
||||||
|
for (NzSceneNode* node : m_impl->visibleNodes)
|
||||||
|
{
|
||||||
|
if (node->ShouldUpdateWhenVisible())
|
||||||
|
node->Update();
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
NzScene::operator const NzSceneNode&() const
|
||||||
|
{
|
||||||
|
return m_impl->root;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzScene::SetActiveCamera(const NzCamera* camera)
|
||||||
|
{
|
||||||
|
m_impl->activeCamera = camera;
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,75 @@
|
||||||
// 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/3D/SceneNode.hpp>
|
#include <Nazara/3D/SceneNode.hpp>
|
||||||
|
#include <Nazara/3D/Scene.hpp>
|
||||||
#include <Nazara/3D/Debug.hpp>
|
#include <Nazara/3D/Debug.hpp>
|
||||||
|
|
||||||
|
NzSceneNode::NzSceneNode() :
|
||||||
|
m_scene(nullptr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
NzSceneNode::NzSceneNode(const NzSceneNode& node) :
|
||||||
|
NzNode(node),
|
||||||
|
m_scene(node.m_scene)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
NzSceneNode::~NzSceneNode() = default;
|
NzSceneNode::~NzSceneNode() = default;
|
||||||
|
|
||||||
|
nzNodeType NzSceneNode::GetNodeType() const
|
||||||
|
{
|
||||||
|
return nzNodeType_Scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
NzScene* NzSceneNode::GetScene() const
|
||||||
|
{
|
||||||
|
return m_scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzSceneNode::OnParenting(const NzNode* parent)
|
||||||
|
{
|
||||||
|
if (parent)
|
||||||
|
{
|
||||||
|
if (parent->GetNodeType() == nzNodeType_Scene)
|
||||||
|
SetScene(static_cast<const NzSceneNode*>(parent)->m_scene);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
SetScene(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzSceneNode::Register()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzSceneNode::SetScene(NzScene* scene)
|
||||||
|
{
|
||||||
|
if (m_scene != scene)
|
||||||
|
{
|
||||||
|
if (m_scene)
|
||||||
|
Unregister();
|
||||||
|
|
||||||
|
m_scene = scene;
|
||||||
|
if (m_scene)
|
||||||
|
Register();
|
||||||
|
|
||||||
|
for (NzNode* child : m_childs)
|
||||||
|
{
|
||||||
|
if (child->GetNodeType() == nzNodeType_Scene)
|
||||||
|
static_cast<NzSceneNode*>(child)->SetScene(scene);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NzSceneNode::ShouldUpdateWhenVisible()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzSceneNode::Unregister()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzSceneNode::Update()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
// Copyright (C) 2012 Jérôme Leclercq
|
||||||
|
// This file is part of the "Nazara Engine - 3D Module"
|
||||||
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||||
|
|
||||||
|
#include <Nazara/3D/SceneRoot.hpp>
|
||||||
|
#include <Nazara/Core/Error.hpp>
|
||||||
|
#include <Nazara/3D/Debug.hpp>
|
||||||
|
|
||||||
|
NzSceneRoot::NzSceneRoot(NzScene* scene)
|
||||||
|
{
|
||||||
|
m_scene = scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
NzSceneRoot::~NzSceneRoot() = default;
|
||||||
|
|
||||||
|
void NzSceneRoot::AddToRenderQueue(NzRenderQueue& renderQueue) const
|
||||||
|
{
|
||||||
|
NazaraUnused(renderQueue);
|
||||||
|
|
||||||
|
NazaraInternalError("SceneNode::AddToRenderQueue() called on SceneRoot");
|
||||||
|
}
|
||||||
|
|
||||||
|
const NzBoundingBoxf& NzSceneRoot::GetBoundingBox() const
|
||||||
|
{
|
||||||
|
static NzBoundingBoxf infinite(nzExtend_Infinite);
|
||||||
|
return infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
nzSceneNodeType NzSceneRoot::GetSceneNodeType() const
|
||||||
|
{
|
||||||
|
return nzSceneNodeType_Root;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NzSceneRoot::IsVisible(const NzFrustumf& frustum) const
|
||||||
|
{
|
||||||
|
NazaraUnused(frustum);
|
||||||
|
|
||||||
|
return true; // Toujours visible
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzSceneRoot::Register()
|
||||||
|
{
|
||||||
|
NazaraInternalError("SceneNode::Register() called on SceneRoot");
|
||||||
|
}
|
||||||
|
|
||||||
|
void NzSceneRoot::Unregister()
|
||||||
|
{
|
||||||
|
NazaraInternalError("SceneNode::Unregister() called on SceneRoot");
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue