Nazara now use meters as units

Added scale mesh parameter
Added smooth camera to FirstScene demo


Former-commit-id: b3985e10d84512e3b32f2569ac034ba63ace589a
This commit is contained in:
Lynix 2013-06-13 19:29:59 +02:00
parent 3294f18608
commit bb99d044bb
6 changed files with 109 additions and 22 deletions

View File

@ -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;
}

View File

@ -27,6 +27,9 @@ struct NAZARA_API NzMeshParams
// Si ceci sera le stockage utilisé par les buffers
nzBufferStorage storage = nzBufferStorage_Hardware;
// La mise à l'échelle éventuelle que subira le mesh
NzVector3f scale = NzVector3f::Unit();
// Charger une version animée du mesh si possible ?
bool animated = true;

View File

@ -123,7 +123,8 @@ namespace
NzMeshVertex& vertex = meshVertices[positionIt.second];
const NzVector4f& vec = positions[positionIt.first];
vertex.position.Set(vec.x/vec.w, vec.y/vec.w, vec.z/vec.w);
vertex.position.Set(vec.x, vec.y, vec.z);
vertex.position *= parameters.mesh.scale/vec.w;
int index;

View File

@ -183,6 +183,10 @@ namespace
NzByteSwap(&translate.z, sizeof(float));
#endif
// Un personnage de taille moyenne fait ~50 unités de haut dans Quake 2
// Avec Nazara, 1 unité = 1 mètre, nous devons donc adapter l'échelle
scale *= parameters.scale/29.f; // 50/29 = 1.72 (Soit 1.72 mètre, proche de la taille moyenne d'un individu)
NzBufferMapper<NzVertexBuffer> vertexMapper(vertexBuffer.get(), nzBufferAccess_DiscardAndWrite);
NzMeshVertex* vertex = reinterpret_cast<NzMeshVertex*>(vertexMapper.GetPointer());

View File

@ -155,6 +155,10 @@ bool NzMD5MeshParser::Parse(NzMesh* mesh)
NzQuaternionf rotationQuat = NzEulerAnglesf(-90.f, 180.f, 0.f);
NzString baseDir = m_stream.GetDirectory();
// Le hellknight de Doom 3 fait ~120 unités, et il est dit qu'il fait trois mètres
// Nous réduisons donc sa taille de 1/40
NzVector3f scale(m_parameters.scale/40.f);
if (m_parameters.animated)
{
if (!mesh->CreateSkeletal(m_joints.size())) // Ne devrait jamais échouer
@ -175,8 +179,11 @@ bool NzMD5MeshParser::Parse(NzMesh* mesh)
joint->SetName(m_joints[i].name);
NzMatrix4f bindMatrix;
bindMatrix.MakeRotation((parent >= 0) ? m_joints[i].bindOrient : rotationQuat * m_joints[i].bindOrient);
bindMatrix.SetTranslation((parent >= 0) ? m_joints[i].bindPos : rotationQuat * m_joints[i].bindPos); // Plus rapide que de multiplier par une matrice de translation
if (parent >= 0)
bindMatrix.MakeTransform(m_joints[i].bindPos, m_joints[i].bindOrient);
else
bindMatrix.MakeTransform(rotationQuat * m_joints[i].bindPos, rotationQuat * m_joints[i].bindOrient, scale);
joint->SetInverseBindMatrix(bindMatrix.InverseAffine());
}
@ -243,7 +250,7 @@ bool NzMD5MeshParser::Parse(NzMesh* mesh)
vertexWeight->weights[j] = vertex.startWeight + j;
}
bindPosVertex->position = finalPos;
bindPosVertex->position = scale * finalPos;
bindPosVertex->uv.Set(vertex.uv.x, 1.f-vertex.uv.y);
bindPosVertex++;
vertexWeight++;
@ -323,7 +330,7 @@ bool NzMD5MeshParser::Parse(NzMesh* mesh)
}
// On retourne le modèle dans le bon sens
vertex->position = rotationQuat * finalPos;
vertex->position = scale * (rotationQuat * finalPos);
vertex->uv.Set(md5Vertex.uv.x, 1.f - md5Vertex.uv.y);
vertex++;
}
@ -349,6 +356,7 @@ bool NzMD5MeshParser::Parse(NzMesh* mesh)
// Material
mesh->SetMaterial(i, baseDir + md5Mesh.shader);
subMesh->GenerateAABB();
subMesh->GenerateNormalsAndTangents();
subMesh->SetMaterialIndex(i);

View File

@ -37,6 +37,12 @@ bool NzMeshParams::IsValid() const
return false;
}
if (scale == NzVector3f::Zero())
{
NazaraError("Invalid scale");
return false;
}
return true;
}