From f54eb0402f8d22af2f733b19966b9941a9a095ee Mon Sep 17 00:00:00 2001 From: Lynix Date: Mon, 28 Sep 2015 12:41:41 +0200 Subject: [PATCH 1/2] Examples: Fix MeshInfos Former-commit-id: b6c5ea953f60d2b22eaa6aae06d1618efdb09bc1 --- examples/MeshInfos/main.cpp | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/examples/MeshInfos/main.cpp b/examples/MeshInfos/main.cpp index 839c20f31..ba55d2686 100644 --- a/examples/MeshInfos/main.cpp +++ b/examples/MeshInfos/main.cpp @@ -11,7 +11,7 @@ int main() { // Pour charger des ressources, il est impératif d'initialiser le module utilitaire - Initializer utility; + Nz::Initializer utility; if (!utility) { // Ça n'a pas fonctionné, le pourquoi se trouve dans le fichier NazaraLog.log @@ -22,7 +22,7 @@ int main() for (;;) { - Directory resourceDirectory("resources/"); + Nz::Directory resourceDirectory("resources/"); if (!resourceDirectory.Open()) { std::cerr << "Failed to open resource directory" << std::endl; @@ -30,12 +30,12 @@ int main() return EXIT_FAILURE; } - std::vector models; + std::vector models; while (resourceDirectory.NextResult()) { - String path = resourceDirectory.GetResultName(); - String ext = path.SubStringFrom('.', -1, true); // Tout ce qui vient après le dernier '.' de la chaîne - if (MeshLoader::IsExtensionSupported(ext)) // L'extension est-elle supportée par le MeshLoader ? + Nz::String path = resourceDirectory.GetResultName(); + Nz::String ext = path.SubStringFrom('.', -1, true); // Tout ce qui vient après le dernier '.' de la chaîne + if (Nz::MeshLoader::IsExtensionSupported(ext)) // L'extension est-elle supportée par le MeshLoader ? models.push_back(path); } @@ -67,7 +67,7 @@ int main() if (iChoice == 0) break; - Mesh mesh; + Nz::Mesh mesh; if (!mesh.LoadFromFile("resources/" + models[iChoice-1])) { std::cout << "Failed to load mesh" << std::endl; @@ -77,11 +77,11 @@ int main() switch (mesh.GetAnimationType()) { - case AnimationType_Skeletal: + case Nz::AnimationType_Skeletal: std::cout << "This is a skeletal-animated mesh" << std::endl; break; - case AnimationType_Static: + case Nz::AnimationType_Static: std::cout << "This is a static mesh" << std::endl; break; @@ -92,9 +92,9 @@ int main() if (mesh.IsAnimable()) { - if (mesh.GetAnimationType() == AnimationType_Skeletal) + if (mesh.GetAnimationType() == Nz::AnimationType_Skeletal) { - const Skeleton* skeleton = mesh.GetSkeleton(); + const Nz::Skeleton* skeleton = mesh.GetSkeleton(); unsigned int jointCount = skeleton->GetJointCount(); std::cout << "It has a skeleton made of " << skeleton->GetJointCount() << " joint(s)." << std::endl; std::cout << "Print joints ? (Y/N) "; @@ -107,10 +107,10 @@ int main() { for (unsigned int i = 0; i < jointCount; ++i) { - const Joint* joint = skeleton->GetJoint(i); + const Nz::Joint* joint = skeleton->GetJoint(i); std::cout << "\t" << (i+1) << ": " << joint->GetName(); - const Joint* parent = static_cast(joint->GetParent()); + const Nz::Joint* parent = static_cast(joint->GetParent()); if (parent) std::cout << " (Parent: " << parent->GetName() << ')'; @@ -119,10 +119,10 @@ int main() } } - String animationPath = mesh.GetAnimation(); + Nz::String animationPath = mesh.GetAnimation(); if (!animationPath.IsEmpty()) { - Animation animation; + Nz::Animation animation; if (animation.LoadFromFile(animationPath)) { unsigned int sequenceCount = animation.GetSequenceCount(); @@ -137,7 +137,7 @@ int main() { for (unsigned int i = 0; i < sequenceCount; ++i) { - const Sequence* sequence = animation.GetSequence(i); + const Nz::Sequence* sequence = animation.GetSequence(i); std::cout << "\t" << (i+1) << ": " << sequence->name << std::endl; std::cout << "\t\tStart frame: " << sequence->firstFrame << std::endl; std::cout << "\t\tFrame count: " << sequence->frameCount << std::endl; @@ -153,7 +153,7 @@ int main() std::cout << "It's animable but has no animation information" << std::endl; } - Boxf cube = mesh.GetAABB(); + Nz::Boxf cube = mesh.GetAABB(); std::cout << "Mesh is " << cube.width << " units wide, " << cube.height << " units height and " << cube.depth << " units depth" << std::endl; unsigned int materialCount = mesh.GetMaterialCount(); From 1db673f9be012eb21eb087b894e8c073232149a3 Mon Sep 17 00:00:00 2001 From: Lynix Date: Mon, 28 Sep 2015 13:56:33 +0200 Subject: [PATCH 2/2] Example: Update FirstScene for NDK Former-commit-id: d14aad9f6a616796b92b78934e413b588a8e3185 --- examples/FirstScene/build.lua | 3 +- examples/FirstScene/main.cpp | 277 ++++++++++++++++++++-------------- 2 files changed, 165 insertions(+), 115 deletions(-) diff --git a/examples/FirstScene/build.lua b/examples/FirstScene/build.lua index b7f8a146a..bdcdf8bca 100644 --- a/examples/FirstScene/build.lua +++ b/examples/FirstScene/build.lua @@ -10,5 +10,6 @@ EXAMPLE.Libraries = { "NazaraCore", "NazaraGraphics", "NazaraRenderer", - "NazaraUtility" + "NazaraUtility", + "NazaraSDK" } diff --git a/examples/FirstScene/main.cpp b/examples/FirstScene/main.cpp index 9ed702a69..24fef574f 100644 --- a/examples/FirstScene/main.cpp +++ b/examples/FirstScene/main.cpp @@ -15,18 +15,25 @@ #include // Module graphique #include // Module de rendu #include // Module utilitaire +#include +#include +#include +#include +#include +#include +#include #include // Petite fonction permettant de rendre le déplacement de la caméra moins ridige -NzVector3f DampedString(const NzVector3f& currentPos, const NzVector3f& targetPos, float frametime, float springStrength = 3.f); +Nz::Vector3f DampedString(const Nz::Vector3f& currentPos, const Nz::Vector3f& targetPos, float frametime, float springStrength = 3.f); int main() { - // Pour commencer, nous initialisons le module Graphique, celui-ci va provoquer l'initialisation (dans l'ordre), - // du noyau (Core), Utility, Renderer. + // Pour commencer, nous initialisons le SDK de Nazara, celui-ci va préparer le terrain en initialisant le moteur, + // les composants, systèmes, etc. // NzInitializer est une classe RAII appelant Initialize dans son constructeur et Uninitialize dans son destructeur. // Autrement dit, une fois ceci fait nous n'avons plus à nous soucier de la libération du moteur. - NzInitializer nazara; + Nz::Initializer nazara; if (!nazara) { // Une erreur s'est produite dans l'initialisation d'un des modules @@ -36,17 +43,16 @@ int main() return EXIT_FAILURE; } - // Nazara étant initialisé, nous pouvons créer la scène - // Une scène représente tout ce qui est visible par une ou plusieurs caméras. - // La plupart du temps vous n'aurez pas besoin de plus d'une scène, mais cela peut se révéler utile pour mieux - // organiser et optimiser le rendu. - // Par exemple, une pièce contenant une télévision, laquelle affichant des images provenant d'une Camera - // Le rendu sera alors plus efficace en créant deux scènes, une pour la pièce et l'autre pour les images de la télé. - // Cela diminuera le nombre de SceneNode à gérer pour chaque scène, et vous permettra même de ne pas afficher la scène - // affichée dans la télé si cette dernière n'est pas visible dans la première scène. - NzScene scene; + // Nazara étant initialisé, nous pouvons créer le monde pour contenir notre scène. + // Dans un ECS, le monde représente bien ce que son nom indique, c'est l'ensemble de ce qui existe au niveau de l'application. + // Il contient les systèmes et les entités, ces dernières contiennent les composants. + // Il est possible d'utiliser plusieurs mondes au sein d'une même application, par exemple pour gérer un mélange de 2D et de 3D, + // mais nous verrons cela dans un prochain exemple. + Ndk::World world; - // La première chose que nous faisons est d'ajouter un background (fond) à la scène. + // Nous pouvons maintenant ajouter des systèmes, mais dans cet exemple nous nous contenterons de ceux de base. + + // La première chose que nous faisons est d'ajouter un background (fond) à notre scène. // Il en existe plusieurs types, le moteur inclut pour l'instant trois d'entre eux: // -ColorBackground: Une couleur unie en fond // -SkyboxBackground: Une skybox en fond, un cube à six faces rendu autour de la caméra (En perdant la notion de distance) @@ -56,53 +62,59 @@ int main() // Pour commencer il faut charger une texture de type cubemap, certaines images sont assemblées de cette façon, // comme celle que nous allons utiliser. // En réalité les textures "cubemap" regroupent six faces en une, pour faciliter leur utilisation. - NzTexture* texture = new NzTexture; + + // Nous créons une nouvelle texture et prenons une référence sur celle-ci (à la manière des pointeurs intelligents) + Nz::TextureRef texture = Nz::Texture::New(); if (texture->LoadCubemapFromFile("resources/skybox-space.png")) { // Si la création du cubemap a fonctionné - // Nous indiquons que la texture est "non-persistente", autrement dit elle sera libérée automatiquement par le moteur - // à l'instant précis où elle ne sera plus utilisée, dans ce cas-ci, ce sera à la libération de l'objet skybox, - // ceci arrivant lorsqu'un autre background est affecté à la scène, ou lorsque la scène sera libérée - texture->SetPersistent(false); + // Nous créons alors le background à partir de notre texture (celui-ci va référencer notre texture, notre pointeur ne sert alors plus à rien). + Nz::SkyboxBackgroundRef skybox = Nz::SkyboxBackground::New(std::move(texture)); - // Nous créons le background en lui affectant la texture - NzSkyboxBackground* background = new NzSkyboxBackground(texture); + // Accédons maintenant au système de rendu faisant partie du monde + Ndk::RenderSystem& renderSystem = world.GetSystem(); // Une assertion valide la précondition "le système doit faire partie du monde" - // Nous pouvons en profiter pour paramétrer le background. - // Cependant, nous n'avons rien de spécial à faire ici, nous pouvons donc l'envoyer à la scène. - scene.SetBackground(background); + // Nous assignons ensuite notre skybox comme "fond par défaut" du système + // La notion "par défaut" existe parce qu'une caméra pourrait utiliser son propre fond lors du rendu, + // le fond par défaut est utilisé lorsque la caméra n'a pas de fond propre assigné + renderSystem.SetDefaultBackground(std::move(skybox)); - // Comme indiqué plus haut, la scène s'occupera automatiquement de la libération de notre background + // Notre skybox est maintenant référencée par le système, lui-même appartenant au monde, aucune libération explicite n'est nécessaire } else - { - delete texture; // Le chargement a échoué, nous libérons la texture + // Le chargement a échoué std::cout << "Failed to load skybox" << std::endl; - } // Ensuite, nous allons rajouter un modèle à notre scène. + // Les modèles représentent, globalement, tout ce qui est visible en trois dimensions. // Nous choisirons ici un vaisseau spatial (Quoi de mieux pour une scène spatiale ?) - NzModel* spaceship = scene.CreateNode(); // Création depuis la scène - // Une structure permettant de paramétrer le chargement des modèles - NzModelParameters params; + // Encore une fois, nous récupérons une référence plutôt que l'objet lui-même (cela va être très utile par la suite) + Nz::ModelRef spaceshipModel = Nz::Model::New(); - // Le format OBJ ne précise aucune échelle pour ses données, contrairement à Nazara (une unité = un mètre). - // Comme le vaisseau est très grand (Des centaines de mètres de long), nous allons le rendre plus petit - // pour les besoins de la démo. + // Nous allons charger notre modèle depuis un fichier, mais nous pouvons ajuster le modèle lors du chargement via + // une structure permettant de paramétrer le chargement des modèles + Nz::ModelParameters params; + + // Le format OBJ ne précise aucune échelle pour ses données, contrairement à Nazara (une unité = un mètre en 3D). + // Comme le vaisseau est très grand (Des centaines de mètres de long), nous allons le rendre plus petit pour les besoins de la démo. // Ce paramètre sert à indiquer la mise à l'échelle désirée lors du chargement du modèle. params.mesh.scale.Set(0.01f); // Un centième de la taille originelle - // Les UVs de ce fichier sont retournées (repère OpenGL, origine coin bas-gauche) par rapport à ce que le moteur attend - // Nous devons dire au moteur de les retourner lors du chargement + // Les UVs de ce fichier sont retournées (repère OpenGL, origine coin bas-gauche) par rapport à ce que le moteur attend (haut-gauche) + // Nous devons donc indiquer au moteur de les retourner lors du chargement params.mesh.flipUVs = true; + // Nazara va par défaut optimiser les modèles pour un rendu plus rapide, cela peut prendre du temps et n'est pas nécessaire ici + params.mesh.optimizeIndexBuffers = false; + // On charge ensuite le modèle depuis son fichier // Le moteur va charger le fichier et essayer de retrouver les fichiers associés (comme les matériaux, textures, ...) - if (!spaceship->LoadFromFile("resources/Spaceship/spaceship.obj", params)) + if (!spaceshipModel->LoadFromFile("resources/Spaceship/spaceship.obj", params)) { + // Si le chargement a échoué (fichier inexistant/invalide), il ne sert à rien de continuer std::cout << "Failed to load spaceship" << std::endl; std::getchar(); @@ -111,15 +123,18 @@ int main() // Nous voulons afficher quelques statistiques relatives au modèle, comme le nombre de sommets et de triangles // Pour cela, nous devons accéder au mesh (maillage 3D) - NzMesh* mesh = spaceship->GetMesh(); + // Note: Si nous voulions stocker le mesh pour nous en servir après, nous devrions alors récupérer une référence pour nous assurer + // qu'il ne sera pas supprimé tant que nous l'utilisons, mais ici nous faisons un accès direct et ne nous servons plus du pointeur par la suite + // Il est donc acceptable d'utiliser un pointeur nu ici. + Nz::Mesh* mesh = spaceshipModel->GetMesh(); std::cout << mesh->GetVertexCount() << " sommets" << std::endl; std::cout << mesh->GetTriangleCount() << " triangles" << std::endl; // En revanche, le format OBJ ne précise pas l'utilisation d'une normal map, nous devons donc la charger manuellement // Pour commencer on récupère le matériau du mesh, celui-ci en possède plusieurs mais celui qui nous intéresse, // celui de la coque, est le second (Cela est bien entendu lié au modèle en lui-même) - NzMaterial* material = spaceship->GetMaterial(1); + Nz::Material* material = spaceshipModel->GetMaterial(1); // Encore une fois nous ne faisons qu'un accès direct. // On lui indique ensuite le chemin vers la normal map if (!material->SetNormalMap("resources/Spaceship/Texture/normal.png")) @@ -129,27 +144,58 @@ int main() std::cout << "Failed to load normal map" << std::endl; } - // Nous avons besoin également d'une caméra, pour des raisons évidentes, celle-ci sera à l'écart du modèle + // Bien, nous avons un modèle valide, mais celui-ci ne consiste qu'en des informations de rendu, de matériaux et de textures. + // Commençons donc par créer une entité vide, cela se fait en demandant au monde de générer une nouvelle entité. + Ndk::EntityHandle spaceship = world.CreateEntity(); + + // Note: Nous ne récupérons pas l'entité directement mais un "handle" vers elle, ce dernier est un pointeur intelligent non-propriétaire. + // Pour des raisons techniques, le pointeur de l'entité peut venir à changer, ou l'entité être simplement détruite pour n'importe quelle raison. + // Le Handle nous permet de maintenir un pointeur valide vers notre entité, et invalidé automatiquement à sa mort. + + // Nous avons désormais une entité, mais celle-ci ne contient rien et n'a d'autre propriété qu'un identifiant + // Nous devons donc lui rajouter les composants que nous voulons. + + // Un NodeComponent donne à notre entité une position, rotation, échelle, et nous permet de l'attacher à d'autres entités (ce que nous ne ferons pas ici). + // Étant donné que par défaut, un NodeComponent se place en (0,0,0) sans rotation et avec une échelle de 1,1,1 et que cela nous convient, + // nous n'avons pas besoin d'agir sur le composant créé. + spaceship->AddComponent(); + + // Bien, notre entité nouvellement créé dispose maintenant d'une position dans la scène, mais est toujours invisible + // Nous lui ajoutons donc un GraphicsComponent + Ndk::GraphicsComponent& spaceshipGraphics = spaceship->AddComponent(); + + // Ce composant sert de point d'attache pour tous les renderables instanciés (tels que les modèles, les sprites, le texte, etc.) + // Cela signifie également qu'un modèle peut être attaché à autant d'entités que nécessaire. + // Note: Afin de maximiser les performances, essayez d'avoir le moins de renderables instanciés/matériaux et autres ressources possible + // le moteur fonctionne selon le batching et regroupera par exemple tous les modèles identiques ensembles lors du rendu. + spaceshipGraphics.Attach(spaceshipModel); + + // Nous avons besoin également d'une caméra pour servir de point de vue à notre scène, celle-ci sera à l'écart du modèle // regardant dans sa direction. // On conserve la rotation à part via des angles d'eulers pour la caméra free-fly - NzEulerAnglesf camAngles(0.f, -20.f, 0.f); + Nz::EulerAnglesf camAngles(0.f, -20.f, 0.f); - NzCamera camera; - camera.SetPosition(0.f, 0.25f, 2.f); // On place la caméra à l'écart - camera.SetRotation(camAngles); + // Nous créons donc une seconde entité + // Note: La création d'entité est une opération légère au sein du moteur, mais plus vous aurez d'entités et plus le processeur devra travailler. + Ndk::EntityHandle camera = world.CreateEntity(); + + // Notre caméra a elle aussi besoin d'être positionnée dans la scène + Ndk::NodeComponent& cameraNode = camera->AddComponent(); + cameraNode.SetPosition(0.f, 0.25f, 2.f); // On place la caméra à l'écart + cameraNode.SetRotation(camAngles); + + // Et dispose d'un composant pour chaque point de vue de la scène, le CameraComponent + Ndk::CameraComponent& cameraComp = camera->AddComponent(); // Et on n'oublie pas de définir les plans délimitant le champs de vision // (Seul ce qui se trouvera entre les deux plans sera rendu) // La distance entre l'oeil et le plan éloigné - camera.SetZFar(5000.f); + cameraComp.SetZFar(5000.f); // La distance entre l'oeil et le plan rapproché (0 est une valeur interdite car la division par zéro l'est également) - camera.SetZNear(0.1f); - - // On indique à la scène que le viewer (Le point de vue) sera la caméra - scene.SetViewer(camera); + cameraComp.SetZNear(0.1f); // 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. @@ -160,40 +206,47 @@ int main() // -PointLight: Lumière située à un endroit précis, envoyant de la lumière finie dans toutes les directions // -SpotLight: Lumière située à un endroit précis, envoyant de la lumière vers un endroit donné, avec un angle de diffusion - // Nous créons une lumière directionnelle pour représenter la nébuleuse de notre skybox - NzLight* nebulaLight = scene.CreateNode(nzLightType_Directional); + // Nous allons créer une lumière directionnelle pour représenter la nébuleuse de notre skybox + // Encore une fois, nous créons notre entité + Ndk::EntityHandle nebulaLight = world.CreateEntity(); + + // Lui ajoutons une position dans la scène + Ndk::NodeComponent& nebulaLightNode = nebulaLight->AddComponent(); + + // Et ensuite le composant principal, le LightComponent + Ndk::LightComponent& nebulaLightComp = nebulaLight->AddComponent(Nz::LightType_Directional); // Il nous faut ensuite configurer la lumière // Pour commencer, sa couleur, la nébuleuse étant d'une couleur jaune, j'ai choisi ces valeurs - nebulaLight->SetColor(NzColor(255, 182, 90)); + nebulaLightComp.SetColor(Nz::Color(255, 182, 90)); // Nous appliquons ensuite une rotation de sorte que la lumière dans la même direction que la nébuleuse - nebulaLight->SetRotation(NzEulerAnglesf(0.f, 102.f, 0.f)); + nebulaLightNode.SetRotation(Nz::EulerAnglesf(0.f, 102.f, 0.f)); // Nous allons maintenant créer la fenêtre, dans laquelle nous ferons nos rendus // Celle-ci demande des paramètres plus complexes // Pour commencer le mode vidéo, celui-ci va définir la taille de la zone de rendu et le nombre de bits par pixels - NzVideoMode mode = NzVideoMode::GetDesktopMode(); // Nous récupérons le mode vidéo du bureau + Nz::VideoMode mode = Nz::VideoMode::GetDesktopMode(); // Nous récupérons le mode vidéo du bureau // Nous allons prendre les trois quarts de la résolution du bureau pour notre fenêtre - mode.width *= 3.f/4.f; - mode.height *= 3.f/4.f; + mode.width = 3 * mode.width / 4; + mode.height = 3 * mode.height / 4; // Maintenant le titre, rien de plus simple... - NzString windowTitle = "Nazara Demo - First scene"; + Nz::String windowTitle = "Nazara Demo - First scene"; // Ensuite, le "style" de la fenêtre, possède-t-elle des bordures, peut-on cliquer sur la croix de fermeture, // peut-on la redimensionner, ... - nzWindowStyleFlags style = nzWindowStyle_Default; // Nous prenons le style par défaut, autorisant tout ce que je viens de citer + Nz::WindowStyleFlags style = Nz::WindowStyle_Default; // Nous prenons le style par défaut, autorisant tout ce que je viens de citer // Ensuite, les paramètres du contexte de rendu // On peut configurer le niveau d'antialiasing, le nombre de bits du depth buffer et le nombre de bits du stencil buffer // Nous désirons avoir un peu d'antialiasing (4x), les valeurs par défaut pour le reste nous conviendrons très bien - NzRenderTargetParameters parameters; + Nz::RenderTargetParameters parameters; parameters.antialiasingLevel = 4; - NzRenderWindow window(mode, windowTitle, style, parameters); + Nz::RenderWindow window(mode, windowTitle, style, parameters); if (!window.IsValid()) { std::cout << "Failed to create render window" << std::endl; @@ -203,62 +256,63 @@ int main() } // On fait disparaître le curseur de la souris - window.SetCursor(nzWindowCursor_None); + window.SetCursor(Nz::WindowCursor_None); // On lie la caméra à la fenêtre - camera.SetTarget(window); + cameraComp.SetTarget(&window); // Et on créé deux horloges pour gérer le temps - NzClock secondClock, updateClock; + Nz::Clock secondClock, updateClock; + Nz::UInt64 updateAccumulator = 0; // Ainsi qu'un compteur de FPS improvisé unsigned int fps = 0; // Quelques variables de plus pour notre caméra bool smoothMovement = true; - NzVector3f targetPos = camera.GetPosition(); + Nz::Vector3f targetPos = cameraNode.GetPosition(); // Début de la boucle de rendu du programme while (window.IsOpen()) { // Ensuite nous allons traiter les évènements (Étape indispensable pour la fenêtre) - NzEvent event; + Nz::WindowEvent event; while (window.PollEvent(&event)) { switch (event.type) { - case nzEventType_MouseMoved: // La souris a bougé + case Nz::WindowEventType_MouseMoved: // La souris a bougé { // Gestion de la caméra free-fly (Rotation) float sensitivity = 0.3f; // Sensibilité de la souris // On modifie l'angle de la caméra grâce au déplacement relatif sur X de la souris - camAngles.yaw = NzNormalizeAngle(camAngles.yaw - event.mouseMove.deltaX*sensitivity); + camAngles.yaw = Nz::NormalizeAngle(camAngles.yaw - event.mouseMove.deltaX*sensitivity); // Idem, mais pour éviter les problèmes de calcul de la matrice de vue, on restreint les angles - camAngles.pitch = NzClamp(camAngles.pitch - event.mouseMove.deltaY*sensitivity, -89.f, 89.f); + camAngles.pitch = Nz::Clamp(camAngles.pitch - event.mouseMove.deltaY*sensitivity, -89.f, 89.f); // On applique les angles d'Euler à notre caméra - camera.SetRotation(camAngles); + cameraNode.SetRotation(camAngles); // Pour éviter que le curseur ne sorte de l'écran, nous le renvoyons au centre de la fenêtre // Cette fonction est codée de sorte à ne pas provoquer d'évènement MouseMoved - NzMouse::SetPosition(window.GetWidth()/2, window.GetHeight()/2, window); + Nz::Mouse::SetPosition(window.GetWidth()/2, window.GetHeight()/2, window); break; } - case nzEventType_Quit: // L'utilisateur a cliqué sur la croix, ou l'OS veut terminer notre programme + case Nz::WindowEventType_Quit: // L'utilisateur a cliqué sur la croix, ou l'OS veut terminer notre programme window.Close(); // On demande la fermeture de la fenêtre (Qui aura lieu au prochain tour de boucle) break; - case nzEventType_KeyPressed: // Une touche a été pressée ! - if (event.key.code == NzKeyboard::Key::Escape) + case Nz::WindowEventType_KeyPressed: // Une touche a été pressée ! + if (event.key.code == Nz::Keyboard::Key::Escape) window.Close(); - else if (event.key.code == NzKeyboard::F1) + else if (event.key.code == Nz::Keyboard::F1) { if (smoothMovement) { - targetPos = camera.GetPosition(); + targetPos = cameraNode.GetPosition(); smoothMovement = false; } else @@ -271,68 +325,63 @@ int main() } } + Nz::UInt64 elapsedUS = updateClock.GetMicroseconds(); + // On relance l'horloge + updateClock.Restart(); + // Mise à jour (Caméra) - if (updateClock.GetMilliseconds() >= 1000/60) // 60 fois par seconde + const Nz::UInt64 updateRate = 1000000 / 60; // 60 fois par seconde + updateAccumulator += elapsedUS; + + if (updateAccumulator >= updateRate) { - // Le temps écoulé depuis la dernière fois que ce bloc a été exécuté - float elapsedTime = updateClock.GetSeconds(); + // Le temps écoulé en seconde depuis la dernière fois que ce bloc a été exécuté + float elapsedTime = updateAccumulator / 1000000.f; + std::cout << elapsedTime << std::endl; // Vitesse de déplacement de la caméra float cameraSpeed = 3.f * elapsedTime; // Trois mètres par seconde // Si la touche espace est enfoncée, notre vitesse de déplacement est multipliée par deux - if (NzKeyboard::IsKeyPressed(NzKeyboard::Space)) + if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::Space)) cameraSpeed *= 2.f; // Pour que nos déplacement soient liés à la rotation de la caméra, nous allons utiliser // les directions locales de la caméra // Si la flèche du haut ou la touche Z (vive ZQSD) est pressée, on avance - if (NzKeyboard::IsKeyPressed(NzKeyboard::Up) || NzKeyboard::IsKeyPressed(NzKeyboard::Z)) - targetPos += camera.GetForward() * cameraSpeed; + if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::Up) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::Z)) + targetPos += cameraNode.GetForward() * cameraSpeed; // Si la flèche du bas ou la touche S est pressée, on recule - if (NzKeyboard::IsKeyPressed(NzKeyboard::Down) || NzKeyboard::IsKeyPressed(NzKeyboard::S)) - targetPos += camera.GetBackward() * cameraSpeed; + if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::Down) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::S)) + targetPos += cameraNode.GetBackward() * cameraSpeed; // Etc... - if (NzKeyboard::IsKeyPressed(NzKeyboard::Left) || NzKeyboard::IsKeyPressed(NzKeyboard::Q)) - targetPos += camera.GetLeft() * cameraSpeed; + if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::Left) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::Q)) + targetPos += cameraNode.GetLeft() * cameraSpeed; // Etc... - if (NzKeyboard::IsKeyPressed(NzKeyboard::Right) || NzKeyboard::IsKeyPressed(NzKeyboard::D)) - targetPos += camera.GetRight() * cameraSpeed; + if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::Right) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::D)) + targetPos += cameraNode.GetRight() * cameraSpeed; // Majuscule pour monter, notez l'utilisation d'une direction globale (Non-affectée par la rotation) - if (NzKeyboard::IsKeyPressed(NzKeyboard::LShift) || NzKeyboard::IsKeyPressed(NzKeyboard::RShift)) - targetPos += NzVector3f::Up() * cameraSpeed; + if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::LShift) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::RShift)) + targetPos += Nz::Vector3f::Up() * cameraSpeed; // Contrôle (Gauche ou droite) pour descendre dans l'espace global, etc... - if (NzKeyboard::IsKeyPressed(NzKeyboard::LControl) || NzKeyboard::IsKeyPressed(NzKeyboard::RControl)) - targetPos += NzVector3f::Down() * cameraSpeed; + if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::LControl) || Nz::Keyboard::IsKeyPressed(Nz::Keyboard::RControl)) + targetPos += Nz::Vector3f::Down() * cameraSpeed; - camera.SetPosition((smoothMovement) ? DampedString(camera.GetPosition(), targetPos, elapsedTime) : targetPos, nzCoordSys_Global); + cameraNode.SetPosition((smoothMovement) ? DampedString(cameraNode.GetPosition(), targetPos, elapsedTime) : targetPos, Nz::CoordSys_Global); - // On relance l'horloge - updateClock.Restart(); + updateAccumulator = 0; } - // Rendu de la scène: - // On procède maintenant au rendu de la scène en elle-même, celui-ci se décompose en quatre étapes distinctes - - // Pour commencer, on met à jour la scène, ceci appelle la méthode Update de tous les SceneNode enregistrés - // pour la mise à jour globale (Scene::RegisterForUpdate) - scene.Update(); - - // Ensuite il y a le calcul de visibilité, la scène se sert de la caméra active pour effectuer un test de visibilité - // afin de faire une liste des SceneNode visibles (Ex: Frustum culling) - scene.Cull(); - - // Ensuite il y a la mise à jour des SceneNode enregistrés pour la mise à jour visible (Exemple: Terrain) - scene.UpdateVisible(); - - // Pour terminer, il y a l'affichage en lui-même, de façon organisée et optimisée (Batching) - scene.Draw(); + // Et maintenant pour rendre la scène, il nous suffit de mettre à jour le monde en lui envoyant le temps depuis la dernière mise à jour + // Note: La plupart des systèmes, à l'exception de celui de rendu, ont une fréquence de mise à jour fixe (modifiable) + // Il n'est donc pas nécessaire de limiter vous-même les mises à jour du monde + world.Update(elapsedUS / 1000000.f); // Après avoir dessiné sur la fenêtre, il faut s'assurer qu'elle affiche cela // Cet appel ne fait rien d'autre qu'échanger les buffers de rendu (Double Buffering) @@ -344,7 +393,7 @@ int main() if (secondClock.GetMilliseconds() >= 1000) // Toutes les secondes { // Et on insère ces données dans le titre de la fenêtre - window.SetTitle(windowTitle + " - " + NzString::Number(fps) + " FPS"); + window.SetTitle(windowTitle + " - " + Nz::String::Number(fps) + " FPS"); /* Note: En C++11 il est possible d'insérer de l'Unicode de façon standard, quel que soit l'encodage du fichier, @@ -364,7 +413,7 @@ int main() return EXIT_SUCCESS; } -NzVector3f DampedString(const NzVector3f& currentPos, const NzVector3f& targetPos, float frametime, float springStrength) +Nz::Vector3f DampedString(const Nz::Vector3f& currentPos, const Nz::Vector3f& targetPos, float frametime, float springStrength) { // Je ne suis pas l'auteur de cette fonction // Je l'ai reprise du programme "Floaty Camera Example" et adaptée au C++ @@ -372,13 +421,13 @@ NzVector3f DampedString(const NzVector3f& currentPos, const NzVector3f& targetPo // Tout le mérite revient à l'auteur (Qui me permettra ainsi d'améliorer les démos, voire même le moteur) // calculate the displacement between the target and the current position - NzVector3f displacement = targetPos - currentPos; + Nz::Vector3f displacement = targetPos - currentPos; // whats the distance between them? float displacementLength = displacement.GetLength(); // Stops small position fluctuations (integration errors probably - since only using euler) - if (NzNumberEquals(displacementLength, 0.f)) + if (Nz::NumberEquals(displacementLength, 0.f)) return currentPos; float invDisplacementLength = 1.f/displacementLength;