|
|
|
|
@ -4,6 +4,9 @@
|
|
|
|
|
#include <Nazara/Utility.hpp> // Module utilitaire
|
|
|
|
|
#include <iostream>
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
|
|
|
|
|
int main()
|
|
|
|
|
{
|
|
|
|
|
// Pour commencer, nous initialisons le module Graphique, celui-ci va provoquer l'initialisation (dans l'ordre),
|
|
|
|
|
@ -24,7 +27,7 @@ int main()
|
|
|
|
|
// 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 NzCamera
|
|
|
|
|
// 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.
|
|
|
|
|
@ -70,9 +73,19 @@ int main()
|
|
|
|
|
// Nous choisirons ici un vaisseau spatial (Quoi de mieux pour une scène spatiale ?)
|
|
|
|
|
NzModel spaceship;
|
|
|
|
|
|
|
|
|
|
// Une structure permettant de paramétrer le chargement des modèles
|
|
|
|
|
NzModelParameters params;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
|
// 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
|
|
|
|
|
|
|
|
|
|
// 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"))
|
|
|
|
|
if (!spaceship.LoadFromFile("resources/Spaceship/spaceship.obj", params))
|
|
|
|
|
{
|
|
|
|
|
std::cout << "Failed to load spaceship" << std::endl;
|
|
|
|
|
std::getchar();
|
|
|
|
|
@ -84,7 +97,7 @@ int main()
|
|
|
|
|
// Pour cela, nous devons accéder au mesh (maillage 3D)
|
|
|
|
|
NzMesh* mesh = spaceship.GetMesh();
|
|
|
|
|
|
|
|
|
|
std::cout << mesh->GetVertexCount() << " vertices" << std::endl;
|
|
|
|
|
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
|
|
|
|
|
@ -102,7 +115,7 @@ int main()
|
|
|
|
|
|
|
|
|
|
// Il nous reste à attacher le modèle à la scène, ce qui se fait simplement via cet appel
|
|
|
|
|
spaceship.SetParent(scene);
|
|
|
|
|
// Et voilà, à partir de maintenant le modèle fait partie de la "hiérarchie de la scène", et sera donc rendu avec la scène
|
|
|
|
|
// Et voilà, à partir de maintenant le modèle fait partie de la hiérarchie de la scène, et sera donc rendu avec cette dernière
|
|
|
|
|
|
|
|
|
|
// Nous avons besoin également d'une caméra, pour des raisons évidentes, celle-ci sera à l'écart du modèle
|
|
|
|
|
// regardant dans sa direction.
|
|
|
|
|
@ -111,7 +124,7 @@ int main()
|
|
|
|
|
NzEulerAnglesf camAngles(0.f, -20.f, 0.f);
|
|
|
|
|
|
|
|
|
|
NzCamera camera;
|
|
|
|
|
camera.SetPosition(0.f, 25.f, 200.f); // On place la caméra à l'écart
|
|
|
|
|
camera.SetPosition(0.f, 0.25f, 2.f); // On place la caméra à l'écart
|
|
|
|
|
camera.SetRotation(camAngles);
|
|
|
|
|
camera.SetParent(scene); // On l'attache également à la scène
|
|
|
|
|
|
|
|
|
|
@ -122,7 +135,7 @@ int main()
|
|
|
|
|
camera.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(1.f);
|
|
|
|
|
camera.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.
|
|
|
|
|
@ -198,6 +211,10 @@ int main()
|
|
|
|
|
// 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();
|
|
|
|
|
|
|
|
|
|
// Début de la boucle de rendu du programme
|
|
|
|
|
while (window.IsOpen())
|
|
|
|
|
{
|
|
|
|
|
@ -210,7 +227,7 @@ int main()
|
|
|
|
|
case nzEventType_MouseMoved: // La souris a bougé
|
|
|
|
|
{
|
|
|
|
|
// Gestion de la caméra free-fly (Rotation)
|
|
|
|
|
float sensitivity = 0.8f; // Sensibilité de la souris
|
|
|
|
|
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);
|
|
|
|
|
@ -234,6 +251,16 @@ int main()
|
|
|
|
|
case nzEventType_KeyPressed: // Une touche a été pressée !
|
|
|
|
|
if (event.key.code == NzKeyboard::Key::Escape)
|
|
|
|
|
window.Close();
|
|
|
|
|
else if (event.key.code == NzKeyboard::F1)
|
|
|
|
|
{
|
|
|
|
|
if (smoothMovement)
|
|
|
|
|
{
|
|
|
|
|
targetPos = camera.GetPosition();
|
|
|
|
|
smoothMovement = false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
smoothMovement = true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
@ -244,39 +271,44 @@ int main()
|
|
|
|
|
// Mise à jour (Caméra)
|
|
|
|
|
if (updateClock.GetMilliseconds() >= 1000/60) // 60 fois par seconde
|
|
|
|
|
{
|
|
|
|
|
// Gestion de la caméra free-fly (Déplacement)
|
|
|
|
|
float cameraSpeed = 300.f; // Unités par seconde
|
|
|
|
|
// Le temps écoulé depuis la dernière fois que ce bloc a été exécuté
|
|
|
|
|
float elapsedTime = updateClock.GetSeconds();
|
|
|
|
|
|
|
|
|
|
// 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))
|
|
|
|
|
cameraSpeed *= 2.f;
|
|
|
|
|
|
|
|
|
|
// Move agit par défaut dans l'espace local de la caméra, autrement dit la rotation est prise en compte
|
|
|
|
|
// 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))
|
|
|
|
|
camera.Move(NzVector3f::Forward() * cameraSpeed * elapsedTime);
|
|
|
|
|
targetPos += camera.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))
|
|
|
|
|
camera.Move(NzVector3f::Backward() * cameraSpeed * elapsedTime);
|
|
|
|
|
targetPos += camera.GetBackward() * cameraSpeed;
|
|
|
|
|
|
|
|
|
|
// Etc...
|
|
|
|
|
if (NzKeyboard::IsKeyPressed(NzKeyboard::Left) || NzKeyboard::IsKeyPressed(NzKeyboard::Q))
|
|
|
|
|
camera.Move(NzVector3f::Left() * cameraSpeed * elapsedTime);
|
|
|
|
|
targetPos += camera.GetLeft() * cameraSpeed;
|
|
|
|
|
|
|
|
|
|
// Etc...
|
|
|
|
|
if (NzKeyboard::IsKeyPressed(NzKeyboard::Right) || NzKeyboard::IsKeyPressed(NzKeyboard::D))
|
|
|
|
|
camera.Move(NzVector3f::Right() * cameraSpeed * elapsedTime);
|
|
|
|
|
targetPos += camera.GetRight() * cameraSpeed;
|
|
|
|
|
|
|
|
|
|
// Majuscule pour monter, mais dans l'espace global (Sans tenir compte de la rotation)
|
|
|
|
|
// 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))
|
|
|
|
|
camera.Move(NzVector3f::Up() * cameraSpeed * elapsedTime, nzCoordSys_Global);
|
|
|
|
|
targetPos += NzVector3f::Up() * cameraSpeed;
|
|
|
|
|
|
|
|
|
|
// Contrôle (Gauche ou droite) pour descendre dans l'espace global, etc...
|
|
|
|
|
if (NzKeyboard::IsKeyPressed(NzKeyboard::LControl) || NzKeyboard::IsKeyPressed(NzKeyboard::RControl))
|
|
|
|
|
camera.Move(NzVector3f::Down() * cameraSpeed * elapsedTime, nzCoordSys_Global);
|
|
|
|
|
targetPos += NzVector3f::Down() * cameraSpeed;
|
|
|
|
|
|
|
|
|
|
camera.SetPosition((smoothMovement) ? DampedString(camera.GetPosition(), targetPos, elapsedTime) : targetPos, nzCoordSys_Global);
|
|
|
|
|
|
|
|
|
|
// On relance l'horloge
|
|
|
|
|
updateClock.Restart();
|
|
|
|
|
@ -332,3 +364,36 @@ int main()
|
|
|
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NzVector3f DampedString(const NzVector3f& currentPos, const NzVector3f& 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++
|
|
|
|
|
// Trouvé ici: http://nccastaff.bournemouth.ac.uk/jmacey/RobTheBloke/www/opengl_programming.html#4
|
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
|
|
// whats the distance between them?
|
|
|
|
|
float displacementLength = displacement.GetLength();
|
|
|
|
|
|
|
|
|
|
// Stops small position fluctuations (integration errors probably - since only using euler)
|
|
|
|
|
if (displacementLength < 0.0001f)
|
|
|
|
|
return currentPos;
|
|
|
|
|
|
|
|
|
|
float invDisplacementLength = 1.f/displacementLength;
|
|
|
|
|
|
|
|
|
|
const float dampConstant = 0.000065f; // Something v.small to offset 1/ displacement length
|
|
|
|
|
|
|
|
|
|
// the strength of the spring increases the further away the camera is from the target.
|
|
|
|
|
float springMagitude = springStrength*displacementLength + dampConstant*invDisplacementLength;
|
|
|
|
|
|
|
|
|
|
// Normalise the displacement and scale by the spring magnitude
|
|
|
|
|
// and the amount of time passed
|
|
|
|
|
float scalar = invDisplacementLength * springMagitude * frametime;
|
|
|
|
|
displacement *= scalar;
|
|
|
|
|
|
|
|
|
|
// move the camera a bit towards the target
|
|
|
|
|
return currentPos + displacement;
|
|
|
|
|
}
|
|
|
|
|
|