diff --git a/build/scripts/module/renderer.lua b/build/scripts/module/renderer.lua index 391830ee2..f154cbbd7 100644 --- a/build/scripts/module/renderer.lua +++ b/build/scripts/module/renderer.lua @@ -1,40 +1,42 @@ -if (not _OPTIONS["one-library"]) then - project "NazaraRenderer" -end - -files -{ - "../include/Nazara/Renderer/**.hpp", - "../include/Nazara/Renderer/**.inl", - "../src/Nazara/Renderer/**.hpp", - "../src/Nazara/Renderer/**.cpp" -} - -if (os.is("windows")) then - excludes { "../src/Nazara/Renderer/Posix/*.hpp", "../src/Nazara/Renderer/Posix/*.cpp" } - links "gdi32" - links "opengl32" - links "winmm" -else - excludes { "../src/Nazara/Renderer/Win32/*.hpp", "../src/Nazara/Renderer/Win32/*.cpp" } -end - -if (_OPTIONS["one-library"]) then - excludes "../src/Nazara/Renderer/Debug/Leaks.cpp" -else - configuration "DebugStatic" - links "NazaraCore-s-d" - links "NazaraUtility-s-d" - - configuration "ReleaseStatic" - links "NazaraCore-s" - links "NazaraUtility-s" - - configuration "DebugDLL" - links "NazaraCore-d" - links "NazaraUtility-d" - - configuration "ReleaseDLL" - links "NazaraCore" - links "NazaraUtility" +if (not _OPTIONS["one-library"]) then + project "NazaraRenderer" +end + +defines "NAZARA_RENDERER_OPENGL" + +files +{ + "../include/Nazara/Renderer/**.hpp", + "../include/Nazara/Renderer/**.inl", + "../src/Nazara/Renderer/**.hpp", + "../src/Nazara/Renderer/**.cpp" +} + +if (os.is("windows")) then + excludes { "../src/Nazara/Renderer/Posix/*.hpp", "../src/Nazara/Renderer/Posix/*.cpp" } + links "gdi32" + links "opengl32" + links "winmm" +else + excludes { "../src/Nazara/Renderer/Win32/*.hpp", "../src/Nazara/Renderer/Win32/*.cpp" } +end + +if (_OPTIONS["one-library"]) then + excludes "../src/Nazara/Renderer/Debug/Leaks.cpp" +else + configuration "DebugStatic" + links "NazaraCore-s-d" + links "NazaraUtility-s-d" + + configuration "ReleaseStatic" + links "NazaraCore-s" + links "NazaraUtility-s" + + configuration "DebugDLL" + links "NazaraCore-d" + links "NazaraUtility-d" + + configuration "ReleaseDLL" + links "NazaraCore" + links "NazaraUtility" end \ No newline at end of file diff --git a/examples/AnimatedMesh/main.cpp b/examples/AnimatedMesh/main.cpp index 12ba84677..fbe4af5c2 100644 --- a/examples/AnimatedMesh/main.cpp +++ b/examples/AnimatedMesh/main.cpp @@ -1,613 +1,662 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// Une structure pour contenir nos informations (Cette structure est très simpliste) -struct Model -{ - NzMatrix4f matrix; // Les transformations subies par le modèle - NzMesh mesh; // Le mesh - NzTexture texture; // Sa texture -}; - -struct AnimatedModel : public Model -{ - // Quelques variables pour l'animation - const NzSequence* currentSequence = nullptr; // La séquence en cours - float interpolation = 0.f; // La valeur de l'interpolation ([0..1], si dépasse 1, on passe à la frame suivante) - unsigned int currentFrame = 0; // La première frame - unsigned int nextFrame; // La seconde frame, l'animation est interpollée entre ces deux-là -}; - -void AnimateModel(AnimatedModel& moedel, float elapsed); -bool CreateCheckerTexture(NzTexture* texture); -bool CreateFloorMesh(NzMesh* mesh); -void DrawModel(const Model& model); -void SetSequence(AnimatedModel& model, const NzString& name); - -int main() -{ - // Tout d'abord on affiche les instructions - std::cout << "Camera controls: ZQSD" << std::endl; - std::cout << "Dr. Freak controls: Up, down, left and right" << std::endl; - std::cout << "Escape to quit" << std::endl; - - // Cette ligne active le mode de compatibilité d'OpenGL lors de l'initialisation de Nazara (Nécessaire pour le shader) - NzContextParameters::defaultCompatibilityProfile = true; - - // Maintenant nous initialisons le Renderer (Qui initialisera le noyau ainsi que le module utilitaire) - // Cette étape est obligatoire pour beaucoup de fonctionnalités (Notamment le chargement de ressources et le rendu) - NzInitializer renderer; - if (!renderer) - { - // Ça n'a pas fonctionné, le pourquoi se trouve dans le fichier NazaraLog.log - std::cout << "Failed to initialize Nazara, see NazaraLog.log for further informations" << std::endl; - std::getchar(); // On laise le temps de voir l'erreur - return EXIT_FAILURE; - } - - // Maintenant nous pouvons utiliser le moteur comme bon nous semble, tout d'abord nous allons charger les ressources - - // Charger une ressource se fait actuellement manuellement, mais un ResourceManager est à venir - // Vous initialisez une ressource, et la chargez via sa méthode LoadFrom[File|Memory|Stream] - // Note: il est possible de donner des instructions au loader (qui va charger le fichier en ressource) via les ResourceParameters - NzMeshParams parameters; - - // Le loader doit-il automatiquement charger les animations ? - // Attention, ce paramètre possède une signification différente selon le type d'animation du mesh. - // -Pour les animations keyframe (image-clé), c'est la seule et unique façon de charger les animations, étant donné - // qu'elles sont fourniees avec le mesh. - // -Pour les animations squelettiques, le loader ne fera que charger automatiquement l'animation associée au mesh s'il le peut - // Dans les deux cas, les paramètres d'animations (parameters.animation) seront utilisés - parameters.loadAnimations = true; // Vaut true par défaut - - // Pour qu'un mesh puisse être rendu, il doit être stocké du côté de la carte graphique (Hardware), mais il est parfois utile de - // le stocker côté RAM, par exemple pour le moteur physique. En sachant qu'il est facile de changer le stockage d'un buffer. - parameters.storage = nzBufferStorage_Hardware; // Vaut nzBufferStorage_Hardware par défaut - - AnimatedModel drfreak; - if (!drfreak.mesh.LoadFromFile("resources/drfreak.md2", parameters)) // On charge notre bon vieux docteur avec les paramètres de chargement. - { - // Le chargement n'a pas fonctionné, le modèle est peut-être corrompu/non-supporté, ou alors n'existe pas. - std::cout << "Failed to load mesh" << std::endl; - std::getchar(); // On laise le temps de voir l'erreur - return EXIT_FAILURE; - } - - if (!drfreak.mesh.HasAnimation()) // Le mesh possède-t-il des animations ? - { - // Cette démo n'a aucun intérêt sans animations - std::cout << "Mesh has no animation" << std::endl; - std::getchar(); - return EXIT_FAILURE; - } - - SetSequence(drfreak, "stand"); - - // Il est possible que le mesh possède un ou plusieurs skin, nous utiliserons cette information pour charger une texture - if (drfreak.mesh.HasSkin()) - { - // Contrairement aux autres ressources, la texture n'est pas critique - if (drfreak.texture.LoadFromFile("resources/" + drfreak.mesh.GetSkin())) - drfreak.texture.SetFilterMode(nzTextureFilter_Bilinear); // Appliquons-lui un filtrage bilinéaire - else - std::cout << "Failed to load texture" << std::endl; - } - - if (!drfreak.texture.IsValid()) // Les méthodes Resource::IsValid indiquent si la ressource a été correctement créée - { - std::cout << "Creating checker texture for mesh" << std::endl; - - if (!CreateCheckerTexture(&drfreak.texture)) - { - std::cout << "Failed to create mesh texture" << std::endl; - std::getchar(); - return EXIT_FAILURE; - } - } - - // Nous créons maintenant notre sol - Model floor; - if (!CreateFloorMesh(&floor.mesh)) - { - std::cout << "Failed to create floor" << std::endl; - std::getchar(); - return EXIT_FAILURE; - } - - if (!CreateCheckerTexture(&floor.texture)) - { - std::cout << "Failed to create floor texture" << std::endl; - std::getchar(); - return EXIT_FAILURE; - } - - // Le sol ne subit aucune transformation - floor.matrix.MakeIdentity(); - - // Pour effectuer un rendu, il faut que la carte graphique sache quoi faire. - // Les shaders sont de petits programmes qui donnent des instructions à la carte graphique lors du pipeline. - // Ils sont aujourd'hui indispensables pour un rendu 3D, mais sont très utiles pour divers effets ! - // Il existe plusieurs langages de shaders, GLSL pour OpenGL, HLSL pour Direct3D et Cg qui peut être utilisé pour les deux. - // Le Renderer de Nazara utilise OpenGL, par conséquent nous utiliserons le GLSL - // La méthode NzShader::IsLanguageSupported permet de savoir si un langage est supporté. - NzShader shader; - if (!shader.Create(nzShaderLanguage_GLSL)) - { - std::cout << "Failed to load shader" << std::endl; - std::getchar(); - return EXIT_FAILURE; - } - - // Une fois le shader créé, nous devons lui spécifier les codes sources de nos shaders - // Pour notre exemple nous prendrons un shader très simple - // Un shader doit obligatoirement posséder au moins deux codes, un pour le fragment shader et un pour le vertex shader - if (!shader.LoadFromFile(nzShaderType_Fragment, "shaders/basic.frag")) - { - std::cout << "Failed to load fragment shader from file" << std::endl; - // À la différence des autres ressources, le shader possède un log qui peut indiquer les erreurs en cas d'échec - std::cout << "Log: " << shader.GetLog() << std::endl; - std::getchar(); - return EXIT_FAILURE; - } - - // Maintenant le vertex shader - if (!shader.LoadFromFile(nzShaderType_Vertex, "shaders/basic.vert")) - { - std::cout << "Failed to load vertex shader from file" << std::endl; - std::cout << "Log: " << shader.GetLog() << std::endl; - std::getchar(); - return EXIT_FAILURE; - } - - // Une fois le code source de nos shaders chargé, nous pouvons le compiler, afin de le rendre utilisable - if (!shader.Compile()) - { - std::cout << "Failed to compile shader" << std::endl; - std::cout << "Log: " << shader.GetLog() << std::endl; - std::getchar(); - return EXIT_FAILURE; - } - - // Nos ressources sont chargées, et c'est bien beau, mais il nous faudrait une fenêtre pour afficher tout ça - // Window représente une fenêtre singulière, pour y effectuer un rendu il nous faut une RenderWindow - // Tout d'abord, sa taille, disons celle du bureau divisé par deux - NzVideoMode mode = NzVideoMode::GetDesktopMode(); - mode.width /= 2; - mode.height /= 2; - - NzString title = "Nazara Demo - AnimatedMesh"; - - NzRenderWindow window; - // Le premier argument définit la taille de rendu de la fenêtre (Si elle possède une bordure elle sera légèrement plus grande) - // Le deuxième argument est le titre de la fenêtre lors de sa création, vous pouvez le modifier à tout moment via window.SetTitle - // Le troisième argument représente la décoration de la fenêtre, sa bordure, ses boutons. - // Attention que cela permet à la fenêtre d'envoyer des évènements, et de changer sa taille - // Par défaut le troisième argument vaut nzWindowStyle_Default (Bordure + Bouton de fermeture + Redimensionnement) - if (!window.Create(mode, title, nzWindowStyle_Default)) - { - std::cout << "Failed to create window" << std::endl; - std::getchar(); - return EXIT_FAILURE; - } - - // On cache le curseur - window.SetCursor(nzWindowCursor_None); - - // Nous limitons les FPS à 100 - window.SetFramerateLimit(100); - - // La matrice de projection définit la transformation du vertice 3D à un point 2D - NzRenderer::SetMatrix(nzMatrixType_Projection, NzMatrix4f::Perspective(NzDegrees(70.f), static_cast(window.GetWidth())/window.GetHeight(), 1.f, 1000.f)); - - // Notre fenêtre est créée, cependant il faut s'occuper d'elle, pour le rendu et les évènements - NzClock secondClock, updateClock; // Des horloges pour gérer le temps - unsigned int fps = 0; // Compteur de FPS - - // Quelques variables pour notre improvisation de physique - float groundPos = drfreak.mesh.GetAABB().GetMinimum().y; // Pas très exact - NzVector3f modelPos(0.f, groundPos, -50.f); - NzVector3f modelVel(0.f, 0.f, 0.f); - NzQuaternionf modelRot(NzEulerAnglesf(0.f, 0.f, 0.f)); // Les angles d'eulers sont bien plus facile à se représenter - float speed = 60.f; - - // Nous initialisons la matrice - drfreak.matrix = NzMatrix4f::Rotate(modelRot) * NzMatrix4f::Translate(modelPos); - - // Notre caméra - NzVector3f camPos(0.f, 25.f, -20.f); - NzEulerAnglesf camRot(0.f, 0.f, 0.f); - NzMatrix4f camMatrix = NzMatrix4f::Translate(camPos); - float camSpeed = 2.f; - float sensitivity = 0.5; - - // Dernière étape, nos touches - - // Chaque touche fera bouger - struct Movement - { - NzVector3f direction; // La direction - NzQuaternionf rotation; // La rotation du modèle - }; - - std::map movements; - movements[NzKeyboard::Up] = Movement{NzVector3f(0.f, 0.f, -1.f), NzQuaternionf(NzEulerAnglesf(0.f, 180.f, 0.f))}; - movements[NzKeyboard::Down] = Movement{NzVector3f(0.f, 0.f, 1.f), NzQuaternionf(NzEulerAnglesf(0.f, 0.f, 0.f))}; - movements[NzKeyboard::Left] = Movement{NzVector3f(-1.f, 0.f, 0.f), NzQuaternionf(NzEulerAnglesf(0.f, 90.f, 0.f))}; - movements[NzKeyboard::Right] = Movement{NzVector3f(1.f, 0.f, 0.f), NzQuaternionf(NzEulerAnglesf(0.f, -90.f, 0.f))}; - NzKeyboard::Key currentKey = NzKeyboard::Undefined; - - // Quelques booléens - bool camMode = true; - bool windowOpen = true; - - // On peut commencer la boucle du programme - while (windowOpen) - { - // Ici nous gérons les évènements - NzEvent event; - while (window.PollEvent(&event)) // Avons-nous un évènement dans la file ? - { - // Nous avons un évènement - switch (event.type) // De quel type est cet évènement ? - { - case nzEventType_Quit: // L'utilisateur/L'OS nous a demandé de terminer notre exécution - windowOpen = false; // Nous terminons alors la boucle - break; - - case nzEventType_MouseMoved: - { - // Si nous ne sommes pas en mode caméra, on ne traite pas l'évènement - if (!camMode) - break; - - // On modifie l'angle de la caméra grâce au déplacement relatif de la souris - camRot.yaw = NzNormalizeAngle(camRot.yaw - event.mouseMove.deltaX*sensitivity); - - // Pour éviter les loopings, on restreint les angles - camRot.pitch = NzClamp(camRot.pitch + event.mouseMove.deltaY*sensitivity, -90.f, 90.f); - - // La matrice vue représente les transformations effectuées par la caméra - // On recalcule la matrice de la caméra et on l'envoie au renderer - NzRenderer::SetMatrix(nzMatrixType_View, NzMatrix4f::Translate(camPos) * NzMatrix4f::Rotate(camRot)); - - // Pour éviter que le curseur ne sorte de l'écran, nous le renvoyons au centre de la fenêtre - NzMouse::SetPosition(window.GetWidth()/2, window.GetHeight()/2, window); - break; - } - - case nzEventType_MouseButtonPressed: - if (event.mouseButton.button == NzMouse::Left) - { - // L'utilisateur vient d'appuyer sur le bouton left de la souris - // Nous allons inverser le mode caméra et montrer/cacher le curseur en conséquence - if (camMode) - { - camMode = false; - window.SetCursor(nzWindowCursor_Default); - } - else - { - camMode = true; - window.SetCursor(nzWindowCursor_None); - } - - } - break; - - case nzEventType_Resized: // L'utilisateur a changé notre taille, le coquin ! - NzRenderer::SetViewport(NzRectui(0, 0, event.size.width, event.size.height)); // Adaptons l'affichage - - // Il nous faut aussi mettre à jour notre matrice de projection - NzRenderer::SetMatrix(nzMatrixType_Projection, NzMatrix4f::Perspective(NzDegrees(70.f), static_cast(event.size.width)/event.size.height, 1.f, 1000.f)); - break; - - case nzEventType_KeyPressed: - if (!event.key.repeated) // Si la touche n'est pas répétée - { - auto it = movements.find(event.key.code); - if (it != movements.end()) - { - // Si la touche est une touche de mouvement - SetSequence(drfreak, "run"); // On anime le personnage pour qu'il ait une animation de déplacement - - modelRot = it->second.rotation; // On change la rotation du modèle - drfreak.matrix = NzMatrix4f::Rotate(modelRot) * NzMatrix4f::Translate(modelPos); // On recalcule sa matrice - modelVel = it->second.direction * speed; // On change la vitesse de déplacement - currentKey = event.key.code; - } - } - - if (event.key.code == NzKeyboard::Escape) - windowOpen = false; - - break; - - case nzEventType_KeyReleased: - if (event.key.code == currentKey) - { - SetSequence(drfreak, "stand"); - modelVel = NzVector3f(0.f); // On arrête le déplacement - break; - } - - break; - - default: // Les autres évènements, on s'en fiche - break; - } - } - - // On active le shader et paramètrons le rendu - NzRenderer::SetShader(&shader); - NzRenderer::Enable(nzRendererParameter_DepthTest, true); - - NzRenderer::SetClearColor(128, 128, 128); - NzRenderer::Clear(nzRendererClear_Color | nzRendererClear_Depth); - - if (updateClock.GetMilliseconds() >= 1000/60) // 60 fois par seconde - { - float elapsedTime = updateClock.GetSeconds(); // Le temps depuis la dernière mise à jour - - // Déplacement de la caméra - static const NzVector3f forward(NzVector3f::UnitZ()); - static const NzVector3f left(NzVector3f::UnitX()); - static const NzVector3f up(NzVector3f::UnitY()); - - // Notre rotation sous forme de quaternion nous permet de tourner un vecteur - NzQuaternionf quaternion(camRot); - - // Par exemple ici, quaternion * forward nous permet de récupérer le vecteur de la direction "avant" - if (NzKeyboard::IsKeyPressed(NzKeyboard::Z)) - camPos += quaternion * forward * camSpeed; - - if (NzKeyboard::IsKeyPressed(NzKeyboard::S)) - camPos -= quaternion * forward * camSpeed; - - if (NzKeyboard::IsKeyPressed(NzKeyboard::Q)) - camPos += quaternion * left * camSpeed; - - if (NzKeyboard::IsKeyPressed(NzKeyboard::D)) - camPos -= quaternion * left * camSpeed; - - // En revanche, ici la hauteur est toujours la même, peu importe notre orientation - if (NzKeyboard::IsKeyPressed(NzKeyboard::Space)) - camPos += up * camSpeed; - - if (NzKeyboard::IsKeyPressed(NzKeyboard::LControl)) - camPos -= up * camSpeed; - - // Oui le quaternion et la matrice sont calculés même si la caméra ne bouge pas - // C'est une limitation de mon implémentation, qui ne sera pas présente une fois les NzSceneNode intégrés - NzRenderer::SetMatrix(nzMatrixType_View, NzMatrix4f::Translate(camPos) * NzMatrix4f::Rotate(camRot)); - - // Animation - AnimateModel(drfreak, elapsedTime); - updateClock.Restart(); - - // "Physique" - if (modelVel != NzVector3f::Zero()) - { - modelPos += modelVel * elapsedTime; - - // Mise à jour de la matrice - drfreak.matrix = NzMatrix4f::Rotate(modelRot) * NzMatrix4f::Translate(modelPos); - } - } - - // Affichage des meshs - DrawModel(floor); - - // Notre Dr. Freak possède des normales, nous pouvons alors culler les faces qu'on ne voit pas - NzRenderer::Enable(nzRendererParameter_FaceCulling, true); - - DrawModel(drfreak); - - NzRenderer::Enable(nzRendererParameter_FaceCulling, false); - - window.Display(); // Nous mettons à jour l'écran - - fps++; - - // Toutes les secondes - if (secondClock.GetMilliseconds() >= 1000) - { - window.SetTitle(title + " (FPS: " + NzString::Number(fps) + ')'); - fps = 0; - secondClock.Restart(); - } - } - - return EXIT_SUCCESS; -} - -void AnimateModel(AnimatedModel& model, float elapsed) -{ - model.interpolation += model.currentSequence->framePerSecond * elapsed; - while (model.interpolation > 1.f) - { - model.interpolation -= 1.f; - - model.currentFrame = model.nextFrame; - if (++model.nextFrame > model.currentSequence->lastFrame) - model.nextFrame = model.currentSequence->firstFrame; - } - - model.mesh.Animate(model.currentFrame, model.nextFrame, (NzKeyboard::IsKeyPressed(NzKeyboard::A)) ? 0.f : model.interpolation); -} - -bool CreateCheckerTexture(NzTexture* texture) -{ - NzImage image; - // Nous crééons une image 2D, au format RGBA8 de dimensions 128*128 (8 blocs de 16 pixels de côté) - if (!image.Create(nzImageType_2D, nzPixelFormat_RGBA8, 8*16, 8*16)) - { - // Ne devrait pas arriver (La création d'une image ne peut échouer que si l'un des argument est incorrect) - std::cout << "Failed to create image, this means a bug has been found in Nazara" << std::endl; - return false; - } - - // Pour modifier les pixels, nous pouvons accéder directement à ces derniers avec GetPixels(), ou bien à un pixel - // via [Get|Set]PixelColor, mais pour cette occasion nous utiliserons une méthode bien pratique, Fill. - unsigned int blockCountX = image.GetWidth()/16; - unsigned int blockCountY = image.GetHeight()/16; - for (unsigned int x = 0; x < blockCountX; ++x) - { - for (unsigned int y = 0; y < blockCountY; ++y) - { - // Une belle texture de damier - NzColor color = (x%2 == y%2) ? NzColor::White : NzColor::Black; - // Fill remplit une zone de la texture avec une couleur - image.Fill(color, NzRectui(x*16, y*16, 16, 16)); - } - } - - if (!texture->LoadFromImage(image)) // Nous créons notre texture depuis notre image - { - // Nous n'avons vraiment pas beaucoup de chance.. - std::cout << "Failed to load image" << std::endl; - return false; - } - - texture->SetAnisotropyLevel(NzRenderer::GetMaxAnisotropyLevel()); // Un filtrage anisotropique pour la texture - texture->SetWrapMode(nzTextureWrap_Repeat); // Si les coordonnées de texture dépassent 1.f, la texture sera répétée - - return true; -} - -bool CreateFloorMesh(NzMesh* mesh) -{ - // Cette fonction créé un mesh statique simpliste pour servir de sol - - // Nous créons un mesh statique - if (!mesh->Create(nzAnimationType_Static)) - { - // L'échec est techniquement impossible mais le moteur étant en constante évolution ... - std::cout << "Failed to create mesh" << std::endl; - return false; - } - - // Les vertex declaration ont pour seul but de décrire l'agencement d'un vertex buffer - // Elles sont composées de VertexElement, chacun décrivant un élément du buffer - NzVertexDeclaration* declaration = new NzVertexDeclaration; - - // Il y a cinq paramètres différents (stream, usage, type, offset, usageIndex) - // -Stream: À quoi serviront les données ? À définir des sommets (nzElementStream_VertexData) ou à l'instancing (nzElementStream_InstancedData) - // -Usage: Comment cette donnée doit-elle être envoyée au shader - // -Type: Comment sont stockées ces données ? (Un triplet de float ? Deux double ? ..) - // -Offset: La position de la donnée dans le buffer (les données sont entrelacées) - // -UsageIndex: Pour les coordonnées de texture, définit l'indice de texture utilisé. - NzVertexElement elements[2]; - elements[0].usage = nzElementUsage_Position; // Notre premier élément sera la position des vertices - elements[0].offset = 0; // Celles-ci sont placées au début - elements[0].type = nzElementType_Float3; // Sont composées de trois flottants - - elements[1].usage = nzElementUsage_TexCoord; - elements[1].offset = 3*sizeof(float); - elements[1].type = nzElementType_Float2; - - if (!declaration->Create(elements, 2)) - { - // Nos éléments sont invalides ! - std::cout << "Failed to create vertex declaration" << std::endl; - return false; - } - - // Nous créons ensuite un buffer de 4 vertices (le second argument précise l'espace pris par chaque vertex), le stockage - // Et nous indiquons que nous n'y toucherons plus - NzVertexBuffer* buffer = new NzVertexBuffer(4, declaration->GetStride(nzElementStream_VertexData), nzBufferStorage_Hardware, nzBufferUsage_Static); - - // Doit respecter la declaration - float vertices[] = - { - // Vertex 1 - -1000.f, 0.f, -1000.f, // Position - 0.f, 0.f, // UV - - // Vertex 2 - -1000.f, 0.f, 1000.f, // Position - 0.f, 10.f, // UV - - // Vertex 3 - 1000.f, 0.f, -1000.f, // Position - 10.f, 0.f, // UV - - // Vertex 4 - 1000.f, 0.f, 1000.f, // Position - 10.f, 10.f // UV - }; - - // Afin de modifier un buffer, il nous faut soit le verrouiller (accès bas-niveau), soit le remplir (accès de plus haut niveau) - if (!buffer->Fill(vertices, 0, 4)) // Nous remplissons à partir de l'index 0, et nous envoyons 4 vertices - { - std::cout << "Failed to fill buffer" << std::endl; - return false; - } - - NzStaticMesh* subMesh = new NzStaticMesh(mesh); - if (!subMesh->Create(declaration, buffer)) - { - std::cout << "Failed to create subMesh" << std::endl; - return false; - } - - subMesh->SetPrimitiveType(nzPrimitiveType_TriangleStrip); - - // On ajoute le submesh au mesh - mesh->AddSubMesh(subMesh); - - // Nos ressources sont notifiées utilisées par le mesh et le submesh, nous pouvons les rendre éphèmères. - // Les ressources seront donc automatiquement libérées lorsque plus aucune classe n'en aura besoin - buffer->SetPersistent(false); - declaration->SetPersistent(false); - subMesh->SetPersistent(false); // Pour le submesh, c'est déjà à false à la base - - return true; -} - -void DrawModel(const Model& model) -{ - // La matrice world est celle qui représente les transformations du modèle - NzRenderer::SetMatrix(nzMatrixType_World, model.matrix); - - NzShader* shader = NzRenderer::GetShader();// On récupère le shader du rendu - shader->SendTexture(shader->GetUniformLocation("texture"), &model.texture); - - // Un mesh est divisé en plusieurs submeshes - unsigned int subMeshCount = model.mesh.GetSubMeshCount(); - for (unsigned int i = 0; i < subMeshCount; ++i) - { - // On récupère le submesh - const NzSubMesh* subMesh = model.mesh.GetSubMesh(i); - - // On paramètre le Renderer avec ses données - NzRenderer::SetIndexBuffer(subMesh->GetIndexBuffer()); - NzRenderer::SetVertexBuffer(subMesh->GetVertexBuffer()); - NzRenderer::SetVertexDeclaration(subMesh->GetVertexDeclaration()); - - // On fait le rendu - NzRenderer::DrawPrimitives(subMesh->GetPrimitiveType(), 0, subMesh->GetVertexCount()); - } -} - -void SetSequence(AnimatedModel& model, const NzString& sequenceName) -{ - // On récupère l'animation du mesh - const NzAnimation* animation = model.mesh.GetAnimation(); - - // Nous nous basons sur l'assertion que la séquence existe (Chose que nous pouvons tester avec HasSequence()) - model.currentSequence = animation->GetSequence(sequenceName); - - // Pour avoir une interpolation entre la séquence précédente et celle-ci, nous n'affectons que nextFrame - model.nextFrame = model.currentSequence->firstFrame; -} +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Une structure pour contenir nos informations (Cette structure est très simpliste) +struct Model +{ + NzMatrix4f matrix; // Les transformations subies par le modèle + NzMesh mesh; // Le mesh + NzTexture texture; // Sa texture +}; + +struct AnimatedModel : public Model +{ + // Quelques variables pour l'animation + const NzSequence* currentSequence = nullptr; // La séquence en cours + float interpolation = 0.f; // La valeur de l'interpolation ([0..1], si dépasse 1, on passe à la frame suivante) + unsigned int currentFrame = 0; // La première frame + unsigned int nextFrame; // La seconde frame, l'animation est interpollée entre ces deux-là +}; + +void AnimateModel(AnimatedModel& moedel, float elapsed); +bool CreateCheckerTexture(NzTexture* texture); +bool CreateFloorMesh(NzMesh* mesh); +void DrawModel(const Model& model); +void SetSequence(AnimatedModel& model, const NzString& name); + +int main() +{ + // Tout d'abord on affiche les instructions + std::cout << "Controls: ZQSD" << std::endl; + std::cout << "Escape to quit" << std::endl; + std::cout << "Left click to capture/free the mouse" << std::endl; + std::cout << "Right click to control Dr. Freak" << std::endl; + + // Cette ligne active le mode de compatibilité d'OpenGL lors de l'initialisation de Nazara (Nécessaire pour le shader) + NzContextParameters::defaultCompatibilityProfile = true; + + // Maintenant nous initialisons le Renderer (Qui initialisera le noyau ainsi que le module utilitaire) + // Cette étape est obligatoire pour beaucoup de fonctionnalités (Notamment le chargement de ressources et le rendu) + NzInitializer renderer; + if (!renderer) + { + // Ça n'a pas fonctionné, le pourquoi se trouve dans le fichier NazaraLog.log + std::cout << "Failed to initialize Nazara, see NazaraLog.log for further informations" << std::endl; + std::getchar(); // On laise le temps de voir l'erreur + return EXIT_FAILURE; + } + + // Maintenant nous pouvons utiliser le moteur comme bon nous semble, tout d'abord nous allons charger les ressources + + // Charger une ressource se fait actuellement manuellement, mais un ResourceManager est à venir + // Vous initialisez une ressource, et la chargez via sa méthode LoadFrom[File|Memory|Stream] + // Note: il est possible de donner des instructions au loader (qui va charger le fichier en ressource) via les ResourceParameters + NzMeshParams parameters; + + // Le loader doit-il automatiquement charger les animations ? + // Attention, ce paramètre possède une signification différente selon le type d'animation du mesh. + // -Pour les animations keyframe (image-clé), c'est la seule et unique façon de charger les animations, étant donné + // qu'elles sont fourniees avec le mesh. + // -Pour les animations squelettiques, le loader ne fera que charger automatiquement l'animation associée au mesh s'il le peut + // Dans les deux cas, les paramètres d'animations (parameters.animation) seront utilisés + parameters.loadAnimations = true; // Vaut true par défaut + + // Pour qu'un mesh puisse être rendu, il doit être stocké du côté de la carte graphique (Hardware), mais il est parfois utile de + // le stocker côté RAM, par exemple pour le moteur physique. En sachant qu'il est facile de changer le stockage d'un buffer. + parameters.storage = nzBufferStorage_Hardware; // Vaut nzBufferStorage_Hardware par défaut + + AnimatedModel drfreak; + if (!drfreak.mesh.LoadFromFile("resources/drfreak.md2", parameters)) // On charge notre bon vieux docteur avec les paramètres de chargement. + { + // Le chargement n'a pas fonctionné, le modèle est peut-être corrompu/non-supporté, ou alors n'existe pas. + std::cout << "Failed to load mesh" << std::endl; + std::getchar(); // On laise le temps de voir l'erreur + return EXIT_FAILURE; + } + + if (!drfreak.mesh.HasAnimation()) // Le mesh possède-t-il des animations ? + { + // Cette démo n'a aucun intérêt sans animations + std::cout << "Mesh has no animation" << std::endl; + std::getchar(); + return EXIT_FAILURE; + } + + SetSequence(drfreak, "stand"); + + // Il est possible que le mesh possède un ou plusieurs skin, nous utiliserons cette information pour charger une texture + if (drfreak.mesh.HasSkin()) + { + // Contrairement aux autres ressources, la texture n'est pas critique + if (drfreak.texture.LoadFromFile("resources/" + drfreak.mesh.GetSkin())) + drfreak.texture.SetFilterMode(nzTextureFilter_Bilinear); // Appliquons-lui un filtrage bilinéaire + else + std::cout << "Failed to load texture" << std::endl; + } + + if (!drfreak.texture.IsValid()) // Les méthodes Resource::IsValid indiquent si la ressource a été correctement créée + { + std::cout << "Creating checker texture for mesh" << std::endl; + + if (!CreateCheckerTexture(&drfreak.texture)) + { + std::cout << "Failed to create mesh texture" << std::endl; + std::getchar(); + return EXIT_FAILURE; + } + } + + // Nous créons maintenant notre sol + Model floor; + if (!CreateFloorMesh(&floor.mesh)) + { + std::cout << "Failed to create floor" << std::endl; + std::getchar(); + return EXIT_FAILURE; + } + + if (!CreateCheckerTexture(&floor.texture)) + { + std::cout << "Failed to create floor texture" << std::endl; + std::getchar(); + return EXIT_FAILURE; + } + + // Le sol ne subit aucune transformation + floor.matrix.MakeIdentity(); + + // Pour effectuer un rendu, il faut que la carte graphique sache comment le faire. + // Les shaders sont de petits programmes qui donnent des instructions à la carte graphique lors de son pipeline. + // Ils sont aujourd'hui indispensables pour un rendu 3D, mais sont très utiles pour divers effets ! + // Il existe plusieurs langages de shaders, GLSL pour OpenGL, HLSL pour Direct3D et Cg qui peut être utilisé pour les deux. + // Le Renderer de Nazara utilise OpenGL, par conséquent nous utiliserons le GLSL + // La méthode NzShader::IsLanguageSupported permet de savoir si un langage est supporté. + NzShader shader; + if (!shader.Create(nzShaderLanguage_GLSL)) + { + std::cout << "Failed to load shader" << std::endl; + std::getchar(); + return EXIT_FAILURE; + } + + // Une fois le shader créé, nous devons lui spécifier les codes sources de nos shaders + // Pour notre exemple nous prendrons un shader très simple + // Un shader doit obligatoirement posséder au moins deux codes, un pour le fragment shader et un pour le vertex shader + + // Le fragment shader traite la couleur de nos pixels + if (!shader.LoadFromFile(nzShaderType_Fragment, "shaders/basic.frag")) + { + std::cout << "Failed to load fragment shader from file" << std::endl; + // À la différence des autres ressources, le shader possède un log qui peut indiquer les erreurs en cas d'échec + std::cout << "Log: " << shader.GetLog() << std::endl; + std::getchar(); + return EXIT_FAILURE; + } + + // Le vertex shader (Transformation des vertices de l'espace 3D vers l'espace écran) + if (!shader.LoadFromFile(nzShaderType_Vertex, "shaders/basic.vert")) + { + std::cout << "Failed to load vertex shader from file" << std::endl; + std::cout << "Log: " << shader.GetLog() << std::endl; + std::getchar(); + return EXIT_FAILURE; + } + + // Une fois les codes sources de notre shader chargé, nous pouvons le compiler, afin de le rendre utilisable + if (!shader.Compile()) + { + std::cout << "Failed to compile shader" << std::endl; + std::cout << "Log: " << shader.GetLog() << std::endl; + std::getchar(); + return EXIT_FAILURE; + } + + // Nos ressources sont chargées, et c'est bien beau, mais il nous faudrait une fenêtre pour afficher tout ça + // Window représente une fenêtre singulière, pour y effectuer un rendu il nous faut une RenderWindow + // Tout d'abord, sa taille, disons celle du bureau divisé par deux + + // Un VideoMode est une structure contenant une longueur (width), une largeur (height) et le nombre de bits par pixels (bitsPerPixel) + NzVideoMode mode = NzVideoMode::GetDesktopMode(); // Nous récupérons le mode actuellement utilisé par le bureau + + // Nous divisons sa longueur et sa largeur par deux + mode.width /= 2; + mode.height /= 2; + + // Maintenant le titre, rien de plus simple... + NzString windowTitle = "Nazara Demo - AnimatedMesh"; + + // Nous pouvons créer notre fenêtre ! (Via le constructeur directement ou par la méthode Create) + NzRenderWindow window; + + // Le premier argument définit la taille de rendu de la fenêtre (Si elle possède une bordure elle sera légèrement plus grande). + // Le deuxième argument est le titre de la fenêtre lors de sa création, vous pouvez le modifier à tout moment via window.SetTitle. + // Le troisième argument représente la décoration de la fenêtre, sa bordure, ses boutons. + // Attention que cela permet à la fenêtre de changer sa taille et qu'il faudra donc traiter l'évènement. + // Par défaut le troisième argument vaut nzWindowStyle_Default (Bordure + Bouton de fermeture + Redimensionnement) + if (!window.Create(mode, windowTitle, nzWindowStyle_Default)) + { + std::cout << "Failed to create window" << std::endl; + std::getchar(); + return EXIT_FAILURE; + } + + // Notre belle fenêtre est créée, nous pouvons la configurer + + // On cache le curseur + window.SetCursor(nzWindowCursor_None); + + // Nous limitons les FPS à 100 + window.SetFramerateLimit(100); + + // La matrice de projection définit la transformation du vertice 3D à un point 2D + NzRenderer::SetMatrix(nzMatrixType_Projection, NzMatrix4f::Perspective(NzDegrees(70.f), static_cast(window.GetWidth())/window.GetHeight(), 1.f, 1000.f)); + + // Notre fenêtre est créée, cependant il faut s'occuper d'elle, pour le rendu et les évènements + NzClock secondClock, updateClock; // Des horloges pour gérer le temps + unsigned int fps = 0; // Compteur de FPS + + // Quelques variables pour notre improvisation de physique + float groundPos = drfreak.mesh.GetAABB().GetMinimum().y; // Les coordonnées locales du "bas" du modèle + NzVector3f modelPos(0.f, -groundPos, -50.f); + NzVector3f modelVel(0.f, 0.f, 0.f); + NzQuaternionf modelOrient(NzQuaternionf::Identity()); + NzEulerAnglesf modelRot(0.f, 0.f, 0.f); + float modelSpeed = 150.f; + + // Nous initialisons la matrice + drfreak.matrix = NzMatrix4f::Rotate(modelOrient) * NzMatrix4f::Translate(modelPos); + + // Notre caméra + NzVector3f camPos(0.f, 25.f, -20.f); + NzQuaternionf camOrient(NzQuaternionf::Identity()); + NzEulerAnglesf camRot(0.f, 0.f, 0.f); // Les angles d'eulers sont bien plus facile à utiliser + NzMatrix4f camMatrix = NzMatrix4f::Translate(camPos); + float camSpeed = 100.f; + float sensitivity = 0.8f; + + // Quelques variables + bool camMode = true; + bool thirdPerson = false; + bool windowOpen = true; + + // On peut commencer la boucle du programme + while (windowOpen) + { + // Ici nous gérons les évènements + NzEvent event; + while (window.PollEvent(&event)) // Avons-nous un évènement dans la file ? + { + // Nous avons un évènement ! + + switch (event.type) // De quel type est cet évènement ? + { + case nzEventType_Quit: // L'utilisateur/L'OS nous a demandé de terminer notre exécution + windowOpen = false; // Nous terminons alors la boucle + break; + + case nzEventType_MouseMoved: // La souris a bougé + { + // Si nous ne sommes pas en mode free-fly, on ne traite pas l'évènement + if (!camMode || thirdPerson) + break; + + // On modifie l'angle de la caméra grâce au déplacement relatif de la souris + camRot.yaw = NzNormalizeAngle(camRot.yaw - event.mouseMove.deltaX*sensitivity); + + // Pour éviter les loopings mais surtout les problèmes de calculation de la matrice de vue, on restreint les angles + camRot.pitch = NzClamp(camRot.pitch - event.mouseMove.deltaY*sensitivity, -89.f, 89.f); + + // La matrice vue représente les transformations effectuées par la caméra + // On recalcule la matrice de la caméra et on l'envoie au renderer + camOrient = camRot; // Conversion des angles d'euler en quaternion + NzRenderer::SetMatrix(nzMatrixType_View, NzMatrix4f::LookAt(camPos, camPos + camOrient * NzVector3f::Forward())); + + // Pour éviter que le curseur ne sorte de l'écran, nous le renvoyons au centre de la fenêtre + NzMouse::SetPosition(window.GetWidth()/2, window.GetHeight()/2, window); + break; + } + + case nzEventType_MouseButtonPressed: // L'utilisateur (ou son chat) vient de cliquer sur la souris + if (event.mouseButton.button == NzMouse::Left) // Est-ce le clic gauche ? + { + // L'utilisateur vient d'appuyer sur le bouton left de la souris + // Nous allons inverser le mode caméra et montrer/cacher le curseur en conséquence + if (camMode) + { + camMode = false; + window.SetCursor(nzWindowCursor_Default); + } + else + { + camMode = true; + window.SetCursor(nzWindowCursor_None); + } + } + else if (event.mouseButton.button == NzMouse::Right) // Est-ce le clic droit ? + { + if (thirdPerson) + { + // On arrête le mouvement + SetSequence(drfreak, "stand"); + + // Afin de synchroniser le quaternion avec les angles d'euler + camRot = camOrient.ToEulerAngles(); + thirdPerson = false; + } + else + thirdPerson = true; + } + break; + + case nzEventType_Resized: // L'utilisateur a changé la taille de la fenêtre, le coquin ! + NzRenderer::SetViewport(NzRectui(0, 0, event.size.width, event.size.height)); // Adaptons l'affichage + + // Il nous faut aussi mettre à jour notre matrice de projection + NzRenderer::SetMatrix(nzMatrixType_Projection, NzMatrix4f::Perspective(NzDegrees(70.f), static_cast(event.size.width)/event.size.height, 1.f, 1000.f)); + break; + + case nzEventType_KeyPressed: // Une touche du clavier vient d'être enfoncée + if (thirdPerson && + (event.key.code == NzKeyboard::Z || // Est-ce la touche Z ? + event.key.code == NzKeyboard::S || // Ou bien la touche S ? + event.key.code == NzKeyboard::Q || // Ou encore la touche Q ? + event.key.code == NzKeyboard::D)) // Et pourquoi pas la touche D ? + { + // Si une touche concernant le déplacement est appuyée + SetSequence(drfreak, "run"); // On anime le personnage pour qu'il ait une animation de déplacement + } + else if (event.key.code == NzKeyboard::Escape) + windowOpen = false; + + break; + + case nzEventType_KeyReleased: // Une touche du clavier vient d'être relachée + if (thirdPerson && + !NzKeyboard::IsKeyPressed(NzKeyboard::Z) && // Est-ce que la touche Z est enfoncée en ce moment ? + !NzKeyboard::IsKeyPressed(NzKeyboard::S) && // Ou bien la touche S ? + !NzKeyboard::IsKeyPressed(NzKeyboard::Q) && // Etc.. + !NzKeyboard::IsKeyPressed(NzKeyboard::D)) // Etc.. + { + // Si plus aucune touche de déplacement n'est enfoncée + SetSequence(drfreak, "stand"); + } + + break; + + default: // Les autres évènements, on s'en fiche + break; + } + } + + // Mise à jour de la partie logique + if (updateClock.GetMilliseconds() >= 1000/60) // 60 fois par seconde + { + float elapsedTime = updateClock.GetSeconds(); // Le temps depuis la dernière mise à jour + + // Déplacement de la caméra + static const NzVector3f forward(NzVector3f::Forward()); + static const NzVector3f left(NzVector3f::Left()); + static const NzVector3f up(NzVector3f::Up()); + + // Notre rotation sous forme de quaternion nous permet de tourner un vecteur + // Par exemple ici, quaternion * forward nous permet de récupérer le vecteur de la direction "avant" + + if (thirdPerson) + { + // Nous déplaçons le personnage en fonction des touches pressées + + if (NzKeyboard::IsKeyPressed(NzKeyboard::Z)) + modelPos += modelOrient * forward * modelSpeed * elapsedTime; + + if (NzKeyboard::IsKeyPressed(NzKeyboard::S)) + modelPos -= modelOrient * forward * modelSpeed * elapsedTime; + + if (NzKeyboard::IsKeyPressed(NzKeyboard::Q)) + modelRot.yaw += camSpeed * elapsedTime; + + if (NzKeyboard::IsKeyPressed(NzKeyboard::D)) + modelRot.yaw -= camSpeed * elapsedTime; + + modelOrient = modelRot; + } + else + { + // Sinon, c'est la caméra qui se déplace (en fonction des mêmes touches) + + // Un boost en maintenant le shift gauche + float speed = (NzKeyboard::IsKeyPressed(NzKeyboard::Key::LShift)) ? camSpeed*5 : camSpeed; + + if (NzKeyboard::IsKeyPressed(NzKeyboard::Z)) + camPos += camOrient * forward * speed * elapsedTime; + + if (NzKeyboard::IsKeyPressed(NzKeyboard::S)) + camPos -= camOrient * forward * speed * elapsedTime; + + if (NzKeyboard::IsKeyPressed(NzKeyboard::Q)) + camPos += camOrient * left * speed * elapsedTime; + + if (NzKeyboard::IsKeyPressed(NzKeyboard::D)) + camPos -= camOrient * left * speed * elapsedTime; + + // En revanche, ici la hauteur est toujours la même, peu importe notre orientation + if (NzKeyboard::IsKeyPressed(NzKeyboard::Space)) + camPos += up * speed * elapsedTime; + + if (NzKeyboard::IsKeyPressed(NzKeyboard::LControl)) + camPos -= up * speed * elapsedTime; + } + + // Oui les quaternions et les matrices sont calculés même si la caméra ne bouge pas + // C'est une limitation de mon implémentation, qui ne sera pas présente une fois les NzSceneNode intégrés + if (thirdPerson) + { + static NzQuaternionf rotDown(NzEulerAnglesf(-15.f, 0.f, 0.f)); // Une rotation pour regarder vers le bas + camOrient = modelOrient * rotDown; + + camPos = modelPos + camOrient * NzVector3f(0.f, 25.f, 60.f); + } + + NzRenderer::SetMatrix(nzMatrixType_View, NzMatrix4f::LookAt(camPos, camPos + camOrient * NzVector3f::Forward())); + + // Mise à jour de la matrice du personnage + drfreak.matrix = NzMatrix4f::Rotate(modelOrient) * NzMatrix4f::Translate(modelPos); + + // Animation + AnimateModel(drfreak, elapsedTime); + updateClock.Restart(); + } + + // On active le shader et paramètrons le rendu + NzRenderer::SetShader(&shader); + + // Notre scène 3D requiert un test de profondeur + NzRenderer::Enable(nzRendererParameter_DepthTest, true); + + // Nous voulons avoir un fond bien gris + NzRenderer::SetClearColor(128, 128, 128); + + // Et nous effaçons les buffers de couleur et de profondeur + NzRenderer::Clear(nzRendererClear_Color | nzRendererClear_Depth); + + // Affichage des meshs + DrawModel(floor); + + // Notre Dr. Freak possède des normales, nous pouvons alors éliminer les faces qu'on ne voit pas + NzRenderer::Enable(nzRendererParameter_FaceCulling, true); + + DrawModel(drfreak); + + NzRenderer::Enable(nzRendererParameter_FaceCulling, false); + + // Nous mettons à jour l'écran + window.Display(); + + fps++; + + // Toutes les secondes + if (secondClock.GetMilliseconds() >= 1000) + { + window.SetTitle(windowTitle + " (FPS: " + NzString::Number(fps) + ')'); + fps = 0; + secondClock.Restart(); + } + } + + return EXIT_SUCCESS; +} + +void AnimateModel(AnimatedModel& model, float elapsed) +{ + model.interpolation += model.currentSequence->framePerSecond * elapsed; + while (model.interpolation > 1.f) + { + model.interpolation -= 1.f; + + model.currentFrame = model.nextFrame; + if (++model.nextFrame > model.currentSequence->lastFrame) + model.nextFrame = model.currentSequence->firstFrame; + } + + model.mesh.Animate(model.currentFrame, model.nextFrame, (NzKeyboard::IsKeyPressed(NzKeyboard::A)) ? 0.f : model.interpolation); +} + +bool CreateCheckerTexture(NzTexture* texture) +{ + NzImage image; + // Nous crééons une image 2D, au format RGBA8 de dimensions 128*128 (8 blocs de 16 pixels de côté) + if (!image.Create(nzImageType_2D, nzPixelFormat_RGBA8, 8*16, 8*16)) + { + // Ne devrait pas arriver (La création d'une image ne peut échouer que si l'un des argument est incorrect) + std::cout << "Failed to create image, this means a bug has been found in Nazara" << std::endl; + return false; + } + + // Pour modifier les pixels, nous pouvons accéder directement à ces derniers avec GetPixels(), ou bien à un pixel + // via [Get|Set]PixelColor, mais pour cette occasion nous utiliserons une méthode bien pratique, Fill. + unsigned int blockCountX = image.GetWidth()/16; + unsigned int blockCountY = image.GetHeight()/16; + for (unsigned int x = 0; x < blockCountX; ++x) + { + for (unsigned int y = 0; y < blockCountY; ++y) + { + // Une belle texture de damier + NzColor color = (x%2 == y%2) ? NzColor::White : NzColor::Black; + // Fill remplit une zone de la texture avec une couleur + image.Fill(color, NzRectui(x*16, y*16, 16, 16)); + } + } + + if (!texture->LoadFromImage(image)) // Nous créons notre texture depuis notre image + { + // Nous n'avons vraiment pas beaucoup de chance.. + std::cout << "Failed to load image" << std::endl; + return false; + } + + texture->SetAnisotropyLevel(NzRenderer::GetMaxAnisotropyLevel()); // Un filtrage anisotropique pour la texture + texture->SetWrapMode(nzTextureWrap_Repeat); // Si les coordonnées de texture dépassent 1.f, la texture sera répétée + + return true; +} + +bool CreateFloorMesh(NzMesh* mesh) +{ + // Cette fonction créé un mesh statique simpliste pour servir de sol + + // Nous créons un mesh statique + if (!mesh->Create(nzAnimationType_Static)) + { + // L'échec est techniquement impossible mais le moteur étant en constante évolution ... + std::cout << "Failed to create mesh" << std::endl; + return false; + } + + // Les vertex declaration ont pour seul but de décrire l'agencement d'un vertex buffer + // Elles sont composées de VertexElement, chacun décrivant un élément du buffer + NzVertexDeclaration* declaration = new NzVertexDeclaration; + + // Il y a cinq paramètres différents (stream, usage, type, offset, usageIndex) + // -Stream: À quoi serviront les données ? À définir des sommets (nzElementStream_VertexData) ou à l'instancing (nzElementStream_InstancedData) + // -Usage: Comment cette donnée doit-elle être envoyée au shader + // -Type: Comment sont stockées ces données ? (Un triplet de float ? Deux double ? ..) + // -Offset: La position de la donnée dans le buffer (les données sont entrelacées) + // -UsageIndex: Pour les coordonnées de texture, définit l'indice de texture utilisé. + NzVertexElement elements[2]; + elements[0].usage = nzElementUsage_Position; // Notre premier élément sera la position des vertices + elements[0].offset = 0; // Celles-ci sont placées au début + elements[0].type = nzElementType_Float3; // Sont composées de trois flottants + + elements[1].usage = nzElementUsage_TexCoord; + elements[1].offset = 3*sizeof(float); + elements[1].type = nzElementType_Float2; + + if (!declaration->Create(elements, 2)) + { + // Nos éléments sont invalides ! + std::cout << "Failed to create vertex declaration" << std::endl; + return false; + } + + // Nous créons ensuite un buffer de 4 vertices (le second argument précise l'espace pris par chaque vertex), le stockage + // Et nous indiquons que nous n'y toucherons plus + NzVertexBuffer* buffer = new NzVertexBuffer(4, declaration->GetStride(nzElementStream_VertexData), nzBufferStorage_Hardware, nzBufferUsage_Static); + + // Doit respecter la declaration + float vertices[] = + { + // Vertex 1 + -1000.f, 0.f, -1000.f, // Position + 0.f, 0.f, // UV + + // Vertex 2 + -1000.f, 0.f, 1000.f, // Position + 0.f, 10.f, // UV + + // Vertex 3 + 1000.f, 0.f, -1000.f, // Position + 10.f, 0.f, // UV + + // Vertex 4 + 1000.f, 0.f, 1000.f, // Position + 10.f, 10.f // UV + }; + + // Afin de modifier un buffer, il nous faut soit le verrouiller (accès bas-niveau), soit le remplir (accès de plus haut niveau) + if (!buffer->Fill(vertices, 0, 4)) // Nous remplissons à partir de l'index 0, et nous envoyons 4 vertices + { + std::cout << "Failed to fill buffer" << std::endl; + return false; + } + + NzStaticMesh* subMesh = new NzStaticMesh(mesh); + if (!subMesh->Create(declaration, buffer)) + { + std::cout << "Failed to create subMesh" << std::endl; + return false; + } + + subMesh->SetPrimitiveType(nzPrimitiveType_TriangleStrip); + + // On ajoute le submesh au mesh + mesh->AddSubMesh(subMesh); + + // Nos ressources sont notifiées utilisées par le mesh et le submesh, nous pouvons les rendre éphèmères. + // Les ressources seront donc automatiquement libérées lorsqu'elles ne seront plus référencées par une classe + buffer->SetPersistent(false); + declaration->SetPersistent(false); + subMesh->SetPersistent(false); // Pour le submesh, c'est déjà le comportement par défaut + + return true; +} + +void DrawModel(const Model& model) +{ + // La matrice world est celle qui représente les transformations du modèle + NzRenderer::SetMatrix(nzMatrixType_World, model.matrix); + + NzShader* shader = NzRenderer::GetShader();// On récupère le shader du rendu + shader->SendTexture(shader->GetUniformLocation("texture"), &model.texture); + + // Un mesh est divisé en plusieurs submeshes + unsigned int subMeshCount = model.mesh.GetSubMeshCount(); + for (unsigned int i = 0; i < subMeshCount; ++i) + { + // On récupère le submesh + const NzSubMesh* subMesh = model.mesh.GetSubMesh(i); + + // On paramètre le Renderer avec ses données + NzRenderer::SetIndexBuffer(subMesh->GetIndexBuffer()); + NzRenderer::SetVertexBuffer(subMesh->GetVertexBuffer()); + NzRenderer::SetVertexDeclaration(subMesh->GetVertexDeclaration()); + + // On fait le rendu + NzRenderer::DrawPrimitives(subMesh->GetPrimitiveType(), 0, subMesh->GetVertexCount()); + } +} + +void SetSequence(AnimatedModel& model, const NzString& sequenceName) +{ + // On récupère l'animation du mesh + const NzAnimation* animation = model.mesh.GetAnimation(); + + // Nous nous basons sur l'assertion que la séquence existe (Chose que nous pouvons tester avec HasSequence()) + const NzSequence* sequence = animation->GetSequence(sequenceName); + if (model.currentSequence != sequence) + { + model.currentSequence = sequence; + + // Pour avoir une interpolation entre la séquence précédente et celle-ci, nous n'affectons que nextFrame + model.nextFrame = model.currentSequence->firstFrame; + } +} diff --git a/include/Nazara/Core.hpp b/include/Nazara/Core.hpp index 17a1faa6b..a084f1cd2 100644 --- a/include/Nazara/Core.hpp +++ b/include/Nazara/Core.hpp @@ -1,61 +1,62 @@ -// This file was automatically generated by Nazara - -/* - Nazara Engine - Core module - - Copyright (C) 2012 Jérôme "Lynix" Leclercq (Lynix680@gmail.com) - - Permission is hereby granted, free of charge, to any person obtaining a copy of - this software and associated documentation files (the "Software"), to deal in - the Software without restriction, including without limitation the rights to - use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - of the Software, and to permit persons to whom the Software is furnished to do - so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +// This file was automatically generated by Nazara + +/* + Nazara Engine - Core module + + Copyright (C) 2012 Jérôme "Lynix" Leclercq (Lynix680@gmail.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do + so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/include/Nazara/Core/Config.hpp b/include/Nazara/Core/Config.hpp index ddb8efb5b..021568c7f 100644 --- a/include/Nazara/Core/Config.hpp +++ b/include/Nazara/Core/Config.hpp @@ -1,84 +1,85 @@ -/* - Nazara Engine - Core module - - Copyright (C) 2012 Jérôme "Lynix" Leclercq (Lynix680@gmail.com) - - Permission is hereby granted, free of charge, to any person obtaining a copy of - this software and associated documentation files (the "Software"), to deal in - the Software without restriction, including without limitation the rights to - use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - of the Software, and to permit persons to whom the Software is furnished to do - so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ - -#pragma once - -#ifndef NAZARA_CONFIG_CORE_HPP -#define NAZARA_CONFIG_CORE_HPP - -/// Chaque modification d'un paramètre du module nécessite une recompilation de celui-ci - -// Appelle exit dès qu'une assertion est invalide -#define NAZARA_CORE_EXIT_ON_ASSERT_FAILURE 1 - -// Teste les assertions -#define NAZARA_CORE_ENABLE_ASSERTS 0 - -// Taille du buffer lors d'une lecture complète d'un fichier (ex: Hash) -#define NAZARA_CORE_FILE_BUFFERSIZE 4096 - -// Le moteur doit-il incorporer les Unicode Character Data (Nécessaires pour faire fonctionner le flag NzString::HandleUTF8) -#define NAZARA_CORE_INCLUDE_UNICODEDATA 0 - -// Utilise un tracker pour repérer les éventuels leaks (Ralentit l'exécution) -#define NAZARA_CORE_MEMORYLEAKTRACKER 0 - -// Standardise les séparateurs des dossiers selon le système d'exploitation courant (Léger coût à l'exécution) -#define NAZARA_CORE_NORMALIZE_DIRECTORY_SEPARATORS 1 - -// Précision des réels lors de la transformation en texte (Max. chiffres après la virgule) -#define NAZARA_CORE_REAL_PRECISION 6 - -// Duplique la sortie du log sur le flux de sortie standard (cout) -#define NAZARA_CORE_DUPLICATE_TO_COUT 0 - -// Active les tests de sécurité basés sur le code (Conseillé pour le développement) -#define NAZARA_CORE_SAFE 1 - -// Protège les classes des accès concurrentiels -#define NAZARA_CORE_THREADSAFE 1 - -// Les classes à protéger des accès concurrentiels -#define NAZARA_THREADSAFETY_BYTEARRAY 1 // NzByteArray (COW) -#define NAZARA_THREADSAFETY_CLOCK 0 // NzClock -#define NAZARA_THREADSAFETY_DIRECTORY 1 // NzDirectory -#define NAZARA_THREADSAFETY_DYNLIB 1 // NzDynLib -#define NAZARA_THREADSAFETY_FILE 1 // NzFile -#define NAZARA_THREADSAFETY_HASHDIGEST 0 // NzHashDigest -#define NAZARA_THREADSAFETY_LOG 1 // NzLog -#define NAZARA_THREADSAFETY_STRING 1 // NzString (COW) -#define NAZARA_THREADSAFETY_STRINGSTREAM 0 // NzStringStream - -// Le nombre de spinlocks à utiliser avec les critical sections de Windows (0 pour désactiver) -#define NAZARA_CORE_WINDOWS_CS_SPINLOCKS 4096 - -// Optimise certaines parties du code avec certaines avancées venues de Windows Vista (Casse la compatibilité XP mais n'affecte pas les autres OS) -#define NAZARA_CORE_WINDOWS_VISTA 0 - -/* -// Règle le temps entre le réveil du thread des timers et l'activation d'un timer (En millisecondes) -#define NAZARA_CORE_TIMER_WAKEUPTIME 10 -*/ - -#endif // NAZARA_CONFIG_CORE_HPP +/* + Nazara Engine - Core module + + Copyright (C) 2012 Jérôme "Lynix" Leclercq (Lynix680@gmail.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do + so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#pragma once + +#ifndef NAZARA_CONFIG_CORE_HPP +#define NAZARA_CONFIG_CORE_HPP + +/// Chaque modification d'un paramètre du module nécessite une recompilation de celui-ci + +// Appelle exit dès qu'une assertion est invalide +#define NAZARA_CORE_EXIT_ON_ASSERT_FAILURE 1 + +// Teste les assertions +#define NAZARA_CORE_ENABLE_ASSERTS 0 + +// Taille du buffer lors d'une lecture complète d'un fichier (ex: Hash) +#define NAZARA_CORE_FILE_BUFFERSIZE 4096 + +// Le moteur doit-il incorporer les Unicode Character Data (Nécessaires pour faire fonctionner le flag NzString::HandleUTF8) +#define NAZARA_CORE_INCLUDE_UNICODEDATA 0 + +// Utilise un tracker pour repérer les éventuels leaks (Ralentit l'exécution) +#define NAZARA_CORE_MEMORYLEAKTRACKER 0 + +// Standardise les séparateurs des dossiers selon le système d'exploitation courant (Léger coût à l'exécution) +#define NAZARA_CORE_NORMALIZE_DIRECTORY_SEPARATORS 1 + +// Précision des réels lors de la transformation en texte (Max. chiffres après la virgule) +#define NAZARA_CORE_REAL_PRECISION 6 + +// Duplique la sortie du log sur le flux de sortie standard (cout) +#define NAZARA_CORE_DUPLICATE_TO_COUT 0 + +// Active les tests de sécurité basés sur le code (Conseillé pour le développement) +#define NAZARA_CORE_SAFE 1 + +// Protège les classes des accès concurrentiels +#define NAZARA_CORE_THREADSAFE 1 + +// Les classes à protéger des accès concurrentiels +#define NAZARA_THREADSAFETY_BYTEARRAY 1 // NzByteArray (COW) +#define NAZARA_THREADSAFETY_CLOCK 0 // NzClock +#define NAZARA_THREADSAFETY_DIRECTORY 1 // NzDirectory +#define NAZARA_THREADSAFETY_DYNLIB 1 // NzDynLib +#define NAZARA_THREADSAFETY_FILE 1 // NzFile +#define NAZARA_THREADSAFETY_HASHDIGEST 0 // NzHashDigest +#define NAZARA_THREADSAFETY_LOG 1 // NzLog +#define NAZARA_THREADSAFETY_RESOURCE 1 // NzResource +#define NAZARA_THREADSAFETY_STRING 1 // NzString (COW) +#define NAZARA_THREADSAFETY_STRINGSTREAM 0 // NzStringStream + +// Le nombre de spinlocks à utiliser avec les critical sections de Windows (0 pour désactiver) +#define NAZARA_CORE_WINDOWS_CS_SPINLOCKS 4096 + +// Optimise certaines parties du code avec certaines avancées venues de Windows Vista (Casse la compatibilité XP mais n'affecte pas les autres OS) +#define NAZARA_CORE_WINDOWS_VISTA 0 + +/* +// Règle le temps entre le réveil du thread des timers et l'activation d'un timer (En millisecondes) +#define NAZARA_CORE_TIMER_WAKEUPTIME 10 +*/ + +#endif // NAZARA_CONFIG_CORE_HPP diff --git a/include/Nazara/Core/Enums.hpp b/include/Nazara/Core/Enums.hpp index 38f06d8de..21ac61c6e 100644 --- a/include/Nazara/Core/Enums.hpp +++ b/include/Nazara/Core/Enums.hpp @@ -1,26 +1,30 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Core module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef NAZARA_ENUMS_CORE_HPP -#define NAZARA_ENUMS_CORE_HPP - -enum nzEndianness -{ - nzEndianness_Unknown = -1, - - nzEndianness_BigEndian, - nzEndianness_LittleEndian -}; - -enum nzErrorType -{ - nzErrorType_AssertFailed, - nzErrorType_Internal, - nzErrorType_Normal, - nzErrorType_Warning -}; - -#endif // NAZARA_ENUMS_CORE_HPP +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_ENUMS_CORE_HPP +#define NAZARA_ENUMS_CORE_HPP + +enum nzEndianness +{ + nzEndianness_Unknown = -1, + + nzEndianness_BigEndian, + nzEndianness_LittleEndian, + + nzEndianness_Max = nzEndianness_LittleEndian +}; + +enum nzErrorType +{ + nzErrorType_AssertFailed, + nzErrorType_Internal, + nzErrorType_Normal, + nzErrorType_Warning, + + nzErrorType_Max = nzErrorType_Warning +}; + +#endif // NAZARA_ENUMS_CORE_HPP diff --git a/include/Nazara/Core/Resource.hpp b/include/Nazara/Core/Resource.hpp index 920c4aceb..57ca3d2f5 100644 --- a/include/Nazara/Core/Resource.hpp +++ b/include/Nazara/Core/Resource.hpp @@ -1,30 +1,72 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Core module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef NAZARA_RESOURCE_HPP -#define NAZARA_RESOURCE_HPP - -#include - -class NAZARA_API NzResource -{ - public: - NzResource(bool persistent = true); - NzResource(const NzResource& resource); - virtual ~NzResource(); - - void AddResourceReference() const; - bool IsPersistent() const; - void RemoveResourceReference() const; - void SetPersistent(bool persistent = true); - - private: - // Je fais précéder le nom par 'resource' pour éviter les éventuels conflits de noms - mutable bool m_resourcePersistent; - mutable unsigned int m_resourceReferenceCount; -}; - -#endif // NAZARA_RESOURCE_HPP +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_RESOURCE_HPP +#define NAZARA_RESOURCE_HPP + +#include +#include + +#if NAZARA_CORE_THREADSAFE && NAZARA_THREADSAFETY_RESOURCE +#include +#else +#include +#endif + +class NzResourceListener; + +struct NzResourceEntry +{ + NzResourceEntry(NzResourceListener* resourceListener, int i = 0) : + listener(resourceListener), + index(i) + { + } + + bool operator<(const NzResourceEntry& rhs) const + { + return listener < rhs.listener; + } + + NzResourceListener* listener; + int index; +}; + +class NAZARA_API NzResource +{ + public: + NzResource(bool persistent = true); + NzResource(const NzResource& resource); + virtual ~NzResource(); + + void AddResourceListener(NzResourceListener* listener, int index = 0) const; + void AddResourceReference() const; + + bool IsPersistent() const; + + void RemoveResourceListener(NzResourceListener* listener) const; + void RemoveResourceReference() const; + + void SetPersistent(bool persistent = true); + + protected: + void NotifyCreated(); + void NotifyDestroy(); + + private: + void EnsureResourceListenerUpdate() const; + + NazaraMutexAttrib(m_mutex, mutable) + + // Je fais précéder le nom par 'resource' pour éviter les éventuels conflits de noms + mutable std::set m_resourceListeners; + mutable std::set m_resourceListenersCache; + mutable bool m_resourceListenerUpdated; + bool m_resourcePersistent; + mutable unsigned int m_resourceReferenceCount; +}; + +#endif // NAZARA_RESOURCE_HPP diff --git a/include/Nazara/Core/ResourceListener.hpp b/include/Nazara/Core/ResourceListener.hpp new file mode 100644 index 000000000..cff90fe58 --- /dev/null +++ b/include/Nazara/Core/ResourceListener.hpp @@ -0,0 +1,25 @@ +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_RESOURCELISTENER_HPP +#define NAZARA_RESOURCELISTENER_HPP + +#include + +class NzResource; + +class NAZARA_API NzResourceListener +{ + public: + NzResourceListener() = default; + virtual ~NzResourceListener(); + + virtual void OnResourceCreated(const NzResource* resource, int index); + virtual void OnResourceDestroy(const NzResource* resource, int index); + virtual void OnResourceReleased(const NzResource* resource, int index); +}; + +#endif // NAZARA_RESOURCELISTENER_HPP diff --git a/include/Nazara/Core/ResourceLoader.inl b/include/Nazara/Core/ResourceLoader.inl index 321279ed6..392c04c0e 100644 --- a/include/Nazara/Core/ResourceLoader.inl +++ b/include/Nazara/Core/ResourceLoader.inl @@ -1,142 +1,142 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Utility module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include - -template -bool NzResourceLoader::LoadFromFile(Type* resource, const NzString& filePath, const Parameters& parameters) -{ - #if NAZARA_CORE_SAFE - if (!parameters.IsValid()) - { - NazaraError("Invalid Parameters"); - return false; - } - #endif - - NzString path = NzFile::NormalizePath(filePath); - NzString ext = path.SubstrFrom('.', -1, true); - if (ext.IsEmpty()) - { - NazaraError("Failed to get file extension"); - return false; - } - - NzFile file(path, NzFile::ReadOnly); - if (!file.IsOpen()) - { - NazaraError("Failed to open file"); - return false; - } - - for (auto loader = Type::s_loaders.begin(); loader != Type::s_loaders.end(); ++loader) - { - for (const NzString& loaderExt : std::get<0>(*loader)) - { - int cmp = NzString::Compare(loaderExt, ext); - if (cmp == 0) - { - if (!std::get<1>(*loader)(file, parameters)) - continue; - - file.SetCursorPos(0); - - // Chargement de la ressource - if (std::get<2>(*loader)(resource, file, parameters)) - return true; - - NazaraWarning("Loader failed"); - - file.SetCursorPos(0); - } - else if (cmp < 0) // S'il est encore possible que l'extension se situe après - continue; - - break; - } - } - - NazaraError("Failed to load file: no loader"); - - return false; -} - -template -bool NzResourceLoader::LoadFromMemory(Type* resource, const void* data, unsigned int size, const Parameters& parameters) -{ - NzMemoryStream stream(data, size); - - return LoadFromStream(resource, stream, parameters); -} - -template -bool NzResourceLoader::LoadFromStream(Type* resource, NzInputStream& stream, const Parameters& parameters) -{ - #if NAZARA_CORE_SAFE - if (!parameters.IsValid()) - { - NazaraError("Invalid Parameters"); - return false; - } - - if (stream.GetSize() == 0 || stream.GetCursorPos() >= stream.GetSize()) - { - NazaraError("No data to load"); - return false; - } - #endif - - nzUInt64 streamPos = stream.GetCursorPos(); - for (auto loader = Type::s_loaders.begin(); loader != Type::s_loaders.end(); ++loader) - { - // Le loader supporte-t-il les données ? - if (!std::get<1>(*loader)(stream, parameters)) - continue; - - // On repositionne le stream au début - stream.SetCursorPos(streamPos); - - // Chargement de la ressource - if (std::get<2>(*loader)(resource, stream, parameters)) - return true; - - NazaraWarning("Loader failed"); - stream.SetCursorPos(streamPos); // On repositionne au début - } - - NazaraError("Failed to load file: no loader"); - return false; -} - -template -void NzResourceLoader::RegisterLoader(const NzString& fileExtensions, CheckFunction checkFunc, LoadFunction loadFunc) -{ - /// Trouver une alternative à ce code monstrueux - std::vector exts; - fileExtensions.SplitAny(exts, " /\\.,;|-_"); - - std::set extensions; - std::copy(exts.begin(), exts.end(), std::inserter(extensions, extensions.begin())); - - Type::s_loaders.insert(std::make_tuple(std::move(extensions), checkFunc, loadFunc)); -} - -template -void NzResourceLoader::UnregisterLoader(const NzString& fileExtensions, CheckFunction checkFunc, LoadFunction loadFunc) -{ - std::vector exts; - fileExtensions.SplitAny(exts, " /\\.,;|-_"); - - std::set extensions; - std::copy(exts.begin(), exts.end(), std::inserter(extensions, extensions.begin())); - - Type::s_loaders.erase(std::make_tuple(std::move(extensions), checkFunc, loadFunc)); -} - -#include +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include + +template +bool NzResourceLoader::LoadFromFile(Type* resource, const NzString& filePath, const Parameters& parameters) +{ + #if NAZARA_CORE_SAFE + if (!parameters.IsValid()) + { + NazaraError("Invalid parameters"); + return false; + } + #endif + + NzString path = NzFile::NormalizePath(filePath); + NzString ext = path.SubstrFrom('.', -1, true); + if (ext.IsEmpty()) + { + NazaraError("Failed to get file extension"); + return false; + } + + NzFile file(path, NzFile::ReadOnly); + if (!file.IsOpen()) + { + NazaraError("Failed to open file"); + return false; + } + + for (auto loader = Type::s_loaders.begin(); loader != Type::s_loaders.end(); ++loader) + { + for (const NzString& loaderExt : std::get<0>(*loader)) + { + int cmp = NzString::Compare(loaderExt, ext); + if (cmp == 0) + { + if (!std::get<1>(*loader)(file, parameters)) + continue; + + file.SetCursorPos(0); + + // Chargement de la ressource + if (std::get<2>(*loader)(resource, file, parameters)) + return true; + + NazaraWarning("Loader failed"); + + file.SetCursorPos(0); + } + else if (cmp < 0) // S'il est encore possible que l'extension se situe après + continue; + + break; + } + } + + NazaraError("Failed to load file: no loader"); + + return false; +} + +template +bool NzResourceLoader::LoadFromMemory(Type* resource, const void* data, unsigned int size, const Parameters& parameters) +{ + NzMemoryStream stream(data, size); + + return LoadFromStream(resource, stream, parameters); +} + +template +bool NzResourceLoader::LoadFromStream(Type* resource, NzInputStream& stream, const Parameters& parameters) +{ + #if NAZARA_CORE_SAFE + if (!parameters.IsValid()) + { + NazaraError("Invalid parameters"); + return false; + } + + if (stream.GetSize() == 0 || stream.GetCursorPos() >= stream.GetSize()) + { + NazaraError("No data to load"); + return false; + } + #endif + + nzUInt64 streamPos = stream.GetCursorPos(); + for (auto loader = Type::s_loaders.begin(); loader != Type::s_loaders.end(); ++loader) + { + // Le loader supporte-t-il les données ? + if (!std::get<1>(*loader)(stream, parameters)) + continue; + + // On repositionne le stream au début + stream.SetCursorPos(streamPos); + + // Chargement de la ressource + if (std::get<2>(*loader)(resource, stream, parameters)) + return true; + + NazaraWarning("Loader failed"); + stream.SetCursorPos(streamPos); // On repositionne au début + } + + NazaraError("Failed to load file: no loader"); + return false; +} + +template +void NzResourceLoader::RegisterLoader(const NzString& fileExtensions, CheckFunction checkFunc, LoadFunction loadFunc) +{ + ///FIXME: Trouver une alternative à ce code monstrueux + std::vector exts; + fileExtensions.SplitAny(exts, " /\\.,;|-_"); + + std::set extensions; + std::copy(exts.begin(), exts.end(), std::inserter(extensions, extensions.begin())); + + Type::s_loaders.insert(std::make_tuple(std::move(extensions), checkFunc, loadFunc)); +} + +template +void NzResourceLoader::UnregisterLoader(const NzString& fileExtensions, CheckFunction checkFunc, LoadFunction loadFunc) +{ + std::vector exts; + fileExtensions.SplitAny(exts, " /\\.,;|-_"); + + std::set extensions; + std::copy(exts.begin(), exts.end(), std::inserter(extensions, extensions.begin())); + + Type::s_loaders.erase(std::make_tuple(std::move(extensions), checkFunc, loadFunc)); +} + +#include diff --git a/include/Nazara/Core/String.hpp b/include/Nazara/Core/String.hpp index 33b323d01..52f715e9f 100644 --- a/include/Nazara/Core/String.hpp +++ b/include/Nazara/Core/String.hpp @@ -1,321 +1,321 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Core module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef NAZARA_STRING_HPP -#define NAZARA_STRING_HPP - -#include -#include -#include -#include -#include - -#if NAZARA_CORE_THREADSAFE && NAZARA_THREADSAFETY_STRING -#include -#else -#include -#endif - -class NzAbstractHash; -class NzHashDigest; - -class NAZARA_API NzString : public NzHashable -{ - public: - enum Flags - { - None = 0x00, // Mode par défaut - CaseInsensitive = 0x01, // Insensible à la casse - HandleUtf8 = 0x02, // Traite les octets comme une suite de caractères UTF-8 - TrimOnlyLeft = 0x04, // Trim(med), ne coupe que la partie gauche de la chaîne - TrimOnlyRight = 0x08 // Trim(med), ne coupe que la partie droite de la chaîne - }; - - struct SharedString; - - NzString(); - NzString(char character); - NzString(const char* string); - NzString(const std::string& string); - NzString(const NzString& string); - NzString(NzString&& string) noexcept; - NzString(SharedString* sharedString); - ~NzString(); - - NzString& Append(char character); - NzString& Append(const char* string); - NzString& Append(const NzString& string); - - void Clear(bool keepBuffer = false); - - bool Contains(char character, int start = 0, nzUInt32 flags = None) const; - bool Contains(const char* string, int start = 0, nzUInt32 flags = None) const; - bool Contains(const NzString& string, int start = 0, nzUInt32 flags = None) const; - - unsigned int Count(char character, int start = 0, nzUInt32 flags = None) const; - unsigned int Count(const char* string, int start = 0, nzUInt32 flags = None) const; - unsigned int Count(const NzString& string, int start = 0, nzUInt32 flags = None) const; - unsigned int CountAny(const char* string, int start = 0, nzUInt32 flags = None) const; - unsigned int CountAny(const NzString& string, int start = 0, nzUInt32 flags = None) const; - - bool EndsWith(char character, nzUInt32 flags = None) const; - bool EndsWith(const char* string, nzUInt32 flags = None) const; - bool EndsWith(const NzString& string, nzUInt32 flags = None) const; - - unsigned int Find(char character, int start = 0, nzUInt32 flags = None) const; - unsigned int Find(const char* string, int start = 0, nzUInt32 flags = None) const; - unsigned int Find(const NzString& string, int start = 0, nzUInt32 flags = None) const; - unsigned int FindAny(const char* string, int start = 0, nzUInt32 flags = None) const; - unsigned int FindAny(const NzString& string, int start = 0, nzUInt32 flags = None) const; - unsigned int FindLast(char character, int start = -1, nzUInt32 flags = None) const; - unsigned int FindLast(const char *string, int start = -1, nzUInt32 flags = None) const; - unsigned int FindLast(const NzString& string, int start = -1, nzUInt32 flags = None) const; - unsigned int FindLastAny(const char* string, int start = -1, nzUInt32 flags = None) const; - unsigned int FindLastAny(const NzString& string, int start = -1, nzUInt32 flags = None) const; - unsigned int FindLastWord(const char* string, int start = -1, nzUInt32 flags = None) const; - unsigned int FindLastWord(const NzString& string, int start = -1, nzUInt32 flags = None) const; - unsigned int FindWord(const char* string, int start = 0, nzUInt32 flags = None) const; - unsigned int FindWord(const NzString& string, int start = 0, nzUInt32 flags = None) const; - - char* GetBuffer(); - unsigned int GetCapacity() const; - const char* GetConstBuffer() const; - unsigned int GetLength() const; - unsigned int GetSize() const; - char* GetUtf8Buffer(unsigned int* size = nullptr) const; - char16_t* GetUtf16Buffer(unsigned int* size = nullptr) const; - char32_t* GetUtf32Buffer(unsigned int* size = nullptr) const; - wchar_t* GetWideBuffer(unsigned int* size = nullptr) const; - - NzString GetWord(unsigned int index, nzUInt32 flags = None) const; - unsigned int GetWordPosition(unsigned int index, nzUInt32 flags = None) const; - - NzString& Insert(int pos, char character); - NzString& Insert(int pos, const char* string); - NzString& Insert(int pos, const NzString& string); - - bool IsEmpty() const; - bool IsNull() const; - bool IsNumber(nzUInt8 radix = 10, nzUInt32 flags = CaseInsensitive) const; - - bool Match(const char* pattern) const; - bool Match(const NzString& pattern) const; - - NzString& Prepend(char character); - NzString& Prepend(const char* string); - NzString& Prepend(const NzString& string); - - unsigned int Replace(char oldCharacter, char newCharacter, int start = 0, nzUInt32 flags = None); - unsigned int Replace(const char* oldString, const char* replaceString, int start = 0, nzUInt32 flags = None); - unsigned int Replace(const NzString& oldString, const NzString& replaceString, int start = 0, nzUInt32 flags = None); - unsigned int ReplaceAny(const char* oldCharacters, char replaceCharacter, int start = 0, nzUInt32 flags = None); - //unsigned int ReplaceAny(const char* oldCharacters, const char* replaceString, int start = 0, nzUInt32 flags = None); - //unsigned int ReplaceAny(const NzString& oldCharacters, const NzString& replaceString, int start = 0, nzUInt32 flags = None); - - void Reserve(unsigned int bufferSize); - - NzString& Resize(int size, char character = ' '); - NzString Resized(int size, char character = ' ') const; - - NzString& Reverse(); - NzString Reversed() const; - - NzString Simplified(nzUInt32 flags = None) const; - NzString& Simplify(nzUInt32 flags = None); - - unsigned int Split(std::vector& result, char separation = ' ', int start = 0, nzUInt32 flags = None) const; - unsigned int Split(std::vector& result, const char* separation, int start = 0, nzUInt32 flags = None) const; - unsigned int Split(std::vector& result, const NzString& separation, int start = 0, nzUInt32 flags = None) const; - unsigned int SplitAny(std::vector& result, const char* separations, int start = 0, nzUInt32 flags = None) const; - unsigned int SplitAny(std::vector& result, const NzString& separations, int start = 0, nzUInt32 flags = None) const; - - bool StartsWith(char character, nzUInt32 flags = None) const; - bool StartsWith(const char* string, nzUInt32 flags = None) const; - bool StartsWith(const NzString& string, nzUInt32 flags = None) const; - - NzString Substr(int startPos, int endPos = -1) const; - NzString SubstrFrom(char character, int startPos = 0, bool fromLast = false, bool include = false, nzUInt32 flags = None) const; - NzString SubstrFrom(const char *string, int startPos = 0, bool fromLast = false, bool include = false, nzUInt32 flags = None) const; - NzString SubstrFrom(const NzString& string, int startPos = 0, bool fromLast = false, bool include = false, nzUInt32 flags = None) const; - NzString SubstrTo(char character, int startPos = 0, bool toLast = false, bool include = false, nzUInt32 flags = None) const; - NzString SubstrTo(const char *string, int startPos = 0, bool toLast = false, bool include = false, nzUInt32 flags = None) const; - NzString SubstrTo(const NzString& string, int startPos = 0, bool toLast = false, bool include = false, nzUInt32 flags = None) const; - - void Swap(NzString& str); - - bool ToBool(bool* value, nzUInt32 flags = None) const; - bool ToDouble(double* value) const; - bool ToInteger(long long* value, nzUInt8 radix = 10) const; - NzString ToLower(nzUInt32 flags = None) const; - NzString ToUpper(nzUInt32 flags = None) const; - - NzString& Trim(nzUInt32 flags = None); - NzString& Trim(char character, nzUInt32 flags = None); - NzString Trimmed(nzUInt32 flags = None) const; - NzString Trimmed(char character, nzUInt32 flags = None) const; - - // Méthodes STD - char* begin(); - const char* begin() const; - char* end(); - const char* end() const; - void push_front(char c); - void push_back(char c); - /*char* rbegin(); - const char* rbegin() const; - char* rend(); - const char* rend() const;*/ - - typedef const char& const_reference; - typedef char* iterator; - //typedef char* reverse_iterator; - typedef char value_type; - // Méthodes STD - - operator std::string() const; - - char& operator[](unsigned int pos); - char operator[](unsigned int pos) const; - - NzString& operator=(char character); - NzString& operator=(const char* string); - NzString& operator=(const std::string& string); - NzString& operator=(const NzString& string); - NzString& operator=(NzString&& string) noexcept; - - NzString operator+(char character) const; - NzString operator+(const char* string) const; - NzString operator+(const std::string& string) const; - NzString operator+(const NzString& string) const; - - NzString& operator+=(char character); - NzString& operator+=(const char* string); - NzString& operator+=(const std::string& string); - NzString& operator+=(const NzString& string); - - bool operator==(char character) const; - bool operator==(const char* string) const; - bool operator==(const std::string& string) const; - - bool operator!=(char character) const; - bool operator!=(const char* string) const; - bool operator!=(const std::string& string) const; - - bool operator<(char character) const; - bool operator<(const char* string) const; - bool operator<(const std::string& string) const; - - bool operator<=(char character) const; - bool operator<=(const char* string) const; - bool operator<=(const std::string& string) const; - - bool operator>(char character) const; - bool operator>(const char* string) const; - bool operator>(const std::string& string) const; - - bool operator>=(char character) const; - bool operator>=(const char* string) const; - bool operator>=(const std::string& string) const; - - static NzString Boolean(bool boolean); - static int Compare(const NzString& first, const NzString& second); - static NzString Number(float number); - static NzString Number(double number); - static NzString Number(long double number); - static NzString Number(signed char number, nzUInt8 radix = 10); - static NzString Number(unsigned char number, nzUInt8 radix = 10); - static NzString Number(short number, nzUInt8 radix = 10); - static NzString Number(unsigned short number, nzUInt8 radix = 10); - static NzString Number(int number, nzUInt8 radix = 10); - static NzString Number(unsigned int number, nzUInt8 radix = 10); - static NzString Number(long number, nzUInt8 radix = 10); - static NzString Number(unsigned long number, nzUInt8 radix = 10); - static NzString Number(long long number, nzUInt8 radix = 10); - static NzString Number(unsigned long long number, nzUInt8 radix = 10); - static NzString Pointer(const void* ptr); - static NzString Unicode(char32_t character); - static NzString Unicode(const char* u8String); - static NzString Unicode(const char16_t* u16String); - static NzString Unicode(const char32_t* u32String); - static NzString Unicode(const wchar_t* wString); - - NAZARA_API friend std::istream& operator>>(std::istream& in, NzString& string); - NAZARA_API friend std::ostream& operator<<(std::ostream& out, const NzString& string); - - NAZARA_API friend NzString operator+(char character, const NzString& string); - NAZARA_API friend NzString operator+(const char* string, const NzString& nstring); - NAZARA_API friend NzString operator+(const std::string& string, const NzString& nstring); - - NAZARA_API friend bool operator==(const NzString& first, const NzString& second); - NAZARA_API friend bool operator!=(const NzString& first, const NzString& second); - NAZARA_API friend bool operator<(const NzString& first, const NzString& second); - NAZARA_API friend bool operator<=(const NzString& first, const NzString& second); - NAZARA_API friend bool operator>(const NzString& first, const NzString& second); - NAZARA_API friend bool operator>=(const NzString& first, const NzString& second); - - NAZARA_API friend bool operator==(char character, const NzString& nstring); - NAZARA_API friend bool operator==(const char* string, const NzString& nstring); - NAZARA_API friend bool operator==(const std::string& string, const NzString& nstring); - - NAZARA_API friend bool operator!=(char character, const NzString& nstring); - NAZARA_API friend bool operator!=(const char* string, const NzString& nstring); - NAZARA_API friend bool operator!=(const std::string& string, const NzString& nstring); - - NAZARA_API friend bool operator<(char character, const NzString& nstring); - NAZARA_API friend bool operator<(const char* string, const NzString& nstring); - NAZARA_API friend bool operator<(const std::string& string, const NzString& nstring); - - NAZARA_API friend bool operator<=(char character, const NzString& nstring); - NAZARA_API friend bool operator<=(const char* string, const NzString& nstring); - NAZARA_API friend bool operator<=(const std::string& string, const NzString& nstring); - - NAZARA_API friend bool operator>(char character, const NzString& nstring); - NAZARA_API friend bool operator>(const char* string, const NzString& nstring); - NAZARA_API friend bool operator>(const std::string& string, const NzString& nstring); - - NAZARA_API friend bool operator>=(char character, const NzString& nstring); - NAZARA_API friend bool operator>=(const char* string, const NzString& nstring); - NAZARA_API friend bool operator>=(const std::string& string, const NzString& nstring); - - struct NAZARA_API SharedString - { - SharedString() = default; - - SharedString(unsigned short referenceCount, unsigned int bufferSize, unsigned int stringSize, char* str) : - capacity(bufferSize), - size(stringSize), - string(str), - refCount(referenceCount) - { - } - - unsigned int capacity; - unsigned int size; - char* string; - - unsigned short refCount = 1; - NazaraMutex(mutex) - }; - - static SharedString emptyString; - static unsigned int npos; - - private: - void EnsureOwnership(); - bool FillHash(NzHashImpl* hash) const; - void ReleaseString(); - - SharedString* m_sharedString; -}; - -namespace std -{ - NAZARA_API istream& getline(istream& is, NzString& str); - NAZARA_API istream& getline(istream& is, NzString& str, char delim); - NAZARA_API void swap(NzString& lhs, NzString& rhs); -} - -#endif // NAZARA_STRING_HPP +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_STRING_HPP +#define NAZARA_STRING_HPP + +#include +#include +#include +#include +#include + +#if NAZARA_CORE_THREADSAFE && NAZARA_THREADSAFETY_STRING +#include +#else +#include +#endif + +class NzAbstractHash; +class NzHashDigest; + +class NAZARA_API NzString : public NzHashable +{ + public: + enum Flags + { + None = 0x00, // Mode par défaut + CaseInsensitive = 0x01, // Insensible à la casse + HandleUtf8 = 0x02, // Traite les octets comme une suite de caractères UTF-8 + TrimOnlyLeft = 0x04, // Trim(med), ne coupe que la partie gauche de la chaîne + TrimOnlyRight = 0x08 // Trim(med), ne coupe que la partie droite de la chaîne + }; + + struct SharedString; + + NzString(); + NzString(char character); + NzString(const char* string); + NzString(const std::string& string); + NzString(const NzString& string); + NzString(NzString&& string) noexcept; + NzString(SharedString* sharedString); + ~NzString(); + + NzString& Append(char character); + NzString& Append(const char* string); + NzString& Append(const NzString& string); + + void Clear(bool keepBuffer = false); + + bool Contains(char character, int start = 0, nzUInt32 flags = None) const; + bool Contains(const char* string, int start = 0, nzUInt32 flags = None) const; + bool Contains(const NzString& string, int start = 0, nzUInt32 flags = None) const; + + unsigned int Count(char character, int start = 0, nzUInt32 flags = None) const; + unsigned int Count(const char* string, int start = 0, nzUInt32 flags = None) const; + unsigned int Count(const NzString& string, int start = 0, nzUInt32 flags = None) const; + unsigned int CountAny(const char* string, int start = 0, nzUInt32 flags = None) const; + unsigned int CountAny(const NzString& string, int start = 0, nzUInt32 flags = None) const; + + bool EndsWith(char character, nzUInt32 flags = None) const; + bool EndsWith(const char* string, nzUInt32 flags = None) const; + bool EndsWith(const NzString& string, nzUInt32 flags = None) const; + + unsigned int Find(char character, int start = 0, nzUInt32 flags = None) const; + unsigned int Find(const char* string, int start = 0, nzUInt32 flags = None) const; + unsigned int Find(const NzString& string, int start = 0, nzUInt32 flags = None) const; + unsigned int FindAny(const char* string, int start = 0, nzUInt32 flags = None) const; + unsigned int FindAny(const NzString& string, int start = 0, nzUInt32 flags = None) const; + unsigned int FindLast(char character, int start = -1, nzUInt32 flags = None) const; + unsigned int FindLast(const char *string, int start = -1, nzUInt32 flags = None) const; + unsigned int FindLast(const NzString& string, int start = -1, nzUInt32 flags = None) const; + unsigned int FindLastAny(const char* string, int start = -1, nzUInt32 flags = None) const; + unsigned int FindLastAny(const NzString& string, int start = -1, nzUInt32 flags = None) const; + unsigned int FindLastWord(const char* string, int start = -1, nzUInt32 flags = None) const; + unsigned int FindLastWord(const NzString& string, int start = -1, nzUInt32 flags = None) const; + unsigned int FindWord(const char* string, int start = 0, nzUInt32 flags = None) const; + unsigned int FindWord(const NzString& string, int start = 0, nzUInt32 flags = None) const; + + char* GetBuffer(); + unsigned int GetCapacity() const; + const char* GetConstBuffer() const; + unsigned int GetLength() const; + unsigned int GetSize() const; + char* GetUtf8Buffer(unsigned int* size = nullptr) const; + char16_t* GetUtf16Buffer(unsigned int* size = nullptr) const; + char32_t* GetUtf32Buffer(unsigned int* size = nullptr) const; + wchar_t* GetWideBuffer(unsigned int* size = nullptr) const; + + NzString GetWord(unsigned int index, nzUInt32 flags = None) const; + unsigned int GetWordPosition(unsigned int index, nzUInt32 flags = None) const; + + NzString& Insert(int pos, char character); + NzString& Insert(int pos, const char* string); + NzString& Insert(int pos, const NzString& string); + + bool IsEmpty() const; + bool IsNull() const; + bool IsNumber(nzUInt8 radix = 10, nzUInt32 flags = CaseInsensitive) const; + + bool Match(const char* pattern) const; + bool Match(const NzString& pattern) const; + + NzString& Prepend(char character); + NzString& Prepend(const char* string); + NzString& Prepend(const NzString& string); + + unsigned int Replace(char oldCharacter, char newCharacter, int start = 0, nzUInt32 flags = None); + unsigned int Replace(const char* oldString, const char* replaceString, int start = 0, nzUInt32 flags = None); + unsigned int Replace(const NzString& oldString, const NzString& replaceString, int start = 0, nzUInt32 flags = None); + unsigned int ReplaceAny(const char* oldCharacters, char replaceCharacter, int start = 0, nzUInt32 flags = None); + //unsigned int ReplaceAny(const char* oldCharacters, const char* replaceString, int start = 0, nzUInt32 flags = None); + //unsigned int ReplaceAny(const NzString& oldCharacters, const NzString& replaceString, int start = 0, nzUInt32 flags = None); + + void Reserve(unsigned int bufferSize); + + NzString& Resize(int size, char character = ' '); + NzString Resized(int size, char character = ' ') const; + + NzString& Reverse(); + NzString Reversed() const; + + NzString Simplified(nzUInt32 flags = None) const; + NzString& Simplify(nzUInt32 flags = None); + + unsigned int Split(std::vector& result, char separation = ' ', int start = 0, nzUInt32 flags = None) const; + unsigned int Split(std::vector& result, const char* separation, int start = 0, nzUInt32 flags = None) const; + unsigned int Split(std::vector& result, const NzString& separation, int start = 0, nzUInt32 flags = None) const; + unsigned int SplitAny(std::vector& result, const char* separations, int start = 0, nzUInt32 flags = None) const; + unsigned int SplitAny(std::vector& result, const NzString& separations, int start = 0, nzUInt32 flags = None) const; + + bool StartsWith(char character, nzUInt32 flags = None) const; + bool StartsWith(const char* string, nzUInt32 flags = None) const; + bool StartsWith(const NzString& string, nzUInt32 flags = None) const; + + NzString Substr(int startPos, int endPos = -1) const; + NzString SubstrFrom(char character, int startPos = 0, bool fromLast = false, bool include = false, nzUInt32 flags = None) const; + NzString SubstrFrom(const char *string, int startPos = 0, bool fromLast = false, bool include = false, nzUInt32 flags = None) const; + NzString SubstrFrom(const NzString& string, int startPos = 0, bool fromLast = false, bool include = false, nzUInt32 flags = None) const; + NzString SubstrTo(char character, int startPos = 0, bool toLast = false, bool include = false, nzUInt32 flags = None) const; + NzString SubstrTo(const char *string, int startPos = 0, bool toLast = false, bool include = false, nzUInt32 flags = None) const; + NzString SubstrTo(const NzString& string, int startPos = 0, bool toLast = false, bool include = false, nzUInt32 flags = None) const; + + void Swap(NzString& str); + + bool ToBool(bool* value, nzUInt32 flags = None) const; + bool ToDouble(double* value) const; + bool ToInteger(long long* value, nzUInt8 radix = 10) const; + NzString ToLower(nzUInt32 flags = None) const; + NzString ToUpper(nzUInt32 flags = None) const; + + NzString& Trim(nzUInt32 flags = None); + NzString& Trim(char character, nzUInt32 flags = None); + NzString Trimmed(nzUInt32 flags = None) const; + NzString Trimmed(char character, nzUInt32 flags = None) const; + + // Méthodes STD + char* begin(); + const char* begin() const; + char* end(); + const char* end() const; + void push_front(char c); + void push_back(char c); + /*char* rbegin(); + const char* rbegin() const; + char* rend(); + const char* rend() const;*/ + + typedef const char& const_reference; + typedef char* iterator; + //typedef char* reverse_iterator; + typedef char value_type; + // Méthodes STD + + operator std::string() const; + + char& operator[](unsigned int pos); + char operator[](unsigned int pos) const; + + NzString& operator=(char character); + NzString& operator=(const char* string); + NzString& operator=(const std::string& string); + NzString& operator=(const NzString& string); + NzString& operator=(NzString&& string) noexcept; + + NzString operator+(char character) const; + NzString operator+(const char* string) const; + NzString operator+(const std::string& string) const; + NzString operator+(const NzString& string) const; + + NzString& operator+=(char character); + NzString& operator+=(const char* string); + NzString& operator+=(const std::string& string); + NzString& operator+=(const NzString& string); + + bool operator==(char character) const; + bool operator==(const char* string) const; + bool operator==(const std::string& string) const; + + bool operator!=(char character) const; + bool operator!=(const char* string) const; + bool operator!=(const std::string& string) const; + + bool operator<(char character) const; + bool operator<(const char* string) const; + bool operator<(const std::string& string) const; + + bool operator<=(char character) const; + bool operator<=(const char* string) const; + bool operator<=(const std::string& string) const; + + bool operator>(char character) const; + bool operator>(const char* string) const; + bool operator>(const std::string& string) const; + + bool operator>=(char character) const; + bool operator>=(const char* string) const; + bool operator>=(const std::string& string) const; + + static NzString Boolean(bool boolean); + static int Compare(const NzString& first, const NzString& second); + static NzString Number(float number); + static NzString Number(double number); + static NzString Number(long double number); + static NzString Number(signed char number, nzUInt8 radix = 10); + static NzString Number(unsigned char number, nzUInt8 radix = 10); + static NzString Number(short number, nzUInt8 radix = 10); + static NzString Number(unsigned short number, nzUInt8 radix = 10); + static NzString Number(int number, nzUInt8 radix = 10); + static NzString Number(unsigned int number, nzUInt8 radix = 10); + static NzString Number(long number, nzUInt8 radix = 10); + static NzString Number(unsigned long number, nzUInt8 radix = 10); + static NzString Number(long long number, nzUInt8 radix = 10); + static NzString Number(unsigned long long number, nzUInt8 radix = 10); + static NzString Pointer(const void* ptr); + static NzString Unicode(char32_t character); + static NzString Unicode(const char* u8String); + static NzString Unicode(const char16_t* u16String); + static NzString Unicode(const char32_t* u32String); + static NzString Unicode(const wchar_t* wString); + + NAZARA_API friend std::istream& operator>>(std::istream& in, NzString& string); + NAZARA_API friend std::ostream& operator<<(std::ostream& out, const NzString& string); + + NAZARA_API friend NzString operator+(char character, const NzString& string); + NAZARA_API friend NzString operator+(const char* string, const NzString& nstring); + NAZARA_API friend NzString operator+(const std::string& string, const NzString& nstring); + + NAZARA_API friend bool operator==(const NzString& first, const NzString& second); + NAZARA_API friend bool operator!=(const NzString& first, const NzString& second); + NAZARA_API friend bool operator<(const NzString& first, const NzString& second); + NAZARA_API friend bool operator<=(const NzString& first, const NzString& second); + NAZARA_API friend bool operator>(const NzString& first, const NzString& second); + NAZARA_API friend bool operator>=(const NzString& first, const NzString& second); + + NAZARA_API friend bool operator==(char character, const NzString& nstring); + NAZARA_API friend bool operator==(const char* string, const NzString& nstring); + NAZARA_API friend bool operator==(const std::string& string, const NzString& nstring); + + NAZARA_API friend bool operator!=(char character, const NzString& nstring); + NAZARA_API friend bool operator!=(const char* string, const NzString& nstring); + NAZARA_API friend bool operator!=(const std::string& string, const NzString& nstring); + + NAZARA_API friend bool operator<(char character, const NzString& nstring); + NAZARA_API friend bool operator<(const char* string, const NzString& nstring); + NAZARA_API friend bool operator<(const std::string& string, const NzString& nstring); + + NAZARA_API friend bool operator<=(char character, const NzString& nstring); + NAZARA_API friend bool operator<=(const char* string, const NzString& nstring); + NAZARA_API friend bool operator<=(const std::string& string, const NzString& nstring); + + NAZARA_API friend bool operator>(char character, const NzString& nstring); + NAZARA_API friend bool operator>(const char* string, const NzString& nstring); + NAZARA_API friend bool operator>(const std::string& string, const NzString& nstring); + + NAZARA_API friend bool operator>=(char character, const NzString& nstring); + NAZARA_API friend bool operator>=(const char* string, const NzString& nstring); + NAZARA_API friend bool operator>=(const std::string& string, const NzString& nstring); + + struct NAZARA_API SharedString + { + SharedString() = default; + + SharedString(unsigned short referenceCount, unsigned int bufferSize, unsigned int stringSize, char* str) : + capacity(bufferSize), + size(stringSize), + string(str), + refCount(referenceCount) + { + } + + unsigned int capacity; + unsigned int size; + char* string; + + unsigned short refCount = 1; + NazaraMutex(mutex) + }; + + static SharedString emptyString; + static const unsigned int npos; + + private: + void EnsureOwnership(); + bool FillHash(NzHashImpl* hash) const; + void ReleaseString(); + + SharedString* m_sharedString; +}; + +namespace std +{ + NAZARA_API istream& getline(istream& is, NzString& str); + NAZARA_API istream& getline(istream& is, NzString& str, char delim); + NAZARA_API void swap(NzString& lhs, NzString& rhs); +} + +#endif // NAZARA_STRING_HPP diff --git a/include/Nazara/Math/Config.hpp b/include/Nazara/Math/Config.hpp index 6adf384cf..71ee93ee4 100644 --- a/include/Nazara/Math/Config.hpp +++ b/include/Nazara/Math/Config.hpp @@ -1,52 +1,49 @@ -/* - Nazara Engine - Mathematics module - - Copyright (C) 2012 Jérôme "Lynix" Leclercq (Lynix680@gmail.com) - Rémi "overdrivr" Bèges (remi.beges@laposte.net) - - Permission is hereby granted, free of charge, to any person obtaining a copy of - this software and associated documentation files (the "Software"), to deal in - the Software without restriction, including without limitation the rights to - use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - of the Software, and to permit persons to whom the Software is furnished to do - so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ - -#pragma once - -#ifndef NAZARA_CONFIG_MATH_HPP -#define NAZARA_CONFIG_MATH_HPP - -/// Chaque modification d'un paramètre du module nécessite une recompilation de celui-ci - -// Définit le radian comme l'unité utilisée pour les angles -#define NAZARA_MATH_ANGLE_RADIAN 0 - -// Définit la disposition des matrices en colonnes (Façon OpenGL) -#define NAZARA_MATH_MATRIX_COLUMN_MAJOR 1 - -// Optimise les opérations entre matrices affines (Demande plusieurs comparaisons pour déterminer si une matrice est affine) -#define NAZARA_MATH_MATRIX4_CHECK_AFFINE 0 - -// Active les tests de sécurité basés sur le code (Conseillé pour le développement) -#define NAZARA_MATH_SAFE 1 - -// Protège les classes des accès concurrentiels -#define NAZARA_MATH_THREADSAFE 1 - -// Les classes à protéger des accès concurrentiels -#define NAZARA_THREADSAFETY_MATRIX3 1 // NzMatrix3 (COW) -#define NAZARA_THREADSAFETY_MATRIX4 1 // NzMatrix4 (COW) - -#endif // NAZARA_CONFIG_MATH_HPP +/* + Nazara Engine - Mathematics module + + Copyright (C) 2012 Jérôme "Lynix" Leclercq (Lynix680@gmail.com) + Rémi "overdrivr" Bèges (remi.beges@laposte.net) + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do + so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#pragma once + +#ifndef NAZARA_CONFIG_MATH_HPP +#define NAZARA_CONFIG_MATH_HPP + +/// Chaque modification d'un paramètre du module nécessite une recompilation de celui-ci + +// Définit le radian comme l'unité utilisée pour les angles +#define NAZARA_MATH_ANGLE_RADIAN 0 + +// Optimise les opérations entre matrices affines (Demande plusieurs comparaisons pour déterminer si une matrice est affine) +#define NAZARA_MATH_MATRIX4_CHECK_AFFINE 0 + +// Active les tests de sécurité basés sur le code (Conseillé pour le développement) +#define NAZARA_MATH_SAFE 1 + +// Protège les classes des accès concurrentiels +#define NAZARA_MATH_THREADSAFE 1 + +// Les classes à protéger des accès concurrentiels +#define NAZARA_THREADSAFETY_MATRIX3 1 // NzMatrix3 (COW) +#define NAZARA_THREADSAFETY_MATRIX4 1 // NzMatrix4 (COW) + +#endif // NAZARA_CONFIG_MATH_HPP diff --git a/include/Nazara/Math/Matrix4.hpp b/include/Nazara/Math/Matrix4.hpp index 076d1b373..82919f28a 100644 --- a/include/Nazara/Math/Matrix4.hpp +++ b/include/Nazara/Math/Matrix4.hpp @@ -1,144 +1,145 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Mathematics module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef NAZARA_MATRIX4_HPP -#define NAZARA_MATRIX4_HPP - -#include -#include - -#if NAZARA_MATH_THREADSAFE && NAZARA_THREADSAFETY_MATRIX4 -#include -#else -#include -#endif - -template class NzEulerAngles; -template class NzQuaternion; -template class NzVector2; -template class NzVector3; -template class NzVector4; - -template class NzMatrix4 -{ - public: - NzMatrix4(); - NzMatrix4(T r11, T r12, T r13, T r14, - T r21, T r22, T r23, T r24, - T r31, T r32, T r33, T r34, - T r41, T r42, T r43, T r44); - NzMatrix4(const T matrix[16]); - //NzMatrix4(const NzMatrix3& matrix); - template explicit NzMatrix4(const NzMatrix4& matrix); - NzMatrix4(const NzMatrix4& matrix); - NzMatrix4(NzMatrix4&& matrix) noexcept; - ~NzMatrix4(); - - NzMatrix4 Concatenate(const NzMatrix4& matrix) const; - - T GetDeterminant() const; - NzMatrix4 GetInverse() const; - NzQuaternion GetRotation() const; - //NzMatrix3 GetRotationMatrix() const; - NzVector3 GetScale() const; - NzVector3 GetTranslation() const; - NzMatrix4 GetTransposed() const; - - bool HasNegativeScale() const; - bool HasScale() const; - - bool IsAffine() const; - bool IsDefined() const; - - void MakeIdentity(); - void MakeLookAt(const NzVector3& eye, const NzVector3& center, const NzVector3& up); - void MakeOrtho(T left, T top, T width, T height, T zNear = -1.0, T zFar = 1.0); - void MakePerspective(T angle, T ratio, T zNear, T zFar); - void MakeRotation(const NzQuaternion& rotation); - void MakeScale(const NzVector3& scale); - void MakeTranslation(const NzVector3& translation); - void MakeZero(); - - void Set(T r11, T r12, T r13, T r14, - T r21, T r22, T r23, T r24, - T r31, T r32, T r33, T r34, - T r41, T r42, T r43, T r44); - void Set(const T matrix[16]); - //NzMatrix4(const NzMatrix3& matrix); - void Set(const NzMatrix4& matrix); - void Set(NzMatrix4&& matrix); - template void Set(const NzMatrix4& matrix); - void SetRotation(const NzQuaternion& rotation); - void SetScale(const NzVector3& scale); - void SetTranslation(const NzVector3& translation); - - NzString ToString() const; - - NzVector2 Transform(const NzVector2& vector, T z = 0.0, T w = 1.0) const; - NzVector3 Transform(const NzVector3& vector, T w = 1.0) const; - NzVector4 Transform(const NzVector4& vector) const; - - NzMatrix4& Transpose(); - - operator NzString() const; - - operator T*(); - operator const T*() const; - - T& operator()(unsigned int x, unsigned int y); - const T& operator()(unsigned int x, unsigned int y) const; - - NzMatrix4& operator=(const NzMatrix4& matrix); - NzMatrix4& operator=(NzMatrix4&& matrix) noexcept; - - NzMatrix4 operator*(const NzMatrix4& matrix) const; - NzVector2 operator*(const NzVector2& vector) const; - NzVector3 operator*(const NzVector3& vector) const; - NzVector4 operator*(const NzVector4& vector) const; - NzMatrix4 operator*(T scalar) const; - - NzMatrix4& operator*=(const NzMatrix4& matrix); - NzMatrix4& operator*=(T scalar); - - static NzMatrix4 Concatenate(const NzMatrix4& m1, const NzMatrix4& m2); - static NzMatrix4 ConcatenateAffine(const NzMatrix4& m1, const NzMatrix4& m2); - static NzMatrix4 Identity(); - static NzMatrix4 LookAt(const NzVector3& eye, const NzVector3& center, const NzVector3& up); - static NzMatrix4 Ortho(T left, T top, T width, T height, T zNear = -1.0, T zFar = 1.0); - static NzMatrix4 Perspective(T angle, T ratio, T zNear, T zFar); - static NzMatrix4 Rotate(const NzQuaternion& rotation); - static NzMatrix4 Scale(const NzVector3& scale); - static NzMatrix4 Translate(const NzVector3& translation); - static NzMatrix4 Zero(); - - struct SharedMatrix - { - T m11, m12, m13, m14, - m21, m22, m23, m24, - m31, m32, m33, m34, - m41, m42, m43, m44; - - unsigned short refCount = 1; - NazaraMutex(mutex) - }; - - private: - void EnsureOwnership(); - void ReleaseMatrix(); - - SharedMatrix* m_sharedMatrix = nullptr; -}; - -template std::ostream& operator<<(std::ostream& out, const NzMatrix4& matrix); - -template NzMatrix4 operator*(T scale, const NzMatrix4& matrix); - -typedef NzMatrix4 NzMatrix4d; -typedef NzMatrix4 NzMatrix4f; - -#include - -#endif // NAZARA_MATRIX4_HPP +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Mathematics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_MATRIX4_HPP +#define NAZARA_MATRIX4_HPP + +#include +#include + +#if NAZARA_MATH_THREADSAFE && NAZARA_THREADSAFETY_MATRIX4 +#include +#else +#include +#endif + +template class NzEulerAngles; +template class NzQuaternion; +template class NzVector2; +template class NzVector3; +template class NzVector4; + +template class NzMatrix4 +{ + public: + NzMatrix4(); + NzMatrix4(T r11, T r12, T r13, T r14, + T r21, T r22, T r23, T r24, + T r31, T r32, T r33, T r34, + T r41, T r42, T r43, T r44); + NzMatrix4(const T matrix[16]); + //NzMatrix4(const NzMatrix3& matrix); + template explicit NzMatrix4(const NzMatrix4& matrix); + NzMatrix4(const NzMatrix4& matrix); + NzMatrix4(NzMatrix4&& matrix) noexcept; + ~NzMatrix4(); + + NzMatrix4 Concatenate(const NzMatrix4& matrix) const; + NzMatrix4 ConcatenateAffine(const NzMatrix4& matrix) const; + + T GetDeterminant() const; + NzMatrix4 GetInverse() const; + NzQuaternion GetRotation() const; + //NzMatrix3 GetRotationMatrix() const; + NzVector3 GetScale() const; + NzVector3 GetTranslation() const; + NzMatrix4 GetTransposed() const; + + bool HasNegativeScale() const; + bool HasScale() const; + + bool IsAffine() const; + bool IsDefined() const; + + void MakeIdentity(); + void MakeLookAt(const NzVector3& eye, const NzVector3& target, const NzVector3& up = NzVector3::Up()); + void MakeOrtho(T left, T top, T width, T height, T zNear = -1.0, T zFar = 1.0); + void MakePerspective(T angle, T ratio, T zNear, T zFar); + void MakeRotation(const NzQuaternion& rotation); + void MakeScale(const NzVector3& scale); + void MakeTranslation(const NzVector3& translation); + void MakeZero(); + + void Set(T r11, T r12, T r13, T r14, + T r21, T r22, T r23, T r24, + T r31, T r32, T r33, T r34, + T r41, T r42, T r43, T r44); + void Set(const T matrix[16]); + //NzMatrix4(const NzMatrix3& matrix); + void Set(const NzMatrix4& matrix); + void Set(NzMatrix4&& matrix); + template void Set(const NzMatrix4& matrix); + void SetRotation(const NzQuaternion& rotation); + void SetScale(const NzVector3& scale); + void SetTranslation(const NzVector3& translation); + + NzString ToString() const; + + NzVector2 Transform(const NzVector2& vector, T z = 0.0, T w = 1.0) const; + NzVector3 Transform(const NzVector3& vector, T w = 1.0) const; + NzVector4 Transform(const NzVector4& vector) const; + + NzMatrix4& Transpose(); + + operator NzString() const; + + operator T*(); + operator const T*() const; + + T& operator()(unsigned int x, unsigned int y); + const T& operator()(unsigned int x, unsigned int y) const; + + NzMatrix4& operator=(const NzMatrix4& matrix); + NzMatrix4& operator=(NzMatrix4&& matrix) noexcept; + + NzMatrix4 operator*(const NzMatrix4& matrix) const; + NzVector2 operator*(const NzVector2& vector) const; + NzVector3 operator*(const NzVector3& vector) const; + NzVector4 operator*(const NzVector4& vector) const; + NzMatrix4 operator*(T scalar) const; + + NzMatrix4& operator*=(const NzMatrix4& matrix); + NzMatrix4& operator*=(T scalar); + + static NzMatrix4 Concatenate(const NzMatrix4& m1, const NzMatrix4& m2); + static NzMatrix4 ConcatenateAffine(const NzMatrix4& m1, const NzMatrix4& m2); + static NzMatrix4 Identity(); + static NzMatrix4 LookAt(const NzVector3& eye, const NzVector3& target, const NzVector3& up = NzVector3::Up()); + static NzMatrix4 Ortho(T left, T top, T width, T height, T zNear = -1.0, T zFar = 1.0); + static NzMatrix4 Perspective(T angle, T ratio, T zNear, T zFar); + static NzMatrix4 Rotate(const NzQuaternion& rotation); + static NzMatrix4 Scale(const NzVector3& scale); + static NzMatrix4 Translate(const NzVector3& translation); + static NzMatrix4 Zero(); + + struct SharedMatrix + { + T m11, m12, m13, m14, + m21, m22, m23, m24, + m31, m32, m33, m34, + m41, m42, m43, m44; + + unsigned short refCount = 1; + NazaraMutex(mutex) + }; + + private: + void EnsureOwnership(); + void ReleaseMatrix(); + + SharedMatrix* m_sharedMatrix = nullptr; +}; + +template std::ostream& operator<<(std::ostream& out, const NzMatrix4& matrix); + +template NzMatrix4 operator*(T scale, const NzMatrix4& matrix); + +typedef NzMatrix4 NzMatrix4d; +typedef NzMatrix4 NzMatrix4f; + +#include + +#endif // NAZARA_MATRIX4_HPP diff --git a/include/Nazara/Math/Matrix4.inl b/include/Nazara/Math/Matrix4.inl index 2f11f83dd..0e4d6b230 100644 --- a/include/Nazara/Math/Matrix4.inl +++ b/include/Nazara/Math/Matrix4.inl @@ -1,998 +1,942 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Mathematics module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -//#include -///FIXME: Le MLT détecte de faux-leaks ici (Problème lié aux inline ?) - -#define F(a) static_cast(a) - -template -NzMatrix4::NzMatrix4() -{ -} - -template -NzMatrix4::NzMatrix4(T r11, T r12, T r13, T r14, - T r21, T r22, T r23, T r24, - T r31, T r32, T r33, T r34, - T r41, T r42, T r43, T r44) -{ - Set(r11, r12, r13, r14, - r21, r22, r23, r24, - r31, r32, r33, r34, - r41, r42, r43, r44); -} - -template -NzMatrix4::NzMatrix4(const T matrix[16]) -{ - Set(matrix); -} - -template -template -NzMatrix4::NzMatrix4(const NzMatrix4& matrix) -{ - Set(matrix); -} - -template -NzMatrix4::NzMatrix4(const NzMatrix4& matrix) -{ - Set(matrix); -} - -template -NzMatrix4::NzMatrix4(NzMatrix4&& matrix) noexcept -{ - Set(matrix); -} - -template -NzMatrix4::~NzMatrix4() -{ - ReleaseMatrix(); -} - -template -NzMatrix4 NzMatrix4::Concatenate(const NzMatrix4& matrix) const -{ - return Concatenate(*this, matrix); -} - -template -T NzMatrix4::GetDeterminant() const -{ - #if NAZARA_MATH_SAFE - if (!IsDefined()) - { - NazaraError("Matrix not defined"); - return F(0.0); - } - #endif - - T A = m_sharedMatrix->m22*(m_sharedMatrix->m33*m_sharedMatrix->m44 - m_sharedMatrix->m43*m_sharedMatrix->m34) - m_sharedMatrix->m32*(m_sharedMatrix->m23*m_sharedMatrix->m44 - m_sharedMatrix->m43*m_sharedMatrix->m24) + m_sharedMatrix->m42*(m_sharedMatrix->m23*m_sharedMatrix->m34 - m_sharedMatrix->m33*m_sharedMatrix->m24); - T B = m_sharedMatrix->m12*(m_sharedMatrix->m33*m_sharedMatrix->m44 - m_sharedMatrix->m43*m_sharedMatrix->m34) - m_sharedMatrix->m32*(m_sharedMatrix->m13*m_sharedMatrix->m44 - m_sharedMatrix->m43*m_sharedMatrix->m14) + m_sharedMatrix->m42*(m_sharedMatrix->m13*m_sharedMatrix->m34 - m_sharedMatrix->m33*m_sharedMatrix->m14); - T C = m_sharedMatrix->m12*(m_sharedMatrix->m23*m_sharedMatrix->m44 - m_sharedMatrix->m43*m_sharedMatrix->m24) - m_sharedMatrix->m22*(m_sharedMatrix->m13*m_sharedMatrix->m44 - m_sharedMatrix->m43*m_sharedMatrix->m14) + m_sharedMatrix->m42*(m_sharedMatrix->m13*m_sharedMatrix->m24 - m_sharedMatrix->m23*m_sharedMatrix->m14); - T D = m_sharedMatrix->m12*(m_sharedMatrix->m23*m_sharedMatrix->m34 - m_sharedMatrix->m33*m_sharedMatrix->m24) - m_sharedMatrix->m22*(m_sharedMatrix->m13*m_sharedMatrix->m34 - m_sharedMatrix->m33*m_sharedMatrix->m14) + m_sharedMatrix->m32*(m_sharedMatrix->m13*m_sharedMatrix->m24 - m_sharedMatrix->m23*m_sharedMatrix->m14); - - return m_sharedMatrix->m11*A - m_sharedMatrix->m21*B + m_sharedMatrix->m31*C - m_sharedMatrix->m41*D; -} - -template -NzMatrix4 NzMatrix4::GetInverse() const -{ - #if NAZARA_MATH_SAFE - if (!IsDefined()) - { - NazaraError("Matrix not defined"); - return NzMatrix4(); - } - #endif - - T det = GetDeterminant(); - if (!NzNumberEquals(det, F(0.0))) - { - return NzMatrix4((m_sharedMatrix->m22*(m_sharedMatrix->m33*m_sharedMatrix->m44 - m_sharedMatrix->m34*m_sharedMatrix->m43) - m_sharedMatrix->m32*(m_sharedMatrix->m23*m_sharedMatrix->m44 - m_sharedMatrix->m43*m_sharedMatrix->m24) + m_sharedMatrix->m42*(m_sharedMatrix->m23*m_sharedMatrix->m34 - m_sharedMatrix->m33*m_sharedMatrix->m24)) / det, - -(m_sharedMatrix->m12*(m_sharedMatrix->m33*m_sharedMatrix->m44 - m_sharedMatrix->m43*m_sharedMatrix->m34) - m_sharedMatrix->m32*(m_sharedMatrix->m13*m_sharedMatrix->m44 - m_sharedMatrix->m43*m_sharedMatrix->m14) + m_sharedMatrix->m42*(m_sharedMatrix->m13*m_sharedMatrix->m34 - m_sharedMatrix->m33*m_sharedMatrix->m14)) / det, - (m_sharedMatrix->m12*(m_sharedMatrix->m23*m_sharedMatrix->m44 - m_sharedMatrix->m43*m_sharedMatrix->m24) - m_sharedMatrix->m22*(m_sharedMatrix->m13*m_sharedMatrix->m44 - m_sharedMatrix->m43*m_sharedMatrix->m14) + m_sharedMatrix->m42*(m_sharedMatrix->m13*m_sharedMatrix->m24 - m_sharedMatrix->m23*m_sharedMatrix->m14)) / det, - -(m_sharedMatrix->m12*(m_sharedMatrix->m23*m_sharedMatrix->m34 - m_sharedMatrix->m33*m_sharedMatrix->m24) - m_sharedMatrix->m22*(m_sharedMatrix->m13*m_sharedMatrix->m34 - m_sharedMatrix->m33*m_sharedMatrix->m14) + m_sharedMatrix->m32*(m_sharedMatrix->m13*m_sharedMatrix->m24 - m_sharedMatrix->m23*m_sharedMatrix->m14)) / det, - - -(m_sharedMatrix->m21*(m_sharedMatrix->m33*m_sharedMatrix->m44 - m_sharedMatrix->m34*m_sharedMatrix->m43) - m_sharedMatrix->m23*(m_sharedMatrix->m31*m_sharedMatrix->m44 - m_sharedMatrix->m34*m_sharedMatrix->m41) + m_sharedMatrix->m24*(m_sharedMatrix->m31*m_sharedMatrix->m43 - m_sharedMatrix->m33*m_sharedMatrix->m41)) / det, - (m_sharedMatrix->m11*(m_sharedMatrix->m33*m_sharedMatrix->m44 - m_sharedMatrix->m34*m_sharedMatrix->m43) - m_sharedMatrix->m13*(m_sharedMatrix->m31*m_sharedMatrix->m44 - m_sharedMatrix->m34*m_sharedMatrix->m41) + m_sharedMatrix->m14*(m_sharedMatrix->m31*m_sharedMatrix->m43 - m_sharedMatrix->m33*m_sharedMatrix->m41)) / det, - -(m_sharedMatrix->m11*(m_sharedMatrix->m23*m_sharedMatrix->m44 - m_sharedMatrix->m24*m_sharedMatrix->m43) - m_sharedMatrix->m13*(m_sharedMatrix->m21*m_sharedMatrix->m44 - m_sharedMatrix->m24*m_sharedMatrix->m41) + m_sharedMatrix->m14*(m_sharedMatrix->m21*m_sharedMatrix->m43 - m_sharedMatrix->m23*m_sharedMatrix->m41)) / det, - (m_sharedMatrix->m11*(m_sharedMatrix->m23*m_sharedMatrix->m34 - m_sharedMatrix->m24*m_sharedMatrix->m33) - m_sharedMatrix->m13*(m_sharedMatrix->m21*m_sharedMatrix->m34 - m_sharedMatrix->m24*m_sharedMatrix->m31) + m_sharedMatrix->m14*(m_sharedMatrix->m21*m_sharedMatrix->m33 - m_sharedMatrix->m23*m_sharedMatrix->m31)) / det, - - (m_sharedMatrix->m21*(m_sharedMatrix->m32*m_sharedMatrix->m44 - m_sharedMatrix->m34*m_sharedMatrix->m42) - m_sharedMatrix->m22*(m_sharedMatrix->m31*m_sharedMatrix->m44 - m_sharedMatrix->m34*m_sharedMatrix->m41) + m_sharedMatrix->m24*(m_sharedMatrix->m31*m_sharedMatrix->m42 - m_sharedMatrix->m32*m_sharedMatrix->m41)) / det, - -(m_sharedMatrix->m11*(m_sharedMatrix->m32*m_sharedMatrix->m44 - m_sharedMatrix->m34*m_sharedMatrix->m42) - m_sharedMatrix->m12*(m_sharedMatrix->m31*m_sharedMatrix->m44 - m_sharedMatrix->m34*m_sharedMatrix->m41) + m_sharedMatrix->m14*(m_sharedMatrix->m31*m_sharedMatrix->m42 - m_sharedMatrix->m32*m_sharedMatrix->m41)) / det, - (m_sharedMatrix->m11*(m_sharedMatrix->m22*m_sharedMatrix->m44 - m_sharedMatrix->m24*m_sharedMatrix->m42) - m_sharedMatrix->m12*(m_sharedMatrix->m21*m_sharedMatrix->m44 - m_sharedMatrix->m24*m_sharedMatrix->m41) + m_sharedMatrix->m14*(m_sharedMatrix->m21*m_sharedMatrix->m42 - m_sharedMatrix->m22*m_sharedMatrix->m41)) / det, - -(m_sharedMatrix->m11*(m_sharedMatrix->m22*m_sharedMatrix->m34 - m_sharedMatrix->m24*m_sharedMatrix->m32) - m_sharedMatrix->m12*(m_sharedMatrix->m21*m_sharedMatrix->m34 - m_sharedMatrix->m24*m_sharedMatrix->m31) + m_sharedMatrix->m14*(m_sharedMatrix->m21*m_sharedMatrix->m32 - m_sharedMatrix->m22*m_sharedMatrix->m31)) / det, - - -(m_sharedMatrix->m21*(m_sharedMatrix->m32*m_sharedMatrix->m43 - m_sharedMatrix->m33*m_sharedMatrix->m42) - m_sharedMatrix->m22*(m_sharedMatrix->m31*m_sharedMatrix->m43 - m_sharedMatrix->m33*m_sharedMatrix->m41) + m_sharedMatrix->m23*(m_sharedMatrix->m31*m_sharedMatrix->m42 - m_sharedMatrix->m32*m_sharedMatrix->m41)) / det, - (m_sharedMatrix->m11*(m_sharedMatrix->m32*m_sharedMatrix->m43 - m_sharedMatrix->m33*m_sharedMatrix->m42) - m_sharedMatrix->m12*(m_sharedMatrix->m31*m_sharedMatrix->m43 - m_sharedMatrix->m33*m_sharedMatrix->m41) + m_sharedMatrix->m13*(m_sharedMatrix->m31*m_sharedMatrix->m42 - m_sharedMatrix->m32*m_sharedMatrix->m41)) / det, - -(m_sharedMatrix->m11*(m_sharedMatrix->m22*m_sharedMatrix->m43 - m_sharedMatrix->m23*m_sharedMatrix->m42) - m_sharedMatrix->m12*(m_sharedMatrix->m21*m_sharedMatrix->m43 - m_sharedMatrix->m23*m_sharedMatrix->m41) + m_sharedMatrix->m13*(m_sharedMatrix->m21*m_sharedMatrix->m42 - m_sharedMatrix->m22*m_sharedMatrix->m41)) / det, - (m_sharedMatrix->m11*(m_sharedMatrix->m22*m_sharedMatrix->m33 - m_sharedMatrix->m23*m_sharedMatrix->m32) - m_sharedMatrix->m12*(m_sharedMatrix->m21*m_sharedMatrix->m33 - m_sharedMatrix->m23*m_sharedMatrix->m31) + m_sharedMatrix->m13*(m_sharedMatrix->m21*m_sharedMatrix->m32 - m_sharedMatrix->m22*m_sharedMatrix->m31)) / det); - } - else - { - NazaraError("Matrix has no inverse"); - - return Identity(); - } -} - -template -NzVector3 NzMatrix4::GetScale() const -{ - #if NAZARA_MATH_SAFE - if (!IsDefined()) - { - NazaraError("Matrix not defined"); - return NzVector3(); - } - #endif - - return NzVector3(std::sqrt(m_sharedMatrix->m11*m_sharedMatrix->m11 + m_sharedMatrix->m21*m_sharedMatrix->m21 + m_sharedMatrix->m31*m_sharedMatrix->m31), - std::sqrt(m_sharedMatrix->m12*m_sharedMatrix->m12 + m_sharedMatrix->m22*m_sharedMatrix->m22 + m_sharedMatrix->m32*m_sharedMatrix->m32), - std::sqrt(m_sharedMatrix->m13*m_sharedMatrix->m13 + m_sharedMatrix->m23*m_sharedMatrix->m23 + m_sharedMatrix->m33*m_sharedMatrix->m33)); -} - -template -NzVector3 NzMatrix4::GetTranslation() const -{ - #if NAZARA_MATH_SAFE - if (!IsDefined()) - { - NazaraError("Matrix not defined"); - return NzVector3(); - } - #endif - - #if NAZARA_MATH_MATRIX_COLUMN_MAJOR - return NzVector3(m_sharedMatrix->m41, m_sharedMatrix->m42, m_sharedMatrix->m43); - #else - return NzVector3(m_sharedMatrix->m14, m_sharedMatrix->m24, m_sharedMatrix->m34); - #endif -} - -template -NzMatrix4 NzMatrix4::GetTransposed() const -{ - #if NAZARA_MATH_SAFE - if (!IsDefined()) - { - NazaraError("Matrix not defined"); - return NzMatrix4(); - } - #endif - - return NzMatrix4(m_sharedMatrix->m11, m_sharedMatrix->m21, m_sharedMatrix->m31, m_sharedMatrix->m41, - m_sharedMatrix->m12, m_sharedMatrix->m22, m_sharedMatrix->m32, m_sharedMatrix->m42, - m_sharedMatrix->m13, m_sharedMatrix->m23, m_sharedMatrix->m33, m_sharedMatrix->m43, - m_sharedMatrix->m14, m_sharedMatrix->m24, m_sharedMatrix->m34, m_sharedMatrix->m44); -} - -template -bool NzMatrix4::HasNegativeScale() const -{ - return GetDeterminant() < F(0.0); -} - -template -bool NzMatrix4::HasScale() const -{ - #if NAZARA_MATH_SAFE - if (!IsDefined()) - { - NazaraError("Matrix not defined"); - return false; - } - #endif - - T t = m_sharedMatrix->m11*m_sharedMatrix->m11 + m_sharedMatrix->m21*m_sharedMatrix->m21 + m_sharedMatrix->m31*m_sharedMatrix->m31; - if (!NzNumberEquals(t, F(1.0))) - return true; - - t = m_sharedMatrix->m12*m_sharedMatrix->m12 + m_sharedMatrix->m22*m_sharedMatrix->m22 + m_sharedMatrix->m32*m_sharedMatrix->m32; - if (!NzNumberEquals(t, F(1.0))) - return true; - - t = m_sharedMatrix->m13*m_sharedMatrix->m13 + m_sharedMatrix->m23*m_sharedMatrix->m23 + m_sharedMatrix->m33*m_sharedMatrix->m33; - if (!NzNumberEquals(t, F(1.0))) - return true; - - return false; -} - -template -bool NzMatrix4::IsAffine() const -{ - #if NAZARA_MATH_SAFE - if (!IsDefined()) - { - NazaraError("Matrix not defined"); - return false; - } - #endif - - #if NAZARA_MATH_MATRIX_COLUMN_MAJOR - return NzNumberEquals(m_sharedMatrix->m14, F(0.0)) && - NzNumberEquals(m_sharedMatrix->m24, F(0.0)) && - NzNumberEquals(m_sharedMatrix->m34, F(0.0)) && - NzNumberEquals(m_sharedMatrix->m44, F(1.0)); - #else - return NzNumberEquals(m_sharedMatrix->m41, F(0.0)) && - NzNumberEquals(m_sharedMatrix->m42, F(0.0)) && - NzNumberEquals(m_sharedMatrix->m43, F(0.0)) && - NzNumberEquals(m_sharedMatrix->m44, F(1.0)); - #endif -} - -template -bool NzMatrix4::IsDefined() const -{ - return m_sharedMatrix != nullptr; -} - -template -void NzMatrix4::MakeIdentity() -{ - Set(F(1.0), F(0.0), F(0.0), F(0.0), - F(0.0), F(1.0), F(0.0), F(0.0), - F(0.0), F(0.0), F(1.0), F(0.0), - F(0.0), F(0.0), F(0.0), F(1.0)); -} - -template -void NzMatrix4::MakeOrtho(T left, T top, T width, T height, T zNear, T zFar) -{ - #if NAZARA_MATH_MATRIX_COLUMN_MAJOR - Set(F(2.0)/(width-left), F(0.0), F(0.0), -(width+left)/(width-left), - F(0.0), F(2.0)/(top-height), F(0.0), -(top+height)/(top-height), - F(0.0), F(0.0), F(-2.0)/(zFar-zNear), -(zFar+zNear)/(zFar-zNear), - F(0.0), F(0.0), F(0.0), F(1.0)); - #else - Set(F(2.0)/(width-left), F(0.0), F(0.0), F(0.0), - F(0.0), F(2.0)/(top-height), F(0.0), F(0.0), - F(0.0), F(0.0), F(-2.0)/(zFar-zNear), F(0.0), - -(width+left)/(width-left), -(top+height)/(top-height), -(zFar+zNear)/(zFar-zNear), F(1.0)); - #endif -} - -template -void NzMatrix4::MakeLookAt(const NzVector3& eye, const NzVector3& center, const NzVector3& up) -{ - // http://www.opengl.org/sdk/docs/man/xhtml/gluLookAt.xml - NzVector3 f = center - eye; - f.Normalize(); - - NzVector3 u = up; - u.Normalize(); - - NzVector3 s = f.CrossProduct(u); - u = s.CrossProduct(f); - - #if NAZARA_MATH_MATRIX_COLUMN_MAJOR - Set(s.x, s.y, s.z, F(0.0), - u.x, u.y, u.z, F(0.0), - -f.x, -f.y, -f.z, F(0.0), - F(0.0), F(0.0), F(0.0), F(1.0)); - #else - Set(s.x, u.x, -f.x, F(0.0), - s.y, u.y, -f.y, F(0.0), - s.z, u.z, -f.z, F(0.0), - F(0.0), F(0.0), F(0.0), F(1.0)); - #endif - - Concatenate(Translate(-eye)); -} - -template -void NzMatrix4::MakePerspective(T angle, T ratio, T zNear, T zFar) -{ - #if NAZARA_MATH_ANGLE_RADIAN - angle /= F(2.0); - #else - angle = NzDegreeToRadian(angle/2); - #endif - - T f = F(1.0) / std::tan(angle); - - #if NAZARA_MATH_MATRIX_COLUMN_MAJOR - Set(f / ratio, F(0.0), F(0.0), F(0.0), - F(0.0), f, F(0.0), F(0.0), - F(0.0), F(0.0), (zNear+zFar) / (zNear-zFar), F(-1.0), - F(0.0), F(0.0), (F(2.0)*zNear*zFar) / (zNear-zFar), F(1.0)); - #else - Set(f / ratio, F(0.0), F(0.0), F(0.0), - F(0.0), f, F(0.0), F(0.0), - F(0.0), F(0.0), (zNear+zFar) / (zNear-zFar), (F(2.0)*zNear*zFar) / (zNear-zFar), - F(0.0), F(0.0), F(-1.0), F(1.0)); - #endif -} - -template -void NzMatrix4::MakeRotation(const NzQuaternion& rotation) -{ - // http://stackoverflow.com/questions/1556260/convert-quaternion-rotation-to-rotation-matrix - #if NAZARA_MATH_MATRIX_COLUMN_MAJOR - Set(F(1.0) - F(2.0)*rotation.y*rotation.y - F(2.0)*rotation.z*rotation.z, F(2.0)*rotation.x*rotation.y - F(2.0)*rotation.z*rotation.w, F(2.0)*rotation.x*rotation.z + F(2.0)*rotation.y*rotation.w, F(0.0), - F(2.0)*rotation.x*rotation.y + F(2.0)*rotation.z*rotation.w, F(1.0) - F(2.0)*rotation.x*rotation.x - F(2.0)*rotation.z*rotation.z, F(2.0)*rotation.y*rotation.z - F(2.0)*rotation.x*rotation.w, F(0.0), - F(2.0)*rotation.x*rotation.z - F(2.0)*rotation.y*rotation.w, F(2.0)*rotation.y*rotation.z + F(2.0)*rotation.x*rotation.w, F(1.0) - F(2.0)*rotation.x*rotation.x - F(2.0)*rotation.y*rotation.y, F(0.0), - F(0.0), F(0.0), F(0.0), F(1.0)); - #else - Set(F(1.0) - F(2.0)*rotation.y*rotation.y - F(2.0)*rotation.z*rotation.z, F(2.0)*rotation.x*rotation.y + F(2.0)*rotation.z*rotation.w, F(2.0)*rotation.x*rotation.z - F(2.0)*rotation.y*rotation.w, F(0.0), - F(2.0)*rotation.x*rotation.y - F(2.0)*rotation.z*rotation.w, F(1.0) - F(2.0)*rotation.x*rotation.x - F(2.0)*rotation.z*rotation.z, F(2.0)*rotation.y*rotation.z + F(2.0)*rotation.x*rotation.w, F(0.0), - F(2.0)*rotation.x*rotation.z + F(2.0)*rotation.y*rotation.w, F(2.0)*rotation.y*rotation.z - F(2.0)*rotation.x*rotation.w, F(1.0) - F(2.0)*rotation.x*rotation.x - F(2.0)*rotation.y*rotation.y, F(0.0), - F(0.0), F(0.0), F(0.0), F(1.0)); - #endif -} - -template -void NzMatrix4::MakeScale(const NzVector3& scale) -{ - Set(scale.x, F(0.0), F(0.0), F(0.0), - F(0.0), scale.y, F(0.0), F(0.0), - F(0.0), F(0.0), scale.z, F(0.0), - F(0.0), F(0.0), F(0.0), F(1.0)); -} - -template -void NzMatrix4::MakeTranslation(const NzVector3& translation) -{ - #if NAZARA_MATH_MATRIX_COLUMN_MAJOR - Set(F(1.0), F(0.0), F(0.0), F(0.0), - F(0.0), F(1.0), F(0.0), F(0.0), - F(0.0), F(0.0), F(1.0), F(0.0), - translation.x, translation.y, translation.z, F(1.0)); - #else - Set(F(1.0), F(0.0), F(0.0), translation.x, - F(0.0), F(1.0), F(0.0), translation.y, - F(0.0), F(0.0), F(1.0), translation.z, - F(0.0), F(0.0), F(0.0), F(1.0)); - #endif -} - -template -void NzMatrix4::MakeZero() -{ - Set(F(0.0), F(0.0), F(0.0), F(0.0), - F(0.0), F(0.0), F(0.0), F(0.0), - F(0.0), F(0.0), F(0.0), F(0.0), - F(0.0), F(0.0), F(0.0), F(0.0)); -} - -template -void NzMatrix4::Set(T r11, T r12, T r13, T r14, - T r21, T r22, T r23, T r24, - T r31, T r32, T r33, T r34, - T r41, T r42, T r43, T r44) -{ - EnsureOwnership(); - - m_sharedMatrix->m11 = r11; - m_sharedMatrix->m12 = r12; - m_sharedMatrix->m13 = r13; - m_sharedMatrix->m14 = r14; - m_sharedMatrix->m21 = r21; - m_sharedMatrix->m22 = r22; - m_sharedMatrix->m23 = r23; - m_sharedMatrix->m24 = r24; - m_sharedMatrix->m31 = r31; - m_sharedMatrix->m32 = r32; - m_sharedMatrix->m33 = r33; - m_sharedMatrix->m34 = r34; - m_sharedMatrix->m41 = r41; - m_sharedMatrix->m42 = r42; - m_sharedMatrix->m43 = r43; - m_sharedMatrix->m44 = r44; -} - -template -void NzMatrix4::Set(const T matrix[16]) -{ - EnsureOwnership(); - - // Ici nous sommes certains de la continuité des éléments en mémoire - std::memcpy(&m_sharedMatrix->m11, matrix, 16*sizeof(T)); -} - -template -void NzMatrix4::Set(const NzMatrix4& matrix) -{ - ReleaseMatrix(); - - m_sharedMatrix = matrix.m_sharedMatrix; - if (m_sharedMatrix) - { - NazaraMutexLock(m_sharedMatrix->mutex); - m_sharedMatrix->refCount++; - NazaraMutexUnlock(m_sharedMatrix->mutex); - } -} - -template -void NzMatrix4::Set(NzMatrix4&& matrix) -{ - std::swap(m_sharedMatrix, matrix.m_sharedMatrix); -} - -template -template -void NzMatrix4::Set(const NzMatrix4& matrix) -{ - Set(F(matrix.m_sharedMatrix->m11), F(matrix.m_sharedMatrix->m12), F(matrix.m_sharedMatrix->m13), F(matrix.m_sharedMatrix->m14), - F(matrix.m_sharedMatrix->m21), F(matrix.m_sharedMatrix->m22), F(matrix.m_sharedMatrix->m23), F(matrix.m_sharedMatrix->m24), - F(matrix.m_sharedMatrix->m31), F(matrix.m_sharedMatrix->m32), F(matrix.m_sharedMatrix->m33), F(matrix.m_sharedMatrix->m34), - F(matrix.m_sharedMatrix->m41), F(matrix.m_sharedMatrix->m42), F(matrix.m_sharedMatrix->m43), F(matrix.m_sharedMatrix->m44)); -} - -template -void NzMatrix4::SetRotation(const NzQuaternion& rotation) -{ - // http://www.flipcode.com/documents/matrfaq.html#Q54 - EnsureOwnership(); - - m_sharedMatrix->m11 = F(1.0) - F(2.0)*rotation.y*rotation.y - F(2.0)*rotation.z*rotation.z; - m_sharedMatrix->m22 = F(1.0) - F(2.0)*rotation.x*rotation.x - F(2.0)*rotation.z*rotation.z; - m_sharedMatrix->m33 = F(1.0) - F(2.0)*rotation.x*rotation.x - F(2.0)*rotation.y*rotation.y; - - #if NAZARA_MATH_MATRIX_COLUMN_MAJOR - m_sharedMatrix->m12 = F(2.0)*rotation.x*rotation.y - F(2.0)*rotation.z*rotation.w; - m_sharedMatrix->m13 = F(2.0)*rotation.x*rotation.z + F(2.0)*rotation.y*rotation.w; - m_sharedMatrix->m21 = F(2.0)*rotation.x*rotation.y + F(2.0)*rotation.z*rotation.w; - m_sharedMatrix->m23 = F(2.0)*rotation.y*rotation.z - F(2.0)*rotation.x*rotation.w; - m_sharedMatrix->m31 = F(2.0)*rotation.x*rotation.z - F(2.0)*rotation.y*rotation.w; - m_sharedMatrix->m32 = F(2.0)*rotation.y*rotation.z + F(2.0)*rotation.x*rotation.w; - #else - m_sharedMatrix->m12 = F(2.0)*rotation.x*rotation.y + F(2.0)*rotation.z*rotation.w; - m_sharedMatrix->m13 = F(2.0)*rotation.x*rotation.z - F(2.0)*rotation.y*rotation.w; - m_sharedMatrix->m21 = F(2.0)*rotation.x*rotation.y - F(2.0)*rotation.z*rotation.w; - m_sharedMatrix->m23 = F(2.0)*rotation.y*rotation.z + F(2.0)*rotation.x*rotation.w; - m_sharedMatrix->m31 = F(2.0)*rotation.x*rotation.z + F(2.0)*rotation.y*rotation.w; - m_sharedMatrix->m32 = F(2.0)*rotation.y*rotation.z - F(2.0)*rotation.x*rotation.w; - #endif -} - -template -void NzMatrix4::SetScale(const NzVector3& scale) -{ - EnsureOwnership(); - - m_sharedMatrix->m11 = scale.x; - m_sharedMatrix->m22 = scale.y; - m_sharedMatrix->m33 = scale.z; -} - -template -void NzMatrix4::SetTranslation(const NzVector3& translation) -{ - EnsureOwnership(); - - #if NAZARA_MATH_MATRIX_COLUMN_MAJOR - m_sharedMatrix->m41 = translation.x; - m_sharedMatrix->m42 = translation.y; - m_sharedMatrix->m43 = translation.z; - m_sharedMatrix->m44 = F(1.0); - #else - m_sharedMatrix->m14 = translation.x; - m_sharedMatrix->m24 = translation.y; - m_sharedMatrix->m34 = translation.z; - m_sharedMatrix->m44 = F(1.0); - #endif -} - -template -NzString NzMatrix4::ToString() const -{ - if (!m_sharedMatrix) - return "Matrix4(undefined)"; - - NzStringStream ss; - return ss << "Matrix4(" << m_sharedMatrix->m11 << ", " << m_sharedMatrix->m12 << ", " << m_sharedMatrix->m13 << ", " << m_sharedMatrix->m14 << ",\n" - << " " << m_sharedMatrix->m21 << ", " << m_sharedMatrix->m22 << ", " << m_sharedMatrix->m23 << ", " << m_sharedMatrix->m24 << ",\n" - << " " << m_sharedMatrix->m31 << ", " << m_sharedMatrix->m32 << ", " << m_sharedMatrix->m33 << ", " << m_sharedMatrix->m34 << ",\n" - << " " << m_sharedMatrix->m41 << ", " << m_sharedMatrix->m42 << ", " << m_sharedMatrix->m43 << ", " << m_sharedMatrix->m44 << ')'; -} - -template -NzVector2 NzMatrix4::Transform(const NzVector2& vector, T z, T w) const -{ - #if NAZARA_MATH_SAFE - if (!m_sharedMatrix) - { - NazaraError("Matrix not defined"); - return vector; - } - #endif - - return NzVector2(m_sharedMatrix->m11*vector.x + m_sharedMatrix->m12*vector.y + m_sharedMatrix->m13*z + m_sharedMatrix->m14*w, - m_sharedMatrix->m21*vector.x + m_sharedMatrix->m22*vector.y + m_sharedMatrix->m23*z + m_sharedMatrix->m24*w); -} - -template -NzVector3 NzMatrix4::Transform(const NzVector3& vector, T w) const -{ - #if NAZARA_MATH_SAFE - if (!m_sharedMatrix) - { - NazaraError("Matrix not defined"); - return vector; - } - #endif - - return NzVector3(m_sharedMatrix->m11*vector.x + m_sharedMatrix->m12*vector.y + m_sharedMatrix->m13*vector.z + m_sharedMatrix->m14*w, - m_sharedMatrix->m21*vector.x + m_sharedMatrix->m22*vector.y + m_sharedMatrix->m23*vector.z + m_sharedMatrix->m24*w, - m_sharedMatrix->m31*vector.x + m_sharedMatrix->m32*vector.y + m_sharedMatrix->m33*vector.z + m_sharedMatrix->m34*w); -} - -template -NzVector4 NzMatrix4::Transform(const NzVector4& vector) const -{ - #if NAZARA_MATH_SAFE - if (!m_sharedMatrix) - { - NazaraError("Matrix not defined"); - return vector; - } - #endif - - return NzVector4(m_sharedMatrix->m11*vector.x + m_sharedMatrix->m12*vector.y + m_sharedMatrix->m13*vector.z + m_sharedMatrix->m14*vector.w, - m_sharedMatrix->m21*vector.x + m_sharedMatrix->m22*vector.y + m_sharedMatrix->m23*vector.z + m_sharedMatrix->m24*vector.w, - m_sharedMatrix->m31*vector.x + m_sharedMatrix->m32*vector.y + m_sharedMatrix->m33*vector.z + m_sharedMatrix->m34*vector.w, - m_sharedMatrix->m41*vector.x + m_sharedMatrix->m42*vector.y + m_sharedMatrix->m43*vector.z + m_sharedMatrix->m44*vector.w); -} - -template -NzMatrix4& NzMatrix4::Transpose() -{ - #if NAZARA_MATH_SAFE - if (!m_sharedMatrix) - { - NazaraError("Matrix not defined"); - return *this; - } - #endif - - std::swap(m_sharedMatrix->m12, m_sharedMatrix->m21); - std::swap(m_sharedMatrix->m13, m_sharedMatrix->m31); - std::swap(m_sharedMatrix->m14, m_sharedMatrix->m41); - std::swap(m_sharedMatrix->m23, m_sharedMatrix->m32); - std::swap(m_sharedMatrix->m24, m_sharedMatrix->m42); - std::swap(m_sharedMatrix->m34, m_sharedMatrix->m43); - - return *this; -} - -template -NzMatrix4::operator NzString() const -{ - return ToString(); -} - -template -NzMatrix4::operator T*() -{ - #if NAZARA_MATH_SAFE - if (!m_sharedMatrix) - { - NazaraError("Matrix not defined"); - return nullptr; - } - #endif - - EnsureOwnership(); - - return &m_sharedMatrix->m11; -} - -template -NzMatrix4::operator const T*() const -{ - #if NAZARA_MATH_SAFE - if (!m_sharedMatrix) - { - NazaraError("Matrix not defined"); - return nullptr; - } - #endif - - return &m_sharedMatrix->m11; -} - -template -T& NzMatrix4::operator()(unsigned int x, unsigned int y) -{ - #if NAZARA_MATH_SAFE - if (x > 3 || y > 3) - { - NzStringStream ss; - ss << __FILE__ << ':' << __LINE__ << ": Index out of range: (" << x << ", " << y << ") > (3,3)"; - - throw std::out_of_range(ss.ToString()); - } - #endif - - EnsureOwnership(); - - return (&m_sharedMatrix->m11)[y*4+x]; -} - -template -const T& NzMatrix4::operator()(unsigned int x, unsigned int y) const -{ - #if NAZARA_MATH_SAFE - if (!m_sharedMatrix) - { - NazaraError("Matrix not defined"); - throw std::runtime_error("Tried to access element of Matrix not defined"); - } - - if (x > 3 || y > 3) - { - NzStringStream ss; - ss << __FILE__ << ':' << __LINE__ << ": Index out of range: (" << x << ", " << y << ") > (3,3)"; - - throw std::out_of_range(ss.ToString()); - } - #endif - - return (&m_sharedMatrix->m11)[y*4+x]; -} - -template -NzMatrix4& NzMatrix4::operator=(const NzMatrix4& matrix) -{ - Set(matrix); - - return *this; -} - -template -NzMatrix4& NzMatrix4::operator=(NzMatrix4&& matrix) noexcept -{ - Set(matrix); - - return *this; -} - -template -NzMatrix4 NzMatrix4::operator*(const NzMatrix4& matrix) const -{ - return Concatenate(matrix); -} - -template -NzVector2 NzMatrix4::operator*(const NzVector2& vector) const -{ - return Transform(vector); -} - -template -NzVector3 NzMatrix4::operator*(const NzVector3& vector) const -{ - return Transform(vector); -} - -template -NzVector4 NzMatrix4::operator*(const NzVector4& vector) const -{ - return Transform(vector); -} - -template -NzMatrix4 NzMatrix4::operator*(T scalar) const -{ - #if NAZARA_MATH_SAFE - if (!m_sharedMatrix) - { - NazaraError("Matrix not defined"); - return *this; - } - #endif - - return NzMatrix4(m_sharedMatrix->m11 * scalar, m_sharedMatrix->m12 * scalar, m_sharedMatrix->m13 * scalar, m_sharedMatrix->m14 * scalar, - m_sharedMatrix->m21 * scalar, m_sharedMatrix->m22 * scalar, m_sharedMatrix->m23 * scalar, m_sharedMatrix->m24 * scalar, - m_sharedMatrix->m31 * scalar, m_sharedMatrix->m32 * scalar, m_sharedMatrix->m33 * scalar, m_sharedMatrix->m34 * scalar, - m_sharedMatrix->m41 * scalar, m_sharedMatrix->m42 * scalar, m_sharedMatrix->m43 * scalar, m_sharedMatrix->m44 * scalar); -} - -template -NzMatrix4& NzMatrix4::operator*=(const NzMatrix4& matrix) -{ - #if NAZARA_MATH_SAFE - if (!m_sharedMatrix) - { - NazaraError("Matrix not defined"); - return *this; - } - #endif - - Set(Concatenate(*this, matrix)); - - return *this; -} - -template -NzMatrix4& NzMatrix4::operator*=(T scalar) -{ - #if NAZARA_MATH_SAFE - if (!m_sharedMatrix) - { - NazaraError("Matrix not defined"); - return *this; - } - #endif - - m_sharedMatrix->m11 *= scalar; - m_sharedMatrix->m12 *= scalar; - m_sharedMatrix->m13 *= scalar; - m_sharedMatrix->m14 *= scalar; - m_sharedMatrix->m21 *= scalar; - m_sharedMatrix->m22 *= scalar; - m_sharedMatrix->m23 *= scalar; - m_sharedMatrix->m24 *= scalar; - m_sharedMatrix->m31 *= scalar; - m_sharedMatrix->m32 *= scalar; - m_sharedMatrix->m33 *= scalar; - m_sharedMatrix->m34 *= scalar; - m_sharedMatrix->m41 *= scalar; - m_sharedMatrix->m42 *= scalar; - m_sharedMatrix->m43 *= scalar; - m_sharedMatrix->m44 *= scalar; - - return *this; -} - -template -NzMatrix4 NzMatrix4::Concatenate(const NzMatrix4& m1, const NzMatrix4& m2) -{ - #if NAZARA_MATH_SAFE - if (!m1.IsDefined()) - { - NazaraError("First matrix not defined"); - return NzMatrix4(); - } - - if (!m2.IsDefined()) - { - NazaraError("Second matrix not defined"); - return NzMatrix4(); - } - #endif - - #if NAZARA_MATH_MATRIX4_CHECK_AFFINE - if (m1.IsAffine() && m2.IsAffine()) - return ConcatenateAffine(m1, m2); - #endif - - return NzMatrix4(m1.m_sharedMatrix->m11*m2.m_sharedMatrix->m11 + m1.m_sharedMatrix->m12*m2.m_sharedMatrix->m21 + m1.m_sharedMatrix->m13*m2.m_sharedMatrix->m31 + m1.m_sharedMatrix->m14*m2.m_sharedMatrix->m41, - m1.m_sharedMatrix->m11*m2.m_sharedMatrix->m12 + m1.m_sharedMatrix->m12*m2.m_sharedMatrix->m22 + m1.m_sharedMatrix->m13*m2.m_sharedMatrix->m32 + m1.m_sharedMatrix->m14*m2.m_sharedMatrix->m42, - m1.m_sharedMatrix->m11*m2.m_sharedMatrix->m13 + m1.m_sharedMatrix->m12*m2.m_sharedMatrix->m23 + m1.m_sharedMatrix->m13*m2.m_sharedMatrix->m33 + m1.m_sharedMatrix->m14*m2.m_sharedMatrix->m43, - m1.m_sharedMatrix->m11*m2.m_sharedMatrix->m14 + m1.m_sharedMatrix->m12*m2.m_sharedMatrix->m24 + m1.m_sharedMatrix->m13*m2.m_sharedMatrix->m34 + m1.m_sharedMatrix->m14*m2.m_sharedMatrix->m44, - - m1.m_sharedMatrix->m21*m2.m_sharedMatrix->m11 + m1.m_sharedMatrix->m22*m2.m_sharedMatrix->m21 + m1.m_sharedMatrix->m23*m2.m_sharedMatrix->m31 + m1.m_sharedMatrix->m24*m2.m_sharedMatrix->m41, - m1.m_sharedMatrix->m21*m2.m_sharedMatrix->m12 + m1.m_sharedMatrix->m22*m2.m_sharedMatrix->m22 + m1.m_sharedMatrix->m23*m2.m_sharedMatrix->m32 + m1.m_sharedMatrix->m24*m2.m_sharedMatrix->m42, - m1.m_sharedMatrix->m21*m2.m_sharedMatrix->m13 + m1.m_sharedMatrix->m22*m2.m_sharedMatrix->m23 + m1.m_sharedMatrix->m23*m2.m_sharedMatrix->m33 + m1.m_sharedMatrix->m24*m2.m_sharedMatrix->m43, - m1.m_sharedMatrix->m21*m2.m_sharedMatrix->m14 + m1.m_sharedMatrix->m22*m2.m_sharedMatrix->m24 + m1.m_sharedMatrix->m23*m2.m_sharedMatrix->m34 + m1.m_sharedMatrix->m24*m2.m_sharedMatrix->m44, - - m1.m_sharedMatrix->m31*m2.m_sharedMatrix->m11 + m1.m_sharedMatrix->m32*m2.m_sharedMatrix->m21 + m1.m_sharedMatrix->m33*m2.m_sharedMatrix->m31 + m1.m_sharedMatrix->m34*m2.m_sharedMatrix->m41, - m1.m_sharedMatrix->m31*m2.m_sharedMatrix->m12 + m1.m_sharedMatrix->m32*m2.m_sharedMatrix->m22 + m1.m_sharedMatrix->m33*m2.m_sharedMatrix->m32 + m1.m_sharedMatrix->m34*m2.m_sharedMatrix->m42, - m1.m_sharedMatrix->m31*m2.m_sharedMatrix->m13 + m1.m_sharedMatrix->m32*m2.m_sharedMatrix->m23 + m1.m_sharedMatrix->m33*m2.m_sharedMatrix->m33 + m1.m_sharedMatrix->m34*m2.m_sharedMatrix->m43, - m1.m_sharedMatrix->m31*m2.m_sharedMatrix->m14 + m1.m_sharedMatrix->m32*m2.m_sharedMatrix->m24 + m1.m_sharedMatrix->m33*m2.m_sharedMatrix->m34 + m1.m_sharedMatrix->m34*m2.m_sharedMatrix->m44, - - m1.m_sharedMatrix->m41*m2.m_sharedMatrix->m11 + m1.m_sharedMatrix->m42*m2.m_sharedMatrix->m21 + m1.m_sharedMatrix->m43*m2.m_sharedMatrix->m31 + m1.m_sharedMatrix->m44*m2.m_sharedMatrix->m41, - m1.m_sharedMatrix->m41*m2.m_sharedMatrix->m12 + m1.m_sharedMatrix->m42*m2.m_sharedMatrix->m22 + m1.m_sharedMatrix->m43*m2.m_sharedMatrix->m32 + m1.m_sharedMatrix->m44*m2.m_sharedMatrix->m42, - m1.m_sharedMatrix->m41*m2.m_sharedMatrix->m13 + m1.m_sharedMatrix->m42*m2.m_sharedMatrix->m23 + m1.m_sharedMatrix->m43*m2.m_sharedMatrix->m33 + m1.m_sharedMatrix->m44*m2.m_sharedMatrix->m43, - m1.m_sharedMatrix->m41*m2.m_sharedMatrix->m14 + m1.m_sharedMatrix->m42*m2.m_sharedMatrix->m24 + m1.m_sharedMatrix->m43*m2.m_sharedMatrix->m34 + m1.m_sharedMatrix->m44*m2.m_sharedMatrix->m44); -} - -template -NzMatrix4 NzMatrix4::ConcatenateAffine(const NzMatrix4& m1, const NzMatrix4& m2) -{ - #if NAZARA_MATH_SAFE - if (!m1.IsDefined()) - { - NazaraError("First matrix not defined"); - return NzMatrix4(); - } - - if (!m2.IsDefined()) - { - NazaraError("Second matrix not defined"); - return NzMatrix4(); - } - #endif - - #ifdef NAZARA_DEBUG - if (!m1.IsAffine()) - { - NazaraError("First matrix not affine"); - return NzMatrix4(); - } - - if (!m2.IsAffine()) - { - NazaraError("Second matrix not affine"); - return NzMatrix4(); - } - #endif - - #if NAZARA_MATH_MATRIX_COLUMN_MAJOR - return NzMatrix4(m1.m_sharedMatrix->m11*m2.m_sharedMatrix->m11 + m1.m_sharedMatrix->m12*m2.m_sharedMatrix->m21 + m1.m_sharedMatrix->m13*m2.m_sharedMatrix->m31, - m1.m_sharedMatrix->m11*m2.m_sharedMatrix->m12 + m1.m_sharedMatrix->m12*m2.m_sharedMatrix->m22 + m1.m_sharedMatrix->m13*m2.m_sharedMatrix->m32, - m1.m_sharedMatrix->m11*m2.m_sharedMatrix->m13 + m1.m_sharedMatrix->m12*m2.m_sharedMatrix->m23 + m1.m_sharedMatrix->m13*m2.m_sharedMatrix->m33, - F(0.0), - - m1.m_sharedMatrix->m21*m2.m_sharedMatrix->m11 + m1.m_sharedMatrix->m22*m2.m_sharedMatrix->m21 + m1.m_sharedMatrix->m23*m2.m_sharedMatrix->m31, - m1.m_sharedMatrix->m21*m2.m_sharedMatrix->m12 + m1.m_sharedMatrix->m22*m2.m_sharedMatrix->m22 + m1.m_sharedMatrix->m23*m2.m_sharedMatrix->m32, - m1.m_sharedMatrix->m21*m2.m_sharedMatrix->m13 + m1.m_sharedMatrix->m22*m2.m_sharedMatrix->m23 + m1.m_sharedMatrix->m23*m2.m_sharedMatrix->m33, - F(0.0), - - m1.m_sharedMatrix->m31*m2.m_sharedMatrix->m11 + m1.m_sharedMatrix->m32*m2.m_sharedMatrix->m21 + m1.m_sharedMatrix->m33*m2.m_sharedMatrix->m31, - m1.m_sharedMatrix->m31*m2.m_sharedMatrix->m12 + m1.m_sharedMatrix->m32*m2.m_sharedMatrix->m22 + m1.m_sharedMatrix->m33*m2.m_sharedMatrix->m32, - m1.m_sharedMatrix->m31*m2.m_sharedMatrix->m13 + m1.m_sharedMatrix->m32*m2.m_sharedMatrix->m23 + m1.m_sharedMatrix->m33*m2.m_sharedMatrix->m33, - F(0.0), - - m1.m_sharedMatrix->m41*m2.m_sharedMatrix->m11 + m1.m_sharedMatrix->m42*m2.m_sharedMatrix->m21 + m1.m_sharedMatrix->m43*m2.m_sharedMatrix->m31 + m2.m_sharedMatrix->m41, - m1.m_sharedMatrix->m41*m2.m_sharedMatrix->m12 + m1.m_sharedMatrix->m42*m2.m_sharedMatrix->m22 + m1.m_sharedMatrix->m43*m2.m_sharedMatrix->m32 + m2.m_sharedMatrix->m42, - m1.m_sharedMatrix->m41*m2.m_sharedMatrix->m13 + m1.m_sharedMatrix->m42*m2.m_sharedMatrix->m23 + m1.m_sharedMatrix->m43*m2.m_sharedMatrix->m33 + m2.m_sharedMatrix->m43, - F(1.0)); - #else - return NzMatrix4(m1.m_sharedMatrix->m11*m2.m_sharedMatrix->m11 + m1.m_sharedMatrix->m12*m2.m_sharedMatrix->m21 + m1.m_sharedMatrix->m13*m2.m_sharedMatrix->m31, - m1.m_sharedMatrix->m11*m2.m_sharedMatrix->m12 + m1.m_sharedMatrix->m12*m2.m_sharedMatrix->m22 + m1.m_sharedMatrix->m13*m2.m_sharedMatrix->m32, - m1.m_sharedMatrix->m11*m2.m_sharedMatrix->m13 + m1.m_sharedMatrix->m12*m2.m_sharedMatrix->m23 + m1.m_sharedMatrix->m13*m2.m_sharedMatrix->m33, - m1.m_sharedMatrix->m11*m2.m_sharedMatrix->m14 + m1.m_sharedMatrix->m12*m2.m_sharedMatrix->m24 + m1.m_sharedMatrix->m13*m2.m_sharedMatrix->m34 + m1.m_sharedMatrix->m14, - - m1.m_sharedMatrix->m21*m2.m_sharedMatrix->m11 + m1.m_sharedMatrix->m22*m2.m_sharedMatrix->m21 + m1.m_sharedMatrix->m23*m2.m_sharedMatrix->m31, - m1.m_sharedMatrix->m21*m2.m_sharedMatrix->m12 + m1.m_sharedMatrix->m22*m2.m_sharedMatrix->m22 + m1.m_sharedMatrix->m23*m2.m_sharedMatrix->m32, - m1.m_sharedMatrix->m21*m2.m_sharedMatrix->m13 + m1.m_sharedMatrix->m22*m2.m_sharedMatrix->m23 + m1.m_sharedMatrix->m23*m2.m_sharedMatrix->m33, - m1.m_sharedMatrix->m21*m2.m_sharedMatrix->m14 + m1.m_sharedMatrix->m22*m2.m_sharedMatrix->m24 + m1.m_sharedMatrix->m23*m2.m_sharedMatrix->m34 + m1.m_sharedMatrix->m24, - - m1.m_sharedMatrix->m31*m2.m_sharedMatrix->m11 + m1.m_sharedMatrix->m32*m2.m_sharedMatrix->m21 + m1.m_sharedMatrix->m33*m2.m_sharedMatrix->m31, - m1.m_sharedMatrix->m31*m2.m_sharedMatrix->m12 + m1.m_sharedMatrix->m32*m2.m_sharedMatrix->m22 + m1.m_sharedMatrix->m33*m2.m_sharedMatrix->m32, - m1.m_sharedMatrix->m31*m2.m_sharedMatrix->m13 + m1.m_sharedMatrix->m32*m2.m_sharedMatrix->m23 + m1.m_sharedMatrix->m33*m2.m_sharedMatrix->m33, - m1.m_sharedMatrix->m31*m2.m_sharedMatrix->m14 + m1.m_sharedMatrix->m32*m2.m_sharedMatrix->m24 + m1.m_sharedMatrix->m33*m2.m_sharedMatrix->m34 + m1.m_sharedMatrix->m34, - - F(0.0), - F(0.0), - F(0.0), - F(1.0)); - #endif -} - -template -NzMatrix4 NzMatrix4::Identity() -{ - NzMatrix4 matrix; - matrix.MakeIdentity(); - - return matrix; -} - -template -NzMatrix4 NzMatrix4::LookAt(const NzVector3& eye, const NzVector3& center, const NzVector3& up) -{ - NzMatrix4 matrix; - matrix.MakeLookAt(eye, center, up); - - return matrix; -} - -template -NzMatrix4 NzMatrix4::Ortho(T left, T top, T width, T height, T zNear, T zFar) -{ - NzMatrix4 matrix; - matrix.MakeOrtho(left, top, width, height, zNear, zFar); - - return matrix; -} - -template -NzMatrix4 NzMatrix4::Perspective(T angle, T ratio, T zNear, T zFar) -{ - NzMatrix4 matrix; - matrix.MakePerspective(angle, ratio, zNear, zFar); - - return matrix; -} - -template -NzMatrix4 NzMatrix4::Rotate(const NzQuaternion& rotation) -{ - NzMatrix4 matrix; - matrix.MakeRotation(rotation); - - return matrix; -} - -template -NzMatrix4 NzMatrix4::Scale(const NzVector3& scale) -{ - NzMatrix4 matrix; - matrix.MakeScale(scale); - - return matrix; -} - -template -NzMatrix4 NzMatrix4::Translate(const NzVector3& translation) -{ - NzMatrix4 mat; - mat.MakeTranslation(translation); - - return mat; -} - -template -NzMatrix4 NzMatrix4::Zero() -{ - NzMatrix4 matrix; - matrix.MakeZero(); - - return matrix; -} - -template -std::ostream& operator<<(std::ostream& out, const NzMatrix4& matrix) -{ - return out << matrix.ToString(); -} - -template -NzMatrix4 operator*(T scale, const NzMatrix4& matrix) -{ - return matrix * scale; -} - -template -void NzMatrix4::EnsureOwnership() -{ - if (m_sharedMatrix) - { - NazaraLock(m_sharedMatrix->mutex); - if (m_sharedMatrix->refCount > 1) - { - m_sharedMatrix->refCount--; - - SharedMatrix* sharedMatrix = new SharedMatrix; - std::memcpy(&sharedMatrix->m11, &m_sharedMatrix->m11, 16*sizeof(T)); - } - } - else - m_sharedMatrix = new SharedMatrix; -} - -template -void NzMatrix4::ReleaseMatrix() -{ - if (!m_sharedMatrix) - return; - - NazaraMutexLock(m_sharedMatrix->mutex); - bool freeSharedMatrix = (--m_sharedMatrix->refCount == 0); - NazaraMutexUnlock(m_sharedMatrix->mutex); - - if (freeSharedMatrix) - delete m_sharedMatrix; - - m_sharedMatrix = nullptr; -} - -#undef F - -#include +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Mathematics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +///FIXME: Le MLT détecte de faux-leaks ici (Problème lié aux inline ?) + +#define F(a) static_cast(a) + +template +NzMatrix4::NzMatrix4() +{ +} + +template +NzMatrix4::NzMatrix4(T r11, T r12, T r13, T r14, + T r21, T r22, T r23, T r24, + T r31, T r32, T r33, T r34, + T r41, T r42, T r43, T r44) +{ + Set(r11, r12, r13, r14, + r21, r22, r23, r24, + r31, r32, r33, r34, + r41, r42, r43, r44); +} + +template +NzMatrix4::NzMatrix4(const T matrix[16]) +{ + Set(matrix); +} + +template +template +NzMatrix4::NzMatrix4(const NzMatrix4& matrix) +{ + Set(matrix); +} + +template +NzMatrix4::NzMatrix4(const NzMatrix4& matrix) +{ + Set(matrix); +} + +template +NzMatrix4::NzMatrix4(NzMatrix4&& matrix) noexcept +{ + Set(matrix); +} + +template +NzMatrix4::~NzMatrix4() +{ + ReleaseMatrix(); +} + +template +NzMatrix4 NzMatrix4::Concatenate(const NzMatrix4& matrix) const +{ + return Concatenate(*this, matrix); +} + +template +NzMatrix4 NzMatrix4::ConcatenateAffine(const NzMatrix4& matrix) const +{ + return ConcatenateAffine(*this, matrix); +} + +template +T NzMatrix4::GetDeterminant() const +{ + #if NAZARA_MATH_SAFE + if (!IsDefined()) + { + NazaraError("Matrix not defined"); + return F(0.0); + } + #endif + + T A = m_sharedMatrix->m22*(m_sharedMatrix->m33*m_sharedMatrix->m44 - m_sharedMatrix->m43*m_sharedMatrix->m34) - m_sharedMatrix->m32*(m_sharedMatrix->m23*m_sharedMatrix->m44 - m_sharedMatrix->m43*m_sharedMatrix->m24) + m_sharedMatrix->m42*(m_sharedMatrix->m23*m_sharedMatrix->m34 - m_sharedMatrix->m33*m_sharedMatrix->m24); + T B = m_sharedMatrix->m12*(m_sharedMatrix->m33*m_sharedMatrix->m44 - m_sharedMatrix->m43*m_sharedMatrix->m34) - m_sharedMatrix->m32*(m_sharedMatrix->m13*m_sharedMatrix->m44 - m_sharedMatrix->m43*m_sharedMatrix->m14) + m_sharedMatrix->m42*(m_sharedMatrix->m13*m_sharedMatrix->m34 - m_sharedMatrix->m33*m_sharedMatrix->m14); + T C = m_sharedMatrix->m12*(m_sharedMatrix->m23*m_sharedMatrix->m44 - m_sharedMatrix->m43*m_sharedMatrix->m24) - m_sharedMatrix->m22*(m_sharedMatrix->m13*m_sharedMatrix->m44 - m_sharedMatrix->m43*m_sharedMatrix->m14) + m_sharedMatrix->m42*(m_sharedMatrix->m13*m_sharedMatrix->m24 - m_sharedMatrix->m23*m_sharedMatrix->m14); + T D = m_sharedMatrix->m12*(m_sharedMatrix->m23*m_sharedMatrix->m34 - m_sharedMatrix->m33*m_sharedMatrix->m24) - m_sharedMatrix->m22*(m_sharedMatrix->m13*m_sharedMatrix->m34 - m_sharedMatrix->m33*m_sharedMatrix->m14) + m_sharedMatrix->m32*(m_sharedMatrix->m13*m_sharedMatrix->m24 - m_sharedMatrix->m23*m_sharedMatrix->m14); + + return m_sharedMatrix->m11*A - m_sharedMatrix->m21*B + m_sharedMatrix->m31*C - m_sharedMatrix->m41*D; +} + +template +NzMatrix4 NzMatrix4::GetInverse() const +{ + #if NAZARA_MATH_SAFE + if (!IsDefined()) + { + NazaraError("Matrix not defined"); + return NzMatrix4(); + } + #endif + + T det = GetDeterminant(); + if (!NzNumberEquals(det, F(0.0))) + { + return NzMatrix4((m_sharedMatrix->m22*(m_sharedMatrix->m33*m_sharedMatrix->m44 - m_sharedMatrix->m34*m_sharedMatrix->m43) - m_sharedMatrix->m32*(m_sharedMatrix->m23*m_sharedMatrix->m44 - m_sharedMatrix->m43*m_sharedMatrix->m24) + m_sharedMatrix->m42*(m_sharedMatrix->m23*m_sharedMatrix->m34 - m_sharedMatrix->m33*m_sharedMatrix->m24)) / det, + -(m_sharedMatrix->m12*(m_sharedMatrix->m33*m_sharedMatrix->m44 - m_sharedMatrix->m43*m_sharedMatrix->m34) - m_sharedMatrix->m32*(m_sharedMatrix->m13*m_sharedMatrix->m44 - m_sharedMatrix->m43*m_sharedMatrix->m14) + m_sharedMatrix->m42*(m_sharedMatrix->m13*m_sharedMatrix->m34 - m_sharedMatrix->m33*m_sharedMatrix->m14)) / det, + (m_sharedMatrix->m12*(m_sharedMatrix->m23*m_sharedMatrix->m44 - m_sharedMatrix->m43*m_sharedMatrix->m24) - m_sharedMatrix->m22*(m_sharedMatrix->m13*m_sharedMatrix->m44 - m_sharedMatrix->m43*m_sharedMatrix->m14) + m_sharedMatrix->m42*(m_sharedMatrix->m13*m_sharedMatrix->m24 - m_sharedMatrix->m23*m_sharedMatrix->m14)) / det, + -(m_sharedMatrix->m12*(m_sharedMatrix->m23*m_sharedMatrix->m34 - m_sharedMatrix->m33*m_sharedMatrix->m24) - m_sharedMatrix->m22*(m_sharedMatrix->m13*m_sharedMatrix->m34 - m_sharedMatrix->m33*m_sharedMatrix->m14) + m_sharedMatrix->m32*(m_sharedMatrix->m13*m_sharedMatrix->m24 - m_sharedMatrix->m23*m_sharedMatrix->m14)) / det, + + -(m_sharedMatrix->m21*(m_sharedMatrix->m33*m_sharedMatrix->m44 - m_sharedMatrix->m34*m_sharedMatrix->m43) - m_sharedMatrix->m23*(m_sharedMatrix->m31*m_sharedMatrix->m44 - m_sharedMatrix->m34*m_sharedMatrix->m41) + m_sharedMatrix->m24*(m_sharedMatrix->m31*m_sharedMatrix->m43 - m_sharedMatrix->m33*m_sharedMatrix->m41)) / det, + (m_sharedMatrix->m11*(m_sharedMatrix->m33*m_sharedMatrix->m44 - m_sharedMatrix->m34*m_sharedMatrix->m43) - m_sharedMatrix->m13*(m_sharedMatrix->m31*m_sharedMatrix->m44 - m_sharedMatrix->m34*m_sharedMatrix->m41) + m_sharedMatrix->m14*(m_sharedMatrix->m31*m_sharedMatrix->m43 - m_sharedMatrix->m33*m_sharedMatrix->m41)) / det, + -(m_sharedMatrix->m11*(m_sharedMatrix->m23*m_sharedMatrix->m44 - m_sharedMatrix->m24*m_sharedMatrix->m43) - m_sharedMatrix->m13*(m_sharedMatrix->m21*m_sharedMatrix->m44 - m_sharedMatrix->m24*m_sharedMatrix->m41) + m_sharedMatrix->m14*(m_sharedMatrix->m21*m_sharedMatrix->m43 - m_sharedMatrix->m23*m_sharedMatrix->m41)) / det, + (m_sharedMatrix->m11*(m_sharedMatrix->m23*m_sharedMatrix->m34 - m_sharedMatrix->m24*m_sharedMatrix->m33) - m_sharedMatrix->m13*(m_sharedMatrix->m21*m_sharedMatrix->m34 - m_sharedMatrix->m24*m_sharedMatrix->m31) + m_sharedMatrix->m14*(m_sharedMatrix->m21*m_sharedMatrix->m33 - m_sharedMatrix->m23*m_sharedMatrix->m31)) / det, + + (m_sharedMatrix->m21*(m_sharedMatrix->m32*m_sharedMatrix->m44 - m_sharedMatrix->m34*m_sharedMatrix->m42) - m_sharedMatrix->m22*(m_sharedMatrix->m31*m_sharedMatrix->m44 - m_sharedMatrix->m34*m_sharedMatrix->m41) + m_sharedMatrix->m24*(m_sharedMatrix->m31*m_sharedMatrix->m42 - m_sharedMatrix->m32*m_sharedMatrix->m41)) / det, + -(m_sharedMatrix->m11*(m_sharedMatrix->m32*m_sharedMatrix->m44 - m_sharedMatrix->m34*m_sharedMatrix->m42) - m_sharedMatrix->m12*(m_sharedMatrix->m31*m_sharedMatrix->m44 - m_sharedMatrix->m34*m_sharedMatrix->m41) + m_sharedMatrix->m14*(m_sharedMatrix->m31*m_sharedMatrix->m42 - m_sharedMatrix->m32*m_sharedMatrix->m41)) / det, + (m_sharedMatrix->m11*(m_sharedMatrix->m22*m_sharedMatrix->m44 - m_sharedMatrix->m24*m_sharedMatrix->m42) - m_sharedMatrix->m12*(m_sharedMatrix->m21*m_sharedMatrix->m44 - m_sharedMatrix->m24*m_sharedMatrix->m41) + m_sharedMatrix->m14*(m_sharedMatrix->m21*m_sharedMatrix->m42 - m_sharedMatrix->m22*m_sharedMatrix->m41)) / det, + -(m_sharedMatrix->m11*(m_sharedMatrix->m22*m_sharedMatrix->m34 - m_sharedMatrix->m24*m_sharedMatrix->m32) - m_sharedMatrix->m12*(m_sharedMatrix->m21*m_sharedMatrix->m34 - m_sharedMatrix->m24*m_sharedMatrix->m31) + m_sharedMatrix->m14*(m_sharedMatrix->m21*m_sharedMatrix->m32 - m_sharedMatrix->m22*m_sharedMatrix->m31)) / det, + + -(m_sharedMatrix->m21*(m_sharedMatrix->m32*m_sharedMatrix->m43 - m_sharedMatrix->m33*m_sharedMatrix->m42) - m_sharedMatrix->m22*(m_sharedMatrix->m31*m_sharedMatrix->m43 - m_sharedMatrix->m33*m_sharedMatrix->m41) + m_sharedMatrix->m23*(m_sharedMatrix->m31*m_sharedMatrix->m42 - m_sharedMatrix->m32*m_sharedMatrix->m41)) / det, + (m_sharedMatrix->m11*(m_sharedMatrix->m32*m_sharedMatrix->m43 - m_sharedMatrix->m33*m_sharedMatrix->m42) - m_sharedMatrix->m12*(m_sharedMatrix->m31*m_sharedMatrix->m43 - m_sharedMatrix->m33*m_sharedMatrix->m41) + m_sharedMatrix->m13*(m_sharedMatrix->m31*m_sharedMatrix->m42 - m_sharedMatrix->m32*m_sharedMatrix->m41)) / det, + -(m_sharedMatrix->m11*(m_sharedMatrix->m22*m_sharedMatrix->m43 - m_sharedMatrix->m23*m_sharedMatrix->m42) - m_sharedMatrix->m12*(m_sharedMatrix->m21*m_sharedMatrix->m43 - m_sharedMatrix->m23*m_sharedMatrix->m41) + m_sharedMatrix->m13*(m_sharedMatrix->m21*m_sharedMatrix->m42 - m_sharedMatrix->m22*m_sharedMatrix->m41)) / det, + (m_sharedMatrix->m11*(m_sharedMatrix->m22*m_sharedMatrix->m33 - m_sharedMatrix->m23*m_sharedMatrix->m32) - m_sharedMatrix->m12*(m_sharedMatrix->m21*m_sharedMatrix->m33 - m_sharedMatrix->m23*m_sharedMatrix->m31) + m_sharedMatrix->m13*(m_sharedMatrix->m21*m_sharedMatrix->m32 - m_sharedMatrix->m22*m_sharedMatrix->m31)) / det); + } + else + { + NazaraError("Matrix has no inverse"); + + return Identity(); + } +} + +template +NzVector3 NzMatrix4::GetScale() const +{ + #if NAZARA_MATH_SAFE + if (!IsDefined()) + { + NazaraError("Matrix not defined"); + return NzVector3(); + } + #endif + + return NzVector3(std::sqrt(m_sharedMatrix->m11*m_sharedMatrix->m11 + m_sharedMatrix->m21*m_sharedMatrix->m21 + m_sharedMatrix->m31*m_sharedMatrix->m31), + std::sqrt(m_sharedMatrix->m12*m_sharedMatrix->m12 + m_sharedMatrix->m22*m_sharedMatrix->m22 + m_sharedMatrix->m32*m_sharedMatrix->m32), + std::sqrt(m_sharedMatrix->m13*m_sharedMatrix->m13 + m_sharedMatrix->m23*m_sharedMatrix->m23 + m_sharedMatrix->m33*m_sharedMatrix->m33)); +} + +template +NzVector3 NzMatrix4::GetTranslation() const +{ + #if NAZARA_MATH_SAFE + if (!IsDefined()) + { + NazaraError("Matrix not defined"); + return NzVector3(); + } + #endif + + return NzVector3(m_sharedMatrix->m41, m_sharedMatrix->m42, m_sharedMatrix->m43); +} + +template +NzMatrix4 NzMatrix4::GetTransposed() const +{ + #if NAZARA_MATH_SAFE + if (!IsDefined()) + { + NazaraError("Matrix not defined"); + return NzMatrix4(); + } + #endif + + return NzMatrix4(m_sharedMatrix->m11, m_sharedMatrix->m21, m_sharedMatrix->m31, m_sharedMatrix->m41, + m_sharedMatrix->m12, m_sharedMatrix->m22, m_sharedMatrix->m32, m_sharedMatrix->m42, + m_sharedMatrix->m13, m_sharedMatrix->m23, m_sharedMatrix->m33, m_sharedMatrix->m43, + m_sharedMatrix->m14, m_sharedMatrix->m24, m_sharedMatrix->m34, m_sharedMatrix->m44); +} + +template +bool NzMatrix4::HasNegativeScale() const +{ + return GetDeterminant() < F(0.0); +} + +template +bool NzMatrix4::HasScale() const +{ + #if NAZARA_MATH_SAFE + if (!IsDefined()) + { + NazaraError("Matrix not defined"); + return false; + } + #endif + + T t = m_sharedMatrix->m11*m_sharedMatrix->m11 + m_sharedMatrix->m21*m_sharedMatrix->m21 + m_sharedMatrix->m31*m_sharedMatrix->m31; + if (!NzNumberEquals(t, F(1.0))) + return true; + + t = m_sharedMatrix->m12*m_sharedMatrix->m12 + m_sharedMatrix->m22*m_sharedMatrix->m22 + m_sharedMatrix->m32*m_sharedMatrix->m32; + if (!NzNumberEquals(t, F(1.0))) + return true; + + t = m_sharedMatrix->m13*m_sharedMatrix->m13 + m_sharedMatrix->m23*m_sharedMatrix->m23 + m_sharedMatrix->m33*m_sharedMatrix->m33; + if (!NzNumberEquals(t, F(1.0))) + return true; + + return false; +} + +template +bool NzMatrix4::IsAffine() const +{ + #if NAZARA_MATH_SAFE + if (!IsDefined()) + { + NazaraError("Matrix not defined"); + return false; + } + #endif + + return NzNumberEquals(m_sharedMatrix->m14, F(0.0)) && + NzNumberEquals(m_sharedMatrix->m24, F(0.0)) && + NzNumberEquals(m_sharedMatrix->m34, F(0.0)) && + NzNumberEquals(m_sharedMatrix->m44, F(1.0)); +} + +template +bool NzMatrix4::IsDefined() const +{ + return m_sharedMatrix != nullptr; +} + +template +void NzMatrix4::MakeIdentity() +{ + Set(F(1.0), F(0.0), F(0.0), F(0.0), + F(0.0), F(1.0), F(0.0), F(0.0), + F(0.0), F(0.0), F(1.0), F(0.0), + F(0.0), F(0.0), F(0.0), F(1.0)); +} + +template +void NzMatrix4::MakeOrtho(T left, T top, T width, T height, T zNear, T zFar) +{ + // http://msdn.microsoft.com/en-us/library/windows/desktop/bb204941(v=vs.85).aspx + Set(F(2.0)/(width-left), F(0.0), F(0.0), -(width+left)/(width-left), + F(0.0), F(2.0)/(top-height), F(0.0), -(top+height)/(top-height), + F(0.0), F(0.0), F(-2.0)/(zFar-zNear), -(zFar+zNear)/(zFar-zNear), + F(0.0), F(0.0), F(0.0), F(1.0)); +} + +template +void NzMatrix4::MakeLookAt(const NzVector3& eye, const NzVector3& target, const NzVector3& up) +{ + NzVector3 f = NzVector3::Normalize(target - eye); + NzVector3 u(up.GetNormal()); + NzVector3 s = NzVector3::Normalize(f.CrossProduct(u)); + u = s.CrossProduct(f); + + Set(s.x, u.x, -f.x, T(0.0), + s.y, u.y, -f.y, T(0.0), + s.z, u.z, -f.z, T(0.0), + -s.DotProduct(eye), -u.DotProduct(eye), f.DotProduct(eye), T(1.0)); +} + +template +void NzMatrix4::MakePerspective(T angle, T ratio, T zNear, T zFar) +{ + // http://msdn.microsoft.com/en-us/library/windows/desktop/bb204944(v=vs.85).aspx + #if NAZARA_MATH_ANGLE_RADIAN + angle /= F(2.0); + #else + angle = NzDegreeToRadian(angle/F(2.0)); + #endif + + T yScale = F(1.0) / std::tan(angle); + + Set(yScale / ratio, F(0.0), F(0.0), F(0.0), + F(0.0), yScale, F(0.0), F(0.0), + F(0.0), F(0.0), zFar / (zNear-zFar), F(-1.0), + F(0.0), F(0.0), (zNear*zFar) / (zNear-zFar), F(0.0)); +} + +template +void NzMatrix4::MakeRotation(const NzQuaternion& rotation) +{ + // http://www.flipcode.com/documents/matrfaq.html#Q54 +/* + | 2 2 | + | 1 - 2Y - 2Z 2XY + 2ZW 2XZ - 2YW | + | | + | 2 2 | + M = | 2XY - 2ZW 1 - 2X - 2Z 2YZ + 2XW | + | | + | 2 2 | + | 2XZ + 2YW 2YZ - 2XW 1 - 2X - 2Y | + | | +*/ + ///FIXME: À corriger (Rotation quaternino != rotation matricielle) + Set(F(1.0) - F(2.0)*rotation.y*rotation.y - F(2.0)*rotation.z*rotation.z, + F(2.0)*rotation.x*rotation.y + F(2.0)*rotation.z*rotation.w, + F(2.0)*rotation.x*rotation.z - F(2.0)*rotation.y*rotation.w, + F(0.0), + + F(2.0)*rotation.x*rotation.y - F(2.0)*rotation.z*rotation.w, + F(1.0) - F(2.0)*rotation.x*rotation.x - F(2.0)*rotation.z*rotation.z, + F(2.0)*rotation.y*rotation.z + F(2.0)*rotation.x*rotation.w, + F(0.0), + + F(2.0)*rotation.x*rotation.z + F(2.0)*rotation.y*rotation.w, + F(2.0)*rotation.y*rotation.z - F(2.0)*rotation.x*rotation.w, + F(1.0) - F(2.0)*rotation.x*rotation.x - F(2.0)*rotation.y*rotation.y, + F(0.0), + + F(0.0), + F(0.0), + F(0.0), + F(1.0)); +} + +template +void NzMatrix4::MakeScale(const NzVector3& scale) +{ + Set(scale.x, F(0.0), F(0.0), F(0.0), + F(0.0), scale.y, F(0.0), F(0.0), + F(0.0), F(0.0), scale.z, F(0.0), + F(0.0), F(0.0), F(0.0), F(1.0)); +} + +template +void NzMatrix4::MakeTranslation(const NzVector3& translation) +{ + Set(F(1.0), F(0.0), F(0.0), F(0.0), + F(0.0), F(1.0), F(0.0), F(0.0), + F(0.0), F(0.0), F(1.0), F(0.0), + translation.x, translation.y, translation.z, F(1.0)); +} + +template +void NzMatrix4::MakeZero() +{ + Set(F(0.0), F(0.0), F(0.0), F(0.0), + F(0.0), F(0.0), F(0.0), F(0.0), + F(0.0), F(0.0), F(0.0), F(0.0), + F(0.0), F(0.0), F(0.0), F(0.0)); +} + +template +void NzMatrix4::Set(T r11, T r12, T r13, T r14, + T r21, T r22, T r23, T r24, + T r31, T r32, T r33, T r34, + T r41, T r42, T r43, T r44) +{ + EnsureOwnership(); + + m_sharedMatrix->m11 = r11; + m_sharedMatrix->m12 = r12; + m_sharedMatrix->m13 = r13; + m_sharedMatrix->m14 = r14; + m_sharedMatrix->m21 = r21; + m_sharedMatrix->m22 = r22; + m_sharedMatrix->m23 = r23; + m_sharedMatrix->m24 = r24; + m_sharedMatrix->m31 = r31; + m_sharedMatrix->m32 = r32; + m_sharedMatrix->m33 = r33; + m_sharedMatrix->m34 = r34; + m_sharedMatrix->m41 = r41; + m_sharedMatrix->m42 = r42; + m_sharedMatrix->m43 = r43; + m_sharedMatrix->m44 = r44; +} + +template +void NzMatrix4::Set(const T matrix[16]) +{ + EnsureOwnership(); + + // Ici nous sommes certains de la continuité des éléments en mémoire + std::memcpy(&m_sharedMatrix->m11, matrix, 16*sizeof(T)); +} + +template +void NzMatrix4::Set(const NzMatrix4& matrix) +{ + ReleaseMatrix(); + + m_sharedMatrix = matrix.m_sharedMatrix; + if (m_sharedMatrix) + { + NazaraMutexLock(m_sharedMatrix->mutex); + m_sharedMatrix->refCount++; + NazaraMutexUnlock(m_sharedMatrix->mutex); + } +} + +template +void NzMatrix4::Set(NzMatrix4&& matrix) +{ + std::swap(m_sharedMatrix, matrix.m_sharedMatrix); +} + +template +template +void NzMatrix4::Set(const NzMatrix4& matrix) +{ + Set(F(matrix.m_sharedMatrix->m11), F(matrix.m_sharedMatrix->m12), F(matrix.m_sharedMatrix->m13), F(matrix.m_sharedMatrix->m14), + F(matrix.m_sharedMatrix->m21), F(matrix.m_sharedMatrix->m22), F(matrix.m_sharedMatrix->m23), F(matrix.m_sharedMatrix->m24), + F(matrix.m_sharedMatrix->m31), F(matrix.m_sharedMatrix->m32), F(matrix.m_sharedMatrix->m33), F(matrix.m_sharedMatrix->m34), + F(matrix.m_sharedMatrix->m41), F(matrix.m_sharedMatrix->m42), F(matrix.m_sharedMatrix->m43), F(matrix.m_sharedMatrix->m44)); +} + +template +void NzMatrix4::SetRotation(const NzQuaternion& rotation) +{ + // http://www.flipcode.com/documents/matrfaq.html#Q54 + EnsureOwnership(); + + m_sharedMatrix->m11 = F(1.0) - F(2.0)*rotation.y*rotation.y - F(2.0)*rotation.z*rotation.z; + m_sharedMatrix->m22 = F(1.0) - F(2.0)*rotation.x*rotation.x - F(2.0)*rotation.z*rotation.z; + m_sharedMatrix->m33 = F(1.0) - F(2.0)*rotation.x*rotation.x - F(2.0)*rotation.y*rotation.y; + + m_sharedMatrix->m12 = F(2.0)*rotation.x*rotation.y - F(2.0)*rotation.z*rotation.w; + m_sharedMatrix->m13 = F(2.0)*rotation.x*rotation.z + F(2.0)*rotation.y*rotation.w; + m_sharedMatrix->m21 = F(2.0)*rotation.x*rotation.y + F(2.0)*rotation.z*rotation.w; + m_sharedMatrix->m23 = F(2.0)*rotation.y*rotation.z - F(2.0)*rotation.x*rotation.w; + m_sharedMatrix->m31 = F(2.0)*rotation.x*rotation.z - F(2.0)*rotation.y*rotation.w; + m_sharedMatrix->m32 = F(2.0)*rotation.y*rotation.z + F(2.0)*rotation.x*rotation.w; +} + +template +void NzMatrix4::SetScale(const NzVector3& scale) +{ + EnsureOwnership(); + + m_sharedMatrix->m11 = scale.x; + m_sharedMatrix->m22 = scale.y; + m_sharedMatrix->m33 = scale.z; +} + +template +void NzMatrix4::SetTranslation(const NzVector3& translation) +{ + EnsureOwnership(); + + m_sharedMatrix->m41 = translation.x; + m_sharedMatrix->m42 = translation.y; + m_sharedMatrix->m43 = translation.z; + m_sharedMatrix->m44 = F(1.0); +} + +template +NzString NzMatrix4::ToString() const +{ + if (!m_sharedMatrix) + return "Matrix4(undefined)"; + + NzStringStream ss; + return ss << "Matrix4(" << m_sharedMatrix->m11 << ", " << m_sharedMatrix->m12 << ", " << m_sharedMatrix->m13 << ", " << m_sharedMatrix->m14 << ",\n" + << " " << m_sharedMatrix->m21 << ", " << m_sharedMatrix->m22 << ", " << m_sharedMatrix->m23 << ", " << m_sharedMatrix->m24 << ",\n" + << " " << m_sharedMatrix->m31 << ", " << m_sharedMatrix->m32 << ", " << m_sharedMatrix->m33 << ", " << m_sharedMatrix->m34 << ",\n" + << " " << m_sharedMatrix->m41 << ", " << m_sharedMatrix->m42 << ", " << m_sharedMatrix->m43 << ", " << m_sharedMatrix->m44 << ')'; +} + +template +NzVector2 NzMatrix4::Transform(const NzVector2& vector, T z, T w) const +{ + #if NAZARA_MATH_SAFE + if (!m_sharedMatrix) + { + NazaraError("Matrix not defined"); + return vector; + } + #endif + + return NzVector2(m_sharedMatrix->m11*vector.x + m_sharedMatrix->m12*vector.y + m_sharedMatrix->m13*z + m_sharedMatrix->m14*w, + m_sharedMatrix->m21*vector.x + m_sharedMatrix->m22*vector.y + m_sharedMatrix->m23*z + m_sharedMatrix->m24*w); +} + +template +NzVector3 NzMatrix4::Transform(const NzVector3& vector, T w) const +{ + #if NAZARA_MATH_SAFE + if (!m_sharedMatrix) + { + NazaraError("Matrix not defined"); + return vector; + } + #endif + + return NzVector3(m_sharedMatrix->m11*vector.x + m_sharedMatrix->m12*vector.y + m_sharedMatrix->m13*vector.z + m_sharedMatrix->m14*w, + m_sharedMatrix->m21*vector.x + m_sharedMatrix->m22*vector.y + m_sharedMatrix->m23*vector.z + m_sharedMatrix->m24*w, + m_sharedMatrix->m31*vector.x + m_sharedMatrix->m32*vector.y + m_sharedMatrix->m33*vector.z + m_sharedMatrix->m34*w); +} + +template +NzVector4 NzMatrix4::Transform(const NzVector4& vector) const +{ + #if NAZARA_MATH_SAFE + if (!m_sharedMatrix) + { + NazaraError("Matrix not defined"); + return vector; + } + #endif + + return NzVector4(m_sharedMatrix->m11*vector.x + m_sharedMatrix->m12*vector.y + m_sharedMatrix->m13*vector.z + m_sharedMatrix->m14*vector.w, + m_sharedMatrix->m21*vector.x + m_sharedMatrix->m22*vector.y + m_sharedMatrix->m23*vector.z + m_sharedMatrix->m24*vector.w, + m_sharedMatrix->m31*vector.x + m_sharedMatrix->m32*vector.y + m_sharedMatrix->m33*vector.z + m_sharedMatrix->m34*vector.w, + m_sharedMatrix->m41*vector.x + m_sharedMatrix->m42*vector.y + m_sharedMatrix->m43*vector.z + m_sharedMatrix->m44*vector.w); +} + +template +NzMatrix4& NzMatrix4::Transpose() +{ + #if NAZARA_MATH_SAFE + if (!m_sharedMatrix) + { + NazaraError("Matrix not defined"); + return *this; + } + #endif + + std::swap(m_sharedMatrix->m12, m_sharedMatrix->m21); + std::swap(m_sharedMatrix->m13, m_sharedMatrix->m31); + std::swap(m_sharedMatrix->m14, m_sharedMatrix->m41); + std::swap(m_sharedMatrix->m23, m_sharedMatrix->m32); + std::swap(m_sharedMatrix->m24, m_sharedMatrix->m42); + std::swap(m_sharedMatrix->m34, m_sharedMatrix->m43); + + return *this; +} + +template +NzMatrix4::operator NzString() const +{ + return ToString(); +} + +template +NzMatrix4::operator T*() +{ + #if NAZARA_MATH_SAFE + if (!m_sharedMatrix) + { + NazaraError("Matrix not defined"); + return nullptr; + } + #endif + + EnsureOwnership(); + + return &m_sharedMatrix->m11; +} + +template +NzMatrix4::operator const T*() const +{ + #if NAZARA_MATH_SAFE + if (!m_sharedMatrix) + { + NazaraError("Matrix not defined"); + return nullptr; + } + #endif + + return &m_sharedMatrix->m11; +} + +template +T& NzMatrix4::operator()(unsigned int x, unsigned int y) +{ + #if NAZARA_MATH_SAFE + if (x > 3 || y > 3) + { + NzStringStream ss; + ss << __FILE__ << ':' << __LINE__ << ": Index out of range: (" << x << ", " << y << ") > (3,3)"; + + throw std::out_of_range(ss.ToString()); + } + #endif + + EnsureOwnership(); + + return (&m_sharedMatrix->m11)[y*4+x]; +} + +template +const T& NzMatrix4::operator()(unsigned int x, unsigned int y) const +{ + #if NAZARA_MATH_SAFE + if (!m_sharedMatrix) + { + NazaraError("Matrix not defined"); + throw std::runtime_error("Tried to access element of Matrix not defined"); + } + + if (x > 3 || y > 3) + { + NzStringStream ss; + ss << __FILE__ << ':' << __LINE__ << ": Index out of range: (" << x << ", " << y << ") > (3,3)"; + + throw std::out_of_range(ss.ToString()); + } + #endif + + return (&m_sharedMatrix->m11)[y*4+x]; +} + +template +NzMatrix4& NzMatrix4::operator=(const NzMatrix4& matrix) +{ + Set(matrix); + + return *this; +} + +template +NzMatrix4& NzMatrix4::operator=(NzMatrix4&& matrix) noexcept +{ + Set(matrix); + + return *this; +} + +template +NzMatrix4 NzMatrix4::operator*(const NzMatrix4& matrix) const +{ + return Concatenate(matrix); +} + +template +NzVector2 NzMatrix4::operator*(const NzVector2& vector) const +{ + return Transform(vector); +} + +template +NzVector3 NzMatrix4::operator*(const NzVector3& vector) const +{ + return Transform(vector); +} + +template +NzVector4 NzMatrix4::operator*(const NzVector4& vector) const +{ + return Transform(vector); +} + +template +NzMatrix4 NzMatrix4::operator*(T scalar) const +{ + #if NAZARA_MATH_SAFE + if (!m_sharedMatrix) + { + NazaraError("Matrix not defined"); + return *this; + } + #endif + + return NzMatrix4(m_sharedMatrix->m11 * scalar, m_sharedMatrix->m12 * scalar, m_sharedMatrix->m13 * scalar, m_sharedMatrix->m14 * scalar, + m_sharedMatrix->m21 * scalar, m_sharedMatrix->m22 * scalar, m_sharedMatrix->m23 * scalar, m_sharedMatrix->m24 * scalar, + m_sharedMatrix->m31 * scalar, m_sharedMatrix->m32 * scalar, m_sharedMatrix->m33 * scalar, m_sharedMatrix->m34 * scalar, + m_sharedMatrix->m41 * scalar, m_sharedMatrix->m42 * scalar, m_sharedMatrix->m43 * scalar, m_sharedMatrix->m44 * scalar); +} + +template +NzMatrix4& NzMatrix4::operator*=(const NzMatrix4& matrix) +{ + #if NAZARA_MATH_SAFE + if (!m_sharedMatrix) + { + NazaraError("Matrix not defined"); + return *this; + } + #endif + + Set(Concatenate(*this, matrix)); + + return *this; +} + +template +NzMatrix4& NzMatrix4::operator*=(T scalar) +{ + #if NAZARA_MATH_SAFE + if (!m_sharedMatrix) + { + NazaraError("Matrix not defined"); + return *this; + } + #endif + + m_sharedMatrix->m11 *= scalar; + m_sharedMatrix->m12 *= scalar; + m_sharedMatrix->m13 *= scalar; + m_sharedMatrix->m14 *= scalar; + m_sharedMatrix->m21 *= scalar; + m_sharedMatrix->m22 *= scalar; + m_sharedMatrix->m23 *= scalar; + m_sharedMatrix->m24 *= scalar; + m_sharedMatrix->m31 *= scalar; + m_sharedMatrix->m32 *= scalar; + m_sharedMatrix->m33 *= scalar; + m_sharedMatrix->m34 *= scalar; + m_sharedMatrix->m41 *= scalar; + m_sharedMatrix->m42 *= scalar; + m_sharedMatrix->m43 *= scalar; + m_sharedMatrix->m44 *= scalar; + + return *this; +} + +template +NzMatrix4 NzMatrix4::Concatenate(const NzMatrix4& m1, const NzMatrix4& m2) +{ + #if NAZARA_MATH_SAFE + if (!m1.IsDefined()) + { + NazaraError("First matrix not defined"); + return NzMatrix4(); + } + + if (!m2.IsDefined()) + { + NazaraError("Second matrix not defined"); + return NzMatrix4(); + } + #endif + + #if NAZARA_MATH_MATRIX4_CHECK_AFFINE + if (m1.IsAffine() && m2.IsAffine()) + return ConcatenateAffine(m1, m2); + #endif + + return NzMatrix4(m1.m_sharedMatrix->m11*m2.m_sharedMatrix->m11 + m1.m_sharedMatrix->m12*m2.m_sharedMatrix->m21 + m1.m_sharedMatrix->m13*m2.m_sharedMatrix->m31 + m1.m_sharedMatrix->m14*m2.m_sharedMatrix->m41, + m1.m_sharedMatrix->m11*m2.m_sharedMatrix->m12 + m1.m_sharedMatrix->m12*m2.m_sharedMatrix->m22 + m1.m_sharedMatrix->m13*m2.m_sharedMatrix->m32 + m1.m_sharedMatrix->m14*m2.m_sharedMatrix->m42, + m1.m_sharedMatrix->m11*m2.m_sharedMatrix->m13 + m1.m_sharedMatrix->m12*m2.m_sharedMatrix->m23 + m1.m_sharedMatrix->m13*m2.m_sharedMatrix->m33 + m1.m_sharedMatrix->m14*m2.m_sharedMatrix->m43, + m1.m_sharedMatrix->m11*m2.m_sharedMatrix->m14 + m1.m_sharedMatrix->m12*m2.m_sharedMatrix->m24 + m1.m_sharedMatrix->m13*m2.m_sharedMatrix->m34 + m1.m_sharedMatrix->m14*m2.m_sharedMatrix->m44, + + m1.m_sharedMatrix->m21*m2.m_sharedMatrix->m11 + m1.m_sharedMatrix->m22*m2.m_sharedMatrix->m21 + m1.m_sharedMatrix->m23*m2.m_sharedMatrix->m31 + m1.m_sharedMatrix->m24*m2.m_sharedMatrix->m41, + m1.m_sharedMatrix->m21*m2.m_sharedMatrix->m12 + m1.m_sharedMatrix->m22*m2.m_sharedMatrix->m22 + m1.m_sharedMatrix->m23*m2.m_sharedMatrix->m32 + m1.m_sharedMatrix->m24*m2.m_sharedMatrix->m42, + m1.m_sharedMatrix->m21*m2.m_sharedMatrix->m13 + m1.m_sharedMatrix->m22*m2.m_sharedMatrix->m23 + m1.m_sharedMatrix->m23*m2.m_sharedMatrix->m33 + m1.m_sharedMatrix->m24*m2.m_sharedMatrix->m43, + m1.m_sharedMatrix->m21*m2.m_sharedMatrix->m14 + m1.m_sharedMatrix->m22*m2.m_sharedMatrix->m24 + m1.m_sharedMatrix->m23*m2.m_sharedMatrix->m34 + m1.m_sharedMatrix->m24*m2.m_sharedMatrix->m44, + + m1.m_sharedMatrix->m31*m2.m_sharedMatrix->m11 + m1.m_sharedMatrix->m32*m2.m_sharedMatrix->m21 + m1.m_sharedMatrix->m33*m2.m_sharedMatrix->m31 + m1.m_sharedMatrix->m34*m2.m_sharedMatrix->m41, + m1.m_sharedMatrix->m31*m2.m_sharedMatrix->m12 + m1.m_sharedMatrix->m32*m2.m_sharedMatrix->m22 + m1.m_sharedMatrix->m33*m2.m_sharedMatrix->m32 + m1.m_sharedMatrix->m34*m2.m_sharedMatrix->m42, + m1.m_sharedMatrix->m31*m2.m_sharedMatrix->m13 + m1.m_sharedMatrix->m32*m2.m_sharedMatrix->m23 + m1.m_sharedMatrix->m33*m2.m_sharedMatrix->m33 + m1.m_sharedMatrix->m34*m2.m_sharedMatrix->m43, + m1.m_sharedMatrix->m31*m2.m_sharedMatrix->m14 + m1.m_sharedMatrix->m32*m2.m_sharedMatrix->m24 + m1.m_sharedMatrix->m33*m2.m_sharedMatrix->m34 + m1.m_sharedMatrix->m34*m2.m_sharedMatrix->m44, + + m1.m_sharedMatrix->m41*m2.m_sharedMatrix->m11 + m1.m_sharedMatrix->m42*m2.m_sharedMatrix->m21 + m1.m_sharedMatrix->m43*m2.m_sharedMatrix->m31 + m1.m_sharedMatrix->m44*m2.m_sharedMatrix->m41, + m1.m_sharedMatrix->m41*m2.m_sharedMatrix->m12 + m1.m_sharedMatrix->m42*m2.m_sharedMatrix->m22 + m1.m_sharedMatrix->m43*m2.m_sharedMatrix->m32 + m1.m_sharedMatrix->m44*m2.m_sharedMatrix->m42, + m1.m_sharedMatrix->m41*m2.m_sharedMatrix->m13 + m1.m_sharedMatrix->m42*m2.m_sharedMatrix->m23 + m1.m_sharedMatrix->m43*m2.m_sharedMatrix->m33 + m1.m_sharedMatrix->m44*m2.m_sharedMatrix->m43, + m1.m_sharedMatrix->m41*m2.m_sharedMatrix->m14 + m1.m_sharedMatrix->m42*m2.m_sharedMatrix->m24 + m1.m_sharedMatrix->m43*m2.m_sharedMatrix->m34 + m1.m_sharedMatrix->m44*m2.m_sharedMatrix->m44); +} + +template +NzMatrix4 NzMatrix4::ConcatenateAffine(const NzMatrix4& m1, const NzMatrix4& m2) +{ + #if NAZARA_MATH_SAFE + if (!m1.IsDefined()) + { + NazaraError("First matrix not defined"); + return NzMatrix4(); + } + + if (!m2.IsDefined()) + { + NazaraError("Second matrix not defined"); + return NzMatrix4(); + } + #endif + + #ifdef NAZARA_DEBUG + if (!m1.IsAffine()) + { + NazaraError("First matrix not affine"); + return NzMatrix4(); + } + + if (!m2.IsAffine()) + { + NazaraError("Second matrix not affine"); + return NzMatrix4(); + } + #endif + + return NzMatrix4(m1.m_sharedMatrix->m11*m2.m_sharedMatrix->m11 + m1.m_sharedMatrix->m12*m2.m_sharedMatrix->m21 + m1.m_sharedMatrix->m13*m2.m_sharedMatrix->m31, + m1.m_sharedMatrix->m11*m2.m_sharedMatrix->m12 + m1.m_sharedMatrix->m12*m2.m_sharedMatrix->m22 + m1.m_sharedMatrix->m13*m2.m_sharedMatrix->m32, + m1.m_sharedMatrix->m11*m2.m_sharedMatrix->m13 + m1.m_sharedMatrix->m12*m2.m_sharedMatrix->m23 + m1.m_sharedMatrix->m13*m2.m_sharedMatrix->m33, + F(0.0), + + m1.m_sharedMatrix->m21*m2.m_sharedMatrix->m11 + m1.m_sharedMatrix->m22*m2.m_sharedMatrix->m21 + m1.m_sharedMatrix->m23*m2.m_sharedMatrix->m31, + m1.m_sharedMatrix->m21*m2.m_sharedMatrix->m12 + m1.m_sharedMatrix->m22*m2.m_sharedMatrix->m22 + m1.m_sharedMatrix->m23*m2.m_sharedMatrix->m32, + m1.m_sharedMatrix->m21*m2.m_sharedMatrix->m13 + m1.m_sharedMatrix->m22*m2.m_sharedMatrix->m23 + m1.m_sharedMatrix->m23*m2.m_sharedMatrix->m33, + F(0.0), + + m1.m_sharedMatrix->m31*m2.m_sharedMatrix->m11 + m1.m_sharedMatrix->m32*m2.m_sharedMatrix->m21 + m1.m_sharedMatrix->m33*m2.m_sharedMatrix->m31, + m1.m_sharedMatrix->m31*m2.m_sharedMatrix->m12 + m1.m_sharedMatrix->m32*m2.m_sharedMatrix->m22 + m1.m_sharedMatrix->m33*m2.m_sharedMatrix->m32, + m1.m_sharedMatrix->m31*m2.m_sharedMatrix->m13 + m1.m_sharedMatrix->m32*m2.m_sharedMatrix->m23 + m1.m_sharedMatrix->m33*m2.m_sharedMatrix->m33, + F(0.0), + + m1.m_sharedMatrix->m41*m2.m_sharedMatrix->m11 + m1.m_sharedMatrix->m42*m2.m_sharedMatrix->m21 + m1.m_sharedMatrix->m43*m2.m_sharedMatrix->m31 + m2.m_sharedMatrix->m41, + m1.m_sharedMatrix->m41*m2.m_sharedMatrix->m12 + m1.m_sharedMatrix->m42*m2.m_sharedMatrix->m22 + m1.m_sharedMatrix->m43*m2.m_sharedMatrix->m32 + m2.m_sharedMatrix->m42, + m1.m_sharedMatrix->m41*m2.m_sharedMatrix->m13 + m1.m_sharedMatrix->m42*m2.m_sharedMatrix->m23 + m1.m_sharedMatrix->m43*m2.m_sharedMatrix->m33 + m2.m_sharedMatrix->m43, + F(1.0)); +} + +template +NzMatrix4 NzMatrix4::Identity() +{ + NzMatrix4 matrix; + matrix.MakeIdentity(); + + return matrix; +} + +template +NzMatrix4 NzMatrix4::LookAt(const NzVector3& eye, const NzVector3& target, const NzVector3& up) +{ + NzMatrix4 matrix; + matrix.MakeLookAt(eye, target, up); + + return matrix; +} + +template +NzMatrix4 NzMatrix4::Ortho(T left, T top, T width, T height, T zNear, T zFar) +{ + NzMatrix4 matrix; + matrix.MakeOrtho(left, top, width, height, zNear, zFar); + + return matrix; +} + +template +NzMatrix4 NzMatrix4::Perspective(T angle, T ratio, T zNear, T zFar) +{ + NzMatrix4 matrix; + matrix.MakePerspective(angle, ratio, zNear, zFar); + + return matrix; +} + +template +NzMatrix4 NzMatrix4::Rotate(const NzQuaternion& rotation) +{ + NzMatrix4 matrix; + matrix.MakeRotation(rotation); + + return matrix; +} + +template +NzMatrix4 NzMatrix4::Scale(const NzVector3& scale) +{ + NzMatrix4 matrix; + matrix.MakeScale(scale); + + return matrix; +} + +template +NzMatrix4 NzMatrix4::Translate(const NzVector3& translation) +{ + NzMatrix4 mat; + mat.MakeTranslation(translation); + + return mat; +} + +template +NzMatrix4 NzMatrix4::Zero() +{ + NzMatrix4 matrix; + matrix.MakeZero(); + + return matrix; +} + +template +std::ostream& operator<<(std::ostream& out, const NzMatrix4& matrix) +{ + return out << matrix.ToString(); +} + +template +NzMatrix4 operator*(T scale, const NzMatrix4& matrix) +{ + return matrix * scale; +} + +template +void NzMatrix4::EnsureOwnership() +{ + if (m_sharedMatrix) + { + NazaraLock(m_sharedMatrix->mutex); + if (m_sharedMatrix->refCount > 1) + { + m_sharedMatrix->refCount--; + + SharedMatrix* sharedMatrix = new SharedMatrix; + std::memcpy(&sharedMatrix->m11, &m_sharedMatrix->m11, 16*sizeof(T)); + } + } + else + m_sharedMatrix = new SharedMatrix; +} + +template +void NzMatrix4::ReleaseMatrix() +{ + if (!m_sharedMatrix) + return; + + NazaraMutexLock(m_sharedMatrix->mutex); + bool freeSharedMatrix = (--m_sharedMatrix->refCount == 0); + NazaraMutexUnlock(m_sharedMatrix->mutex); + + if (freeSharedMatrix) + delete m_sharedMatrix; + + m_sharedMatrix = nullptr; +} + +#undef F + +#include diff --git a/include/Nazara/Math/Quaternion.hpp b/include/Nazara/Math/Quaternion.hpp index 6b3c0e7b7..f4b2ab97a 100644 --- a/include/Nazara/Math/Quaternion.hpp +++ b/include/Nazara/Math/Quaternion.hpp @@ -1,90 +1,93 @@ -// Copyright (C) 2012 Rémi Bèges - Jérôme Leclercq -// This file is part of the "Nazara Engine - Mathematics module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef NAZARA_QUATERNION_HPP -#define NAZARA_QUATERNION_HPP - -#include - -template class NzEulerAngles; -template class NzVector3; - -template class NzQuaternion -{ - public: - NzQuaternion(); - NzQuaternion(T W, T X, T Y, T Z); - NzQuaternion(T quat[4]); - NzQuaternion(T angle, const NzVector3& axis); - NzQuaternion(const NzEulerAngles& angles); - //NzQuaternion(const NzMatrix3& mat); - template explicit NzQuaternion(const NzQuaternion& quat); - NzQuaternion(const NzQuaternion& quat) = default; - ~NzQuaternion() = default; - - T DotProduct(const NzQuaternion& vec) const; - - NzQuaternion GetConjugate() const; - NzQuaternion GetNormalized() const; - - void MakeIdentity(); - void MakeZero(); - - T Magnitude() const; - - T Normalize(); - - void Set(T W, T X, T Y, T Z); - void Set(T quat[4]); - void Set(T angle, const NzVector3& normalizedAxis); - void Set(const NzEulerAngles& angles); - //void Set(const NzMatrix3& mat); - void Set(const NzQuaternion& quat); - template void Set(const NzQuaternion& quat); - - T SquaredMagnitude() const; - - NzEulerAngles ToEulerAngles() const; - //NzMatrix3 ToRotationMatrix() const; - NzString ToString() const; - - operator NzString() const; - - NzQuaternion& operator=(const NzQuaternion& quat); - - NzQuaternion operator+(const NzQuaternion& quat) const; - NzQuaternion operator*(const NzQuaternion& quat) const; - NzVector3 operator*(const NzVector3& vec) const; - NzQuaternion operator*(T scale) const; - NzQuaternion operator/(const NzQuaternion& quat) const; - - NzQuaternion& operator+=(const NzQuaternion& quat); - NzQuaternion& operator*=(const NzQuaternion& quat); - NzQuaternion& operator*=(T scale); - NzQuaternion& operator/=(const NzQuaternion& quat); - - bool operator==(const NzQuaternion& quat) const; - bool operator!=(const NzQuaternion& quat) const; - bool operator<(const NzQuaternion& quat) const; - bool operator<=(const NzQuaternion& quat) const; - bool operator>(const NzQuaternion& quat) const; - bool operator>=(const NzQuaternion& quat) const; - - static NzQuaternion Identity(); - static NzQuaternion Slerp(const NzQuaternion& quatA, const NzQuaternion& quatB, T interp); - static NzQuaternion Zero(); - - T w, x, y, z; -}; - -template std::ostream& operator<<(std::ostream& out, const NzQuaternion& quat); - -typedef NzQuaternion NzQuaterniond; -typedef NzQuaternion NzQuaternionf; - -#include - -#endif // NAZARA_QUATERNION_HPP +// Copyright (C) 2012 Rémi Bèges - Jérôme Leclercq +// This file is part of the "Nazara Engine - Mathematics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_QUATERNION_HPP +#define NAZARA_QUATERNION_HPP + +#include + +template class NzEulerAngles; +template class NzVector3; + +template class NzQuaternion +{ + public: + NzQuaternion(); + NzQuaternion(T W, T X, T Y, T Z); + NzQuaternion(T quat[4]); + NzQuaternion(T angle, const NzVector3& axis); + NzQuaternion(const NzEulerAngles& angles); + //NzQuaternion(const NzMatrix3& mat); + template explicit NzQuaternion(const NzQuaternion& quat); + NzQuaternion(const NzQuaternion& quat) = default; + ~NzQuaternion() = default; + + T DotProduct(const NzQuaternion& vec) const; + + NzQuaternion GetConjugate() const; + NzQuaternion GetInverse() const; + NzQuaternion GetNormal() const; + + void Inverse(); + + void MakeIdentity(); + void MakeZero(); + + T Magnitude() const; + + T Normalize(); + + void Set(T W, T X, T Y, T Z); + void Set(T quat[4]); + void Set(T angle, const NzVector3& normalizedAxis); + void Set(const NzEulerAngles& angles); + //void Set(const NzMatrix3& mat); + void Set(const NzQuaternion& quat); + template void Set(const NzQuaternion& quat); + + T SquaredMagnitude() const; + + NzEulerAngles ToEulerAngles() const; + //NzMatrix3 ToRotationMatrix() const; + NzString ToString() const; + + operator NzString() const; + + NzQuaternion& operator=(const NzQuaternion& quat); + + NzQuaternion operator+(const NzQuaternion& quat) const; + NzQuaternion operator*(const NzQuaternion& quat) const; + NzVector3 operator*(const NzVector3& vec) const; + NzQuaternion operator*(T scale) const; + NzQuaternion operator/(const NzQuaternion& quat) const; + + NzQuaternion& operator+=(const NzQuaternion& quat); + NzQuaternion& operator*=(const NzQuaternion& quat); + NzQuaternion& operator*=(T scale); + NzQuaternion& operator/=(const NzQuaternion& quat); + + bool operator==(const NzQuaternion& quat) const; + bool operator!=(const NzQuaternion& quat) const; + bool operator<(const NzQuaternion& quat) const; + bool operator<=(const NzQuaternion& quat) const; + bool operator>(const NzQuaternion& quat) const; + bool operator>=(const NzQuaternion& quat) const; + + static NzQuaternion Identity(); + static NzQuaternion Slerp(const NzQuaternion& quatA, const NzQuaternion& quatB, T interp); + static NzQuaternion Zero(); + + T w, x, y, z; +}; + +template std::ostream& operator<<(std::ostream& out, const NzQuaternion& quat); + +typedef NzQuaternion NzQuaterniond; +typedef NzQuaternion NzQuaternionf; + +#include + +#endif // NAZARA_QUATERNION_HPP diff --git a/include/Nazara/Math/Quaternion.inl b/include/Nazara/Math/Quaternion.inl index c17e5b026..92bf1329d 100644 --- a/include/Nazara/Math/Quaternion.inl +++ b/include/Nazara/Math/Quaternion.inl @@ -1,403 +1,428 @@ -// Copyright (C) 2012 Rémi Bèges - Jérôme Leclercq -// This file is part of the "Nazara Engine - Mathematics module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include -#include - -#define F(a) static_cast(a) - -template -NzQuaternion::NzQuaternion() -{ -} - -template -NzQuaternion::NzQuaternion(T W, T X, T Y, T Z) -{ - Set(W, X, Y, Z); -} - -template -NzQuaternion::NzQuaternion(T quat[4]) -{ - Set(quat); -} - -template -NzQuaternion::NzQuaternion(T angle, const NzVector3& axis) -{ - Set(angle, axis); -} - -template -NzQuaternion::NzQuaternion(const NzEulerAngles& angles) -{ - Set(angles); -} -/* -template -NzQuaternion::NzQuaternion(const NzMatrix3& mat) -{ - Set(mat); -} -*/ -template -template -NzQuaternion::NzQuaternion(const NzQuaternion& quat) -{ - Set(quat); -} - -template -T NzQuaternion::DotProduct(const NzQuaternion& quat) const -{ - return w*quat.w + x*quat.x + y*quat.y + z*quat.z; -} - -template -NzQuaternion NzQuaternion::GetConjugate() const -{ - return NzQuaternion(w, -x, -y, -z); -} - -template -NzQuaternion NzQuaternion::GetNormalized() const -{ - NzQuaternion quat(*this); - quat.Normalize(); - - return quat; -} - -template -void NzQuaternion::MakeIdentity() -{ - Set(1.0, 0.0, 0.0, 0.0); -} - -template -void NzQuaternion::MakeZero() -{ - Set(0.0, 0.0, 0.0, 0.0); -} - -template -T NzQuaternion::Magnitude() const -{ - return std::sqrt(SquaredMagnitude()); -} - -template -T NzQuaternion::Normalize() -{ - T squaredMagnitude = SquaredMagnitude(); - - if (squaredMagnitude-F(1.0) > std::numeric_limits::epsilon()) - { - T magnitude = std::sqrt(squaredMagnitude); - - w /= magnitude; - x /= magnitude; - y /= magnitude; - z /= magnitude; - - return magnitude; - } - else - return F(1.0); // Le quaternion est déjà normalisé -} - -template -void NzQuaternion::Set(T W, T X, T Y, T Z) -{ - w = W; - x = X; - y = Y; - z = Z; -} - -template -void NzQuaternion::Set(T quat[4]) -{ - w = quat[0]; - x = quat[1]; - y = quat[2]; - z = quat[3]; -} - -template -void NzQuaternion::Set(T angle, const NzVector3& normalizedAxis) -{ - #if !NAZARA_MATH_ANGLE_RADIAN - angle = NzDegreeToRadian(angle); - #endif - - angle /= 2; - - auto sinAngle = std::sin(angle); - - w = std::cos(angle); - x = normalizedAxis.x * sinAngle; - y = normalizedAxis.y * sinAngle; - z = normalizedAxis.z * sinAngle; -} - -template -void NzQuaternion::Set(const NzEulerAngles& angles) -{ - Set(angles.ToQuaternion()); -} - -template -template -void NzQuaternion::Set(const NzQuaternion& quat) -{ - w = static_cast(quat.w); - x = static_cast(quat.x); - y = static_cast(quat.y); - z = static_cast(quat.z); -} - -template -void NzQuaternion::Set(const NzQuaternion& quat) -{ - w = quat.w; - x = quat.x; - y = quat.y; - z = quat.z; -} - -template -T NzQuaternion::SquaredMagnitude() const -{ - return w*w + x*x + y*y + z*z; -} - -template -NzEulerAngles NzQuaternion::ToEulerAngles() const -{ - T test = x*y + z*w; - if (test > F(0.499)) - // singularity at north pole - return NzEulerAngles(NzDegrees(F(90.0)), NzRadians(F(2.0) * std::atan2(x, w)), F(0.0)); - - if (test < F(-0.499)) - return NzEulerAngles(NzDegrees(F(-90.0)), NzRadians(F(-2.0) * std::atan2(x, w)), F(0.0)); - - T xx = x*x; - T yy = y*y; - T zz = z*z; - - return NzEulerAngles(NzRadians(std::atan2(F(2.0)*x*w - F(2.0)*y*z, F(1.0) - F(2.0)*xx - F(2.0)*zz)), - NzRadians(std::atan2(F(2.0)*y*w - F(2.0)*x*z, F(1.0) - F(2.0)*yy - F(2.0)*zz)), - NzRadians(std::asin(F(2.0)*test))); -} - -template -NzString NzQuaternion::ToString() const -{ - NzStringStream ss; - - return ss << "Quaternion(" << w << " | " << x << ", " << y << ", " << z << ')'; -} - -template -NzQuaternion::operator NzString() const -{ - return ToString(); -} - -template -NzQuaternion& NzQuaternion::operator=(const NzQuaternion& quat) -{ - Set(quat); - - return *this; -} - -template -NzQuaternion NzQuaternion::operator+(const NzQuaternion& quat) const -{ - return NzQuaternion(w + quat.w, - x + quat.x, - y + quat.y, - z + quat.z); -} - -template -NzQuaternion NzQuaternion::operator*(const NzQuaternion& quat) const -{ - return NzQuaternion(w*quat.w - x*quat.x - y*quat.y - z*quat.z, - w*quat.x + x*quat.w + y*quat.z - z*quat.y, - w*quat.y + y*quat.w + z*quat.x - x*quat.z, - w*quat.z + z*quat.w + x*quat.y - y*quat.x); -} - -template -NzVector3 NzQuaternion::operator*(const NzVector3& vec) const -{ - NzVector3 normal(vec); - normal.Normalize(); - - NzQuaternion qvec(0.0, normal.x, normal.y, normal.z); - NzQuaternion result(operator*(qvec * GetConjugate())); - - return NzVector3(result.x, result.y, result.z); - -} - -template -NzQuaternion NzQuaternion::operator*(T scale) const -{ - return NzQuaternion(w * scale, - x * scale, - y * scale, - z * scale); -} - -template -NzQuaternion NzQuaternion::operator/(const NzQuaternion& quat) const -{ - return GetConjugate(quat) * (*this); -} - -template -NzQuaternion& NzQuaternion::operator+=(const NzQuaternion& quat) -{ - return operator=(operator+(quat)); -} - -template -NzQuaternion& NzQuaternion::operator*=(const NzQuaternion& quat) -{ - return operator=(operator*(quat)); -} - -template -NzQuaternion& NzQuaternion::operator*=(T scale) -{ - return operator=(operator*(scale)); -} - -template -NzQuaternion& NzQuaternion::operator/=(const NzQuaternion& quat) -{ - return operator=(operator/(quat)); -} - -template -bool NzQuaternion::operator==(const NzQuaternion& quat) const -{ - return NzNumberEquals(w, quat.w) && - NzNumberEquals(x, quat.x) && - NzNumberEquals(y, quat.y) && - NzNumberEquals(z, quat.z); -} - -template -bool NzQuaternion::operator!=(const NzQuaternion& quat) const -{ - return !operator==(quat); -} - -template -bool NzQuaternion::operator<(const NzQuaternion& quat) const -{ - return w < quat.w && x < quat.x && y < quat.y && z < quat.z; -} - -template -bool NzQuaternion::operator<=(const NzQuaternion& quat) const -{ - return operator<(quat) || operator==(quat); -} - -template -bool NzQuaternion::operator>(const NzQuaternion& quat) const -{ - return !operator<=(quat); -} - -template -bool NzQuaternion::operator>=(const NzQuaternion& quat) const -{ - return !operator<(quat); -} - -template -NzQuaternion NzQuaternion::Identity() -{ - NzQuaternion quaternion; - quaternion.MakeIdentity(); - - return quaternion; -} - -template -NzQuaternion NzQuaternion::Slerp(const NzQuaternion& quatA, const NzQuaternion& quatB, T interp) -{ - if (interp <= F(0.0)) - return quatA; - - if (interp >= F(1.0)) - return quatB; - - NzQuaternion q; - - T cosOmega = quatA.DotProduct(quatB); - if (cosOmega < F(0.0)) - { - // On inverse tout - q.Set(-quatB.w, -quatB.x, -quatB.y, -quatB.z); - cosOmega = -cosOmega; - } - else - q.Set(quatB); - - T k0, k1; - if (cosOmega > F(0.9999)) - { - // Interpolation linéaire pour éviter une division par zéro - k0 = F(1.0) - interp; - k1 = interp; - } - else - { - T sinOmega = std::sqrt(F(1.0) - cosOmega*cosOmega); - T omega = std::atan2(sinOmega, cosOmega); - - // Pour éviter deux divisions - sinOmega = F(1.0)/sinOmega; - - k0 = std::sin((F(1.0) - interp) * omega) * sinOmega; - k1 = std::sin(interp*omega) * sinOmega; - } - - NzQuaternion result(k0 * quatA.w, k0 * quatA.x, k0 * quatA.y, k0 * quatA.z); - return result += q*k1; -} - -template -NzQuaternion NzQuaternion::Zero() -{ - NzQuaternion quaternion; - quaternion.MakeZero(); - - return quaternion; -} - -template -std::ostream& operator<<(std::ostream& out, const NzQuaternion& quat) -{ - return out << quat.ToString(); -} - -#undef F - -#include +// Copyright (C) 2012 Rémi Bèges - Jérôme Leclercq +// This file is part of the "Nazara Engine - Mathematics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include + +#define F(a) static_cast(a) + +template +NzQuaternion::NzQuaternion() +{ +} + +template +NzQuaternion::NzQuaternion(T W, T X, T Y, T Z) +{ + Set(W, X, Y, Z); +} + +template +NzQuaternion::NzQuaternion(T quat[4]) +{ + Set(quat); +} + +template +NzQuaternion::NzQuaternion(T angle, const NzVector3& axis) +{ + Set(angle, axis); +} + +template +NzQuaternion::NzQuaternion(const NzEulerAngles& angles) +{ + Set(angles); +} +/* +template +NzQuaternion::NzQuaternion(const NzMatrix3& mat) +{ + Set(mat); +} +*/ +template +template +NzQuaternion::NzQuaternion(const NzQuaternion& quat) +{ + Set(quat); +} + +template +T NzQuaternion::DotProduct(const NzQuaternion& quat) const +{ + return w*quat.w + x*quat.x + y*quat.y + z*quat.z; +} + +template +NzQuaternion NzQuaternion::GetConjugate() const +{ + return NzQuaternion(w, -x, -y, -z); +} + +template +NzQuaternion NzQuaternion::GetInverse() const +{ + NzQuaternion quat(*this); + quat.Inverse(); + + return quat; +} + +template +NzQuaternion NzQuaternion::GetNormal() const +{ + NzQuaternion quat(*this); + quat.Normalize(); + + return quat; +} + +template +void NzQuaternion::Inverse() +{ + T norm = SquaredMagnitude(); + if (norm > F(0.0)) + { + T invNorm = F(1.0) / norm; + + w *= invNorm; + x *= -invNorm; + y *= -invNorm; + z *= -invNorm; + } +} + +template +void NzQuaternion::MakeIdentity() +{ + Set(F(1.0), F(0.0), F(0.0), F(0.0)); +} + +template +void NzQuaternion::MakeZero() +{ + Set(F(0.0), F(0.0), F(0.0), F(0.0)); +} + +template +T NzQuaternion::Magnitude() const +{ + return std::sqrt(SquaredMagnitude()); +} + +template +T NzQuaternion::Normalize() +{ + T squaredMagnitude = SquaredMagnitude(); + + // Inutile de vérifier si la magnitude au carrée est négative (Elle ne peut pas l'être) + if (!NzNumberEquals(squaredMagnitude, F(1.0))) + { + T norm = std::sqrt(squaredMagnitude); + T invNorm = F(1.0) / norm; + + w *= invNorm; + x *= invNorm; + y *= invNorm; + z *= invNorm; + + return norm; + } + else + return F(1.0); // Le quaternion est déjà normalisé +} + +template +void NzQuaternion::Set(T W, T X, T Y, T Z) +{ + w = W; + x = X; + y = Y; + z = Z; +} + +template +void NzQuaternion::Set(T quat[4]) +{ + w = quat[0]; + x = quat[1]; + y = quat[2]; + z = quat[3]; +} + +template +void NzQuaternion::Set(T angle, const NzVector3& axis) +{ + angle /= F(2.0); + + #if !NAZARA_MATH_ANGLE_RADIAN + angle = NzDegreeToRadian(angle); + #endif + + NzVector3 normalizedAxis = axis.GetNormal(); + + T sinAngle = std::sin(angle); + + w = std::cos(angle); + x = normalizedAxis.x * sinAngle; + y = normalizedAxis.y * sinAngle; + z = normalizedAxis.z * sinAngle; + + Normalize(); +} + +template +void NzQuaternion::Set(const NzEulerAngles& angles) +{ + Set(angles.ToQuaternion()); +} + +template +template +void NzQuaternion::Set(const NzQuaternion& quat) +{ + w = static_cast(quat.w); + x = static_cast(quat.x); + y = static_cast(quat.y); + z = static_cast(quat.z); +} + +template +void NzQuaternion::Set(const NzQuaternion& quat) +{ + w = quat.w; + x = quat.x; + y = quat.y; + z = quat.z; +} + +template +T NzQuaternion::SquaredMagnitude() const +{ + return w*w + x*x + y*y + z*z; +} + +template +NzEulerAngles NzQuaternion::ToEulerAngles() const +{ + T test = x*y + z*w; + if (test > F(0.499)) + // singularity at north pole + return NzEulerAngles(NzDegrees(F(90.0)), NzRadians(F(2.0) * std::atan2(x, w)), F(0.0)); + + if (test < F(-0.499)) + return NzEulerAngles(NzDegrees(F(-90.0)), NzRadians(F(-2.0) * std::atan2(x, w)), F(0.0)); + + return NzEulerAngles(NzRadians(std::atan2(F(2.0)*x*w - F(2.0)*y*z, F(1.0) - F(2.0)*x* - F(2.0)*z*z)), + NzRadians(std::atan2(F(2.0)*y*w - F(2.0)*x*z, F(1.0) - F(2.0)*y*y - F(2.0)*z*z)), + NzRadians(std::asin(F(2.0)*test))); +} + +template +NzString NzQuaternion::ToString() const +{ + NzStringStream ss; + + return ss << "Quaternion(" << w << " | " << x << ", " << y << ", " << z << ')'; +} + +template +NzQuaternion::operator NzString() const +{ + return ToString(); +} + +template +NzQuaternion& NzQuaternion::operator=(const NzQuaternion& quat) +{ + Set(quat); + + return *this; +} + +template +NzQuaternion NzQuaternion::operator+(const NzQuaternion& quat) const +{ + return NzQuaternion(w + quat.w, + x + quat.x, + y + quat.y, + z + quat.z); +} + +template +NzQuaternion NzQuaternion::operator*(const NzQuaternion& quat) const +{ + return NzQuaternion(w*quat.w - x*quat.x - y*quat.y - z*quat.z, + w*quat.x + x*quat.w + y*quat.z - z*quat.y, + w*quat.y + y*quat.w + z*quat.x - x*quat.z, + w*quat.z + z*quat.w + x*quat.y - y*quat.x); +} + +template +NzVector3 NzQuaternion::operator*(const NzVector3& vec) const +{ + NzVector3f quatVec(x, y, z); + NzVector3f uv = quatVec.CrossProduct(vec); + NzVector3f uuv = quatVec.CrossProduct(uv); + uv *= F(2.0) * w; + uuv *= F(2.0); + + return vec + uv + uuv; +} + +template +NzQuaternion NzQuaternion::operator*(T scale) const +{ + return NzQuaternion(w * scale, + x * scale, + y * scale, + z * scale); +} + +template +NzQuaternion NzQuaternion::operator/(const NzQuaternion& quat) const +{ + return GetConjugate(quat) * (*this); +} + +template +NzQuaternion& NzQuaternion::operator+=(const NzQuaternion& quat) +{ + return operator=(operator+(quat)); +} + +template +NzQuaternion& NzQuaternion::operator*=(const NzQuaternion& quat) +{ + return operator=(operator*(quat)); +} + +template +NzQuaternion& NzQuaternion::operator*=(T scale) +{ + return operator=(operator*(scale)); +} + +template +NzQuaternion& NzQuaternion::operator/=(const NzQuaternion& quat) +{ + return operator=(operator/(quat)); +} + +template +bool NzQuaternion::operator==(const NzQuaternion& quat) const +{ + return NzNumberEquals(w, quat.w) && + NzNumberEquals(x, quat.x) && + NzNumberEquals(y, quat.y) && + NzNumberEquals(z, quat.z); +} + +template +bool NzQuaternion::operator!=(const NzQuaternion& quat) const +{ + return !operator==(quat); +} + +template +bool NzQuaternion::operator<(const NzQuaternion& quat) const +{ + return w < quat.w && x < quat.x && y < quat.y && z < quat.z; +} + +template +bool NzQuaternion::operator<=(const NzQuaternion& quat) const +{ + return operator<(quat) || operator==(quat); +} + +template +bool NzQuaternion::operator>(const NzQuaternion& quat) const +{ + return !operator<=(quat); +} + +template +bool NzQuaternion::operator>=(const NzQuaternion& quat) const +{ + return !operator<(quat); +} + +template +NzQuaternion NzQuaternion::Identity() +{ + NzQuaternion quaternion; + quaternion.MakeIdentity(); + + return quaternion; +} + +template +NzQuaternion NzQuaternion::Slerp(const NzQuaternion& quatA, const NzQuaternion& quatB, T interp) +{ + if (interp <= F(0.0)) + return quatA; + + if (interp >= F(1.0)) + return quatB; + + NzQuaternion q; + + T cosOmega = quatA.DotProduct(quatB); + if (cosOmega < F(0.0)) + { + // On inverse tout + q.Set(-quatB.w, -quatB.x, -quatB.y, -quatB.z); + cosOmega = -cosOmega; + } + else + q.Set(quatB); + + T k0, k1; + if (cosOmega > F(0.9999)) + { + // Interpolation linéaire pour éviter une division par zéro + k0 = F(1.0) - interp; + k1 = interp; + } + else + { + T sinOmega = std::sqrt(F(1.0) - cosOmega*cosOmega); + T omega = std::atan2(sinOmega, cosOmega); + + // Pour éviter deux divisions + sinOmega = F(1.0)/sinOmega; + + k0 = std::sin((F(1.0) - interp) * omega) * sinOmega; + k1 = std::sin(interp*omega) * sinOmega; + } + + NzQuaternion result(k0 * quatA.w, k0 * quatA.x, k0 * quatA.y, k0 * quatA.z); + return result += q*k1; +} + +template +NzQuaternion NzQuaternion::Zero() +{ + NzQuaternion quaternion; + quaternion.MakeZero(); + + return quaternion; +} + +template +std::ostream& operator<<(std::ostream& out, const NzQuaternion& quat) +{ + return out << quat.ToString(); +} + +#undef F + +#include diff --git a/include/Nazara/Math/Vector2.hpp b/include/Nazara/Math/Vector2.hpp index 4f02ae77e..9d24dbc69 100644 --- a/include/Nazara/Math/Vector2.hpp +++ b/include/Nazara/Math/Vector2.hpp @@ -1,104 +1,105 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Mathematics module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef NAZARA_VECTOR2_HPP -#define NAZARA_VECTOR2_HPP - -#include - -template class NzVector2 -{ - public: - NzVector2(); - NzVector2(T X, T Y); - explicit NzVector2(T scale); - NzVector2(T vec[2]); - template explicit NzVector2(const NzVector2& vec); - NzVector2(const NzVector2& vec) = default; - ~NzVector2() = default; - - T AbsDotProduct(const NzVector2& vec) const; - - T Distance(const NzVector2& vec) const; - float Distancef(const NzVector2& vec) const; - - T DotProduct(const NzVector2& vec) const; - - NzVector2 GetNormal() const; - - void MakeCeil(const NzVector2& vec); - void MakeFloor(const NzVector2& vec); - void MakeUnitX(); - void MakeUnitY(); - void MakeZero(); - - T Length() const; - float Lengthf() const; - - void Normalize(); - - void Set(T X, T Y); - void Set(T scale); - void Set(T vec[2]); - template void Set(const NzVector2& vec); - - T SquaredDistance(const NzVector2& vec) const; - T SquaredLength() const; - - NzString ToString() const; - - operator NzString() const; - - operator T*(); - operator const T*() const; - - T& operator[](unsigned int i); - T operator[](unsigned int i) const; - - const NzVector2& operator+() const; - NzVector2 operator-() const; - - NzVector2 operator+(const NzVector2& vec) const; - NzVector2 operator-(const NzVector2& vec) const; - NzVector2 operator*(const NzVector2& vec) const; - NzVector2 operator*(T scale) const; - NzVector2 operator/(const NzVector2& vec) const; - NzVector2 operator/(T scale) const; - - NzVector2& operator+=(const NzVector2& vec); - NzVector2& operator-=(const NzVector2& vec); - NzVector2& operator*=(const NzVector2& vec); - NzVector2& operator*=(T scale); - NzVector2& operator/=(const NzVector2& vec); - NzVector2& operator/=(T scale); - - bool operator==(const NzVector2& vec) const; - bool operator!=(const NzVector2& vec) const; - bool operator<(const NzVector2& vec) const; - bool operator<=(const NzVector2& vec) const; - bool operator>(const NzVector2& vec) const; - bool operator>=(const NzVector2& vec) const; - - static NzVector2 UnitX(); - static NzVector2 UnitY(); - static NzVector2 Zero(); - - T x, y; -}; - -template std::ostream& operator<<(std::ostream& out, const NzVector2& vec); - -template NzVector2 operator*(T scale, const NzVector2& vec); -template NzVector2 operator/(T scale, const NzVector2& vec); - -typedef NzVector2 NzVector2d; -typedef NzVector2 NzVector2f; -typedef NzVector2 NzVector2i; -typedef NzVector2 NzVector2ui; - -#include - -#endif // NAZARA_VECTOR2_HPP +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Mathematics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_VECTOR2_HPP +#define NAZARA_VECTOR2_HPP + +#include + +template class NzVector2 +{ + public: + NzVector2(); + NzVector2(T X, T Y); + explicit NzVector2(T scale); + NzVector2(T vec[2]); + template explicit NzVector2(const NzVector2& vec); + NzVector2(const NzVector2& vec) = default; + ~NzVector2() = default; + + T AbsDotProduct(const NzVector2& vec) const; + + T Distance(const NzVector2& vec) const; + float Distancef(const NzVector2& vec) const; + + T DotProduct(const NzVector2& vec) const; + + NzVector2 GetNormal() const; + + T Length() const; + float Lengthf() const; + + void MakeUnitX(); + void MakeUnitY(); + void MakeZero(); + + void Maximize(const NzVector2& vec); + void Minimize(const NzVector2& vec); + + void Normalize(); + + void Set(T X, T Y); + void Set(T scale); + void Set(T vec[2]); + template void Set(const NzVector2& vec); + + T SquaredDistance(const NzVector2& vec) const; + T SquaredLength() const; + + NzString ToString() const; + + operator NzString() const; + + operator T*(); + operator const T*() const; + + T& operator[](unsigned int i); + T operator[](unsigned int i) const; + + const NzVector2& operator+() const; + NzVector2 operator-() const; + + NzVector2 operator+(const NzVector2& vec) const; + NzVector2 operator-(const NzVector2& vec) const; + NzVector2 operator*(const NzVector2& vec) const; + NzVector2 operator*(T scale) const; + NzVector2 operator/(const NzVector2& vec) const; + NzVector2 operator/(T scale) const; + + NzVector2& operator+=(const NzVector2& vec); + NzVector2& operator-=(const NzVector2& vec); + NzVector2& operator*=(const NzVector2& vec); + NzVector2& operator*=(T scale); + NzVector2& operator/=(const NzVector2& vec); + NzVector2& operator/=(T scale); + + bool operator==(const NzVector2& vec) const; + bool operator!=(const NzVector2& vec) const; + bool operator<(const NzVector2& vec) const; + bool operator<=(const NzVector2& vec) const; + bool operator>(const NzVector2& vec) const; + bool operator>=(const NzVector2& vec) const; + + static NzVector2 UnitX(); + static NzVector2 UnitY(); + static NzVector2 Zero(); + + T x, y; +}; + +template std::ostream& operator<<(std::ostream& out, const NzVector2& vec); + +template NzVector2 operator*(T scale, const NzVector2& vec); +template NzVector2 operator/(T scale, const NzVector2& vec); + +typedef NzVector2 NzVector2d; +typedef NzVector2 NzVector2f; +typedef NzVector2 NzVector2i; +typedef NzVector2 NzVector2ui; + +#include + +#endif // NAZARA_VECTOR2_HPP diff --git a/include/Nazara/Math/Vector2.inl b/include/Nazara/Math/Vector2.inl index f1a716f7b..1c4639a52 100644 --- a/include/Nazara/Math/Vector2.inl +++ b/include/Nazara/Math/Vector2.inl @@ -1,488 +1,488 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Mathematics module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include -#include - -#define F(a) static_cast(a) - -template -NzVector2::NzVector2() -{ -} - -template -NzVector2::NzVector2(T X, T Y) -{ - Set(X, Y); -} - -template -NzVector2::NzVector2(T scale) -{ - Set(scale); -} - -template -NzVector2::NzVector2(T vec[2]) -{ - Set(vec); -} - -template -template -NzVector2::NzVector2(const NzVector2& vec) -{ - Set(vec); -} - -template -T NzVector2::AbsDotProduct(const NzVector2& vec) const -{ - return std::fabs(x * vec.x) + std::fabs(y * vec.y); -} - -template<> -inline int NzVector2::AbsDotProduct(const NzVector2& vec) const -{ - return std::labs(x * vec.x) + std::labs(y * vec.y); -} - -template<> -inline unsigned int NzVector2::AbsDotProduct(const NzVector2& vec) const -{ - return std::labs(x * vec.x) + std::labs(y * vec.y); -} - -template -T NzVector2::Distance(const NzVector2& vec) const -{ - return std::sqrt(SquaredDistance(vec)); -} - -template -float NzVector2::Distancef(const NzVector2& vec) const -{ - return std::sqrt(static_cast(SquaredDistance(vec))); -} - -template -T NzVector2::DotProduct(const NzVector2& vec) const -{ - return x*vec.x + y*vec.y; -} - -template -NzVector2 NzVector2::GetNormal() const -{ - NzVector2 vec(*this); - vec.Normalize(); - - return vec; -} - -template -T NzVector2::Length() const -{ - return std::sqrt(SquaredLength()); -} - -template -float NzVector2::Lengthf() const -{ - return std::sqrt(static_cast(SquaredLength())); -} - -template -void NzVector2::MakeCeil(const NzVector2& vec) -{ - if (vec.x > x) - x = vec.x; - - if (vec.y > y) - y = vec.y; -} - -template -void NzVector2::MakeFloor(const NzVector2& vec) -{ - if (vec.x < x) - x = vec.x; - - if (vec.y < y) - y = vec.y; -} - -template -void NzVector2::MakeUnitX() -{ - Set(F(1.0), F(0.0)); -} - -template -void NzVector2::MakeUnitY() -{ - Set(F(0.0), F(1.0)); -} - -template -void NzVector2::MakeZero() -{ - Set(F(0.0), F(0.0)); -} - -template -void NzVector2::Normalize() -{ - T squaredLength = SquaredLength(); - - if (squaredLength-F(1.0) > std::numeric_limits::epsilon()) - { - T length = std::sqrt(squaredLength); - - x /= length; - y /= length; - } -} - -template -void NzVector2::Set(T X, T Y) -{ - x = X; - y = Y; -} - -template -void NzVector2::Set(T scale) -{ - x = scale; - y = scale; -} - -template -void NzVector2::Set(T vec[2]) -{ - std::memcpy(&x, vec, 2*sizeof(T)); -} - -template -template -void NzVector2::Set(const NzVector2& vec) -{ - x = F(vec.x); - y = F(vec.y); -} - -template -T NzVector2::SquaredDistance(const NzVector2& vec) const -{ - return operator-(vec).SquaredLength(); -} - -template -T NzVector2::SquaredLength() const -{ - return x*x + y*y; -} - -template -NzString NzVector2::ToString() const -{ - NzStringStream ss; - - return ss << "Vector2(" << x << ", " << y << ')'; -} - -template -NzVector2::operator NzString() const -{ - return ToString(); -} - -template -NzVector2::operator T*() -{ - return &x; -} - -template -NzVector2::operator const T*() const -{ - return &x; -} - -template -T& NzVector2::operator[](unsigned int i) -{ - #if NAZARA_MATH_SAFE - if (i >= 2) - { - NzStringStream ss; - ss << __FILE__ << ':' << __LINE__ << ": Index out of range (" << i << " >= 2)"; - - throw std::domain_error(ss.ToString()); - } - #endif - - return *(&x+i); -} - -template -T NzVector2::operator[](unsigned int i) const -{ - #if NAZARA_MATH_SAFE - if (i >= 2) - { - NzStringStream ss; - ss << __FILE__ << ':' << __LINE__ << ": Index out of range (" << i << " >= 2)"; - - throw std::domain_error(ss.ToString()); - } - #endif - - return *(&x+i); -} - -template -const NzVector2& NzVector2::operator+() const -{ - return *this; -} - -template -NzVector2 NzVector2::operator-() const -{ - return NzVector2(-x, -y); -} - -template -NzVector2 NzVector2::operator+(const NzVector2& vec) const -{ - return NzVector2(x + vec.x, y + vec.y); -} - -template -NzVector2 NzVector2::operator-(const NzVector2& vec) const -{ - return NzVector2(x - vec.x, y - vec.y); -} - -template -NzVector2 NzVector2::operator*(const NzVector2& vec) const -{ - return NzVector2(x * vec.x, y * vec.y); -} - -template -NzVector2 NzVector2::operator*(T scale) const -{ - return NzVector2(x * scale, y * scale); -} - -template -NzVector2 NzVector2::operator/(const NzVector2& vec) const -{ - #if NAZARA_MATH_SAFE - if (NzNumberEquals(vec.x, F(0.0)) || NzNumberEquals(vec.y, F(0.0))) - { - NzStringStream ss; - ss << __FILE__ << ':' << __LINE__ << ": Division by zero"; - - throw std::domain_error(ss.ToString()); - } - #endif - - return NzVector2(x / vec.x, y / vec.y); -} - -template -NzVector2 NzVector2::operator/(T scale) const -{ - #if NAZARA_MATH_SAFE - if (NzNumberEquals(scale, F(0.0))) - { - NzStringStream ss; - ss << __FILE__ << ':' << __LINE__ << ": Division by zero"; - - throw std::domain_error(ss.ToString()); - } - #endif - - return NzVector2(x / scale, y / scale); -} - -template -NzVector2& NzVector2::operator+=(const NzVector2& vec) -{ - x += vec.x; - y += vec.y; - - return *this; -} - -template -NzVector2& NzVector2::operator-=(const NzVector2& vec) -{ - x -= vec.x; - y -= vec.y; - - return *this; -} - -template -NzVector2& NzVector2::operator*=(const NzVector2& vec) -{ - x *= vec.x; - y *= vec.y; - - return *this; -} - -template -NzVector2& NzVector2::operator*=(T scale) -{ - x *= scale; - y *= scale; - - return *this; -} - -template -NzVector2& NzVector2::operator/=(const NzVector2& vec) -{ - #if NAZARA_MATH_SAFE - if (NzNumberEquals(vec.x, F(0.0)) || NzNumberEquals(vec.y, F(0.0)) || NzNumberEquals(vec.z, F(0.0))) - { - NzStringStream ss; - ss << __FILE__ << ':' << __LINE__ << ": Division by zero"; - - throw std::domain_error(ss.ToString()); - } - #endif - - x /= vec.x; - y /= vec.y; - - return *this; -} - -template -NzVector2& NzVector2::operator/=(T scale) -{ - #if NAZARA_MATH_SAFE - if (NzNumberEquals(scale, F(0.0))) - { - NzStringStream ss; - ss << __FILE__ << ':' << __LINE__ << ": Division by zero"; - - throw std::domain_error(ss.ToString()); - } - #endif - - x /= scale; - y /= scale; - - return *this; -} - -template -bool NzVector2::operator==(const NzVector2& vec) const -{ - return NzNumberEquals(x, vec.x) && - NzNumberEquals(y, vec.y); -} - -template -bool NzVector2::operator!=(const NzVector2& vec) const -{ - return !operator==(vec); -} - -template -bool NzVector2::operator<(const NzVector2& vec) const -{ - return x < vec.x && y < vec.y; -} - -template -bool NzVector2::operator<=(const NzVector2& vec) const -{ - return operator<(vec) || operator==(vec); -} - -template -bool NzVector2::operator>(const NzVector2& vec) const -{ - return !operator<=(vec); -} - -template -bool NzVector2::operator>=(const NzVector2& vec) const -{ - return !operator<(vec); -} - -template -NzVector2 NzVector2::UnitX() -{ - NzVector2 vector; - vector.MakeUnitX(); - - return vector; -} - -template -NzVector2 NzVector2::UnitY() -{ - NzVector2 vector; - vector.MakeUnitY(); - - return vector; -} - -template -NzVector2 NzVector2::Zero() -{ - NzVector2 vector; - vector.MakeZero(); - - return vector; -} - -template -std::ostream& operator<<(std::ostream& out, const NzVector2& vec) -{ - return out << vec.ToString(); -} - -template -NzVector2 operator*(T scale, const NzVector2& vec) -{ - return NzVector2(scale * vec.x, scale * vec.y); -} - -template -NzVector2 operator/(T scale, const NzVector2& vec) -{ - #if NAZARA_MATH_SAFE - if (NzNumberEquals(vec.x, F(0.0)) || NzNumberEquals(vec.y, F(0.0)) || NzNumberEquals(vec.z, F(0.0))) - { - NzStringStream ss; - ss << __FILE__ << ':' << __LINE__ << ": Division by zero"; - - throw std::domain_error(ss.ToString()); - } - #endif - - return NzVector2(scale/vec.x, scale/vec.y); -} - -#undef F - -#include +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Mathematics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include + +#define F(a) static_cast(a) + +template +NzVector2::NzVector2() +{ +} + +template +NzVector2::NzVector2(T X, T Y) +{ + Set(X, Y); +} + +template +NzVector2::NzVector2(T scale) +{ + Set(scale); +} + +template +NzVector2::NzVector2(T vec[2]) +{ + Set(vec); +} + +template +template +NzVector2::NzVector2(const NzVector2& vec) +{ + Set(vec); +} + +template +T NzVector2::AbsDotProduct(const NzVector2& vec) const +{ + return std::fabs(x * vec.x) + std::fabs(y * vec.y); +} + +template<> +inline int NzVector2::AbsDotProduct(const NzVector2& vec) const +{ + return std::labs(x * vec.x) + std::labs(y * vec.y); +} + +template<> +inline unsigned int NzVector2::AbsDotProduct(const NzVector2& vec) const +{ + return std::labs(x * vec.x) + std::labs(y * vec.y); +} + +template +T NzVector2::Distance(const NzVector2& vec) const +{ + return std::sqrt(SquaredDistance(vec)); +} + +template +float NzVector2::Distancef(const NzVector2& vec) const +{ + return std::sqrt(static_cast(SquaredDistance(vec))); +} + +template +T NzVector2::DotProduct(const NzVector2& vec) const +{ + return x*vec.x + y*vec.y; +} + +template +NzVector2 NzVector2::GetNormal() const +{ + NzVector2 vec(*this); + vec.Normalize(); + + return vec; +} + +template +T NzVector2::Length() const +{ + return std::sqrt(SquaredLength()); +} + +template +float NzVector2::Lengthf() const +{ + return std::sqrt(static_cast(SquaredLength())); +} + +template +void NzVector2::MakeUnitX() +{ + Set(F(1.0), F(0.0)); +} + +template +void NzVector2::MakeUnitY() +{ + Set(F(0.0), F(1.0)); +} + +template +void NzVector2::MakeZero() +{ + Set(F(0.0), F(0.0)); +} + +template +void NzVector2::Maximize(const NzVector2& vec) +{ + if (vec.x > x) + x = vec.x; + + if (vec.y > y) + y = vec.y; +} + +template +void NzVector2::Minimize(const NzVector2& vec) +{ + if (vec.x < x) + x = vec.x; + + if (vec.y < y) + y = vec.y; +} + +template +void NzVector2::Normalize() +{ + T squaredLength = SquaredLength(); + + if (squaredLength-F(1.0) > std::numeric_limits::epsilon()) + { + T length = std::sqrt(squaredLength); + + x /= length; + y /= length; + } +} + +template +void NzVector2::Set(T X, T Y) +{ + x = X; + y = Y; +} + +template +void NzVector2::Set(T scale) +{ + x = scale; + y = scale; +} + +template +void NzVector2::Set(T vec[2]) +{ + std::memcpy(&x, vec, 2*sizeof(T)); +} + +template +template +void NzVector2::Set(const NzVector2& vec) +{ + x = F(vec.x); + y = F(vec.y); +} + +template +T NzVector2::SquaredDistance(const NzVector2& vec) const +{ + return operator-(vec).SquaredLength(); +} + +template +T NzVector2::SquaredLength() const +{ + return x*x + y*y; +} + +template +NzString NzVector2::ToString() const +{ + NzStringStream ss; + + return ss << "Vector2(" << x << ", " << y << ')'; +} + +template +NzVector2::operator NzString() const +{ + return ToString(); +} + +template +NzVector2::operator T*() +{ + return &x; +} + +template +NzVector2::operator const T*() const +{ + return &x; +} + +template +T& NzVector2::operator[](unsigned int i) +{ + #if NAZARA_MATH_SAFE + if (i >= 2) + { + NzStringStream ss; + ss << __FILE__ << ':' << __LINE__ << ": Index out of range (" << i << " >= 2)"; + + throw std::domain_error(ss.ToString()); + } + #endif + + return *(&x+i); +} + +template +T NzVector2::operator[](unsigned int i) const +{ + #if NAZARA_MATH_SAFE + if (i >= 2) + { + NzStringStream ss; + ss << __FILE__ << ':' << __LINE__ << ": Index out of range (" << i << " >= 2)"; + + throw std::domain_error(ss.ToString()); + } + #endif + + return *(&x+i); +} + +template +const NzVector2& NzVector2::operator+() const +{ + return *this; +} + +template +NzVector2 NzVector2::operator-() const +{ + return NzVector2(-x, -y); +} + +template +NzVector2 NzVector2::operator+(const NzVector2& vec) const +{ + return NzVector2(x + vec.x, y + vec.y); +} + +template +NzVector2 NzVector2::operator-(const NzVector2& vec) const +{ + return NzVector2(x - vec.x, y - vec.y); +} + +template +NzVector2 NzVector2::operator*(const NzVector2& vec) const +{ + return NzVector2(x * vec.x, y * vec.y); +} + +template +NzVector2 NzVector2::operator*(T scale) const +{ + return NzVector2(x * scale, y * scale); +} + +template +NzVector2 NzVector2::operator/(const NzVector2& vec) const +{ + #if NAZARA_MATH_SAFE + if (NzNumberEquals(vec.x, F(0.0)) || NzNumberEquals(vec.y, F(0.0))) + { + NzStringStream ss; + ss << __FILE__ << ':' << __LINE__ << ": Division by zero"; + + throw std::domain_error(ss.ToString()); + } + #endif + + return NzVector2(x / vec.x, y / vec.y); +} + +template +NzVector2 NzVector2::operator/(T scale) const +{ + #if NAZARA_MATH_SAFE + if (NzNumberEquals(scale, F(0.0))) + { + NzStringStream ss; + ss << __FILE__ << ':' << __LINE__ << ": Division by zero"; + + throw std::domain_error(ss.ToString()); + } + #endif + + return NzVector2(x / scale, y / scale); +} + +template +NzVector2& NzVector2::operator+=(const NzVector2& vec) +{ + x += vec.x; + y += vec.y; + + return *this; +} + +template +NzVector2& NzVector2::operator-=(const NzVector2& vec) +{ + x -= vec.x; + y -= vec.y; + + return *this; +} + +template +NzVector2& NzVector2::operator*=(const NzVector2& vec) +{ + x *= vec.x; + y *= vec.y; + + return *this; +} + +template +NzVector2& NzVector2::operator*=(T scale) +{ + x *= scale; + y *= scale; + + return *this; +} + +template +NzVector2& NzVector2::operator/=(const NzVector2& vec) +{ + #if NAZARA_MATH_SAFE + if (NzNumberEquals(vec.x, F(0.0)) || NzNumberEquals(vec.y, F(0.0)) || NzNumberEquals(vec.z, F(0.0))) + { + NzStringStream ss; + ss << __FILE__ << ':' << __LINE__ << ": Division by zero"; + + throw std::domain_error(ss.ToString()); + } + #endif + + x /= vec.x; + y /= vec.y; + + return *this; +} + +template +NzVector2& NzVector2::operator/=(T scale) +{ + #if NAZARA_MATH_SAFE + if (NzNumberEquals(scale, F(0.0))) + { + NzStringStream ss; + ss << __FILE__ << ':' << __LINE__ << ": Division by zero"; + + throw std::domain_error(ss.ToString()); + } + #endif + + x /= scale; + y /= scale; + + return *this; +} + +template +bool NzVector2::operator==(const NzVector2& vec) const +{ + return NzNumberEquals(x, vec.x) && + NzNumberEquals(y, vec.y); +} + +template +bool NzVector2::operator!=(const NzVector2& vec) const +{ + return !operator==(vec); +} + +template +bool NzVector2::operator<(const NzVector2& vec) const +{ + return x < vec.x && y < vec.y; +} + +template +bool NzVector2::operator<=(const NzVector2& vec) const +{ + return operator<(vec) || operator==(vec); +} + +template +bool NzVector2::operator>(const NzVector2& vec) const +{ + return !operator<=(vec); +} + +template +bool NzVector2::operator>=(const NzVector2& vec) const +{ + return !operator<(vec); +} + +template +NzVector2 NzVector2::UnitX() +{ + NzVector2 vector; + vector.MakeUnitX(); + + return vector; +} + +template +NzVector2 NzVector2::UnitY() +{ + NzVector2 vector; + vector.MakeUnitY(); + + return vector; +} + +template +NzVector2 NzVector2::Zero() +{ + NzVector2 vector; + vector.MakeZero(); + + return vector; +} + +template +std::ostream& operator<<(std::ostream& out, const NzVector2& vec) +{ + return out << vec.ToString(); +} + +template +NzVector2 operator*(T scale, const NzVector2& vec) +{ + return NzVector2(scale * vec.x, scale * vec.y); +} + +template +NzVector2 operator/(T scale, const NzVector2& vec) +{ + #if NAZARA_MATH_SAFE + if (NzNumberEquals(vec.x, F(0.0)) || NzNumberEquals(vec.y, F(0.0)) || NzNumberEquals(vec.z, F(0.0))) + { + NzStringStream ss; + ss << __FILE__ << ':' << __LINE__ << ": Division by zero"; + + throw std::domain_error(ss.ToString()); + } + #endif + + return NzVector2(scale/vec.x, scale/vec.y); +} + +#undef F + +#include diff --git a/include/Nazara/Math/Vector3.hpp b/include/Nazara/Math/Vector3.hpp index b501fee58..8734bed41 100644 --- a/include/Nazara/Math/Vector3.hpp +++ b/include/Nazara/Math/Vector3.hpp @@ -1,111 +1,121 @@ -// Copyright (C) 2012 Rémi Bèges - Jérôme Leclercq -// This file is part of the "Nazara Engine - Mathematics module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef NAZARA_VECTOR3_HPP -#define NAZARA_VECTOR3_HPP - -#include -#include - -template class NzVector3 -{ - public: - NzVector3(); - NzVector3(T X, T Y, T Z); - explicit NzVector3(T scale); - NzVector3(T vec[3]); - NzVector3(const NzVector2& vec, T Z = 0.0); - template explicit NzVector3(const NzVector3& vec); - NzVector3(const NzVector3& vec) = default; - ~NzVector3() = default; - - T AbsDotProduct(const NzVector3& vec) const; - - NzVector3 CrossProduct(const NzVector3& vec) const; - - T Distance(const NzVector3& vec) const; - float Distancef(const NzVector3& vec) const; - - T DotProduct(const NzVector3& vec) const; - - NzVector3 GetNormal() const; - - T Length() const; - float Lengthf() const; - - void MakeCeil(const NzVector3& vec); - void MakeFloor(const NzVector3& vec); - void MakeUnitX(); - void MakeUnitY(); - void MakeUnitZ(); - void MakeZero(); - - void Normalize(); - - void Set(T X, T Y, T Z); - void Set(T scale); - void Set(T vec[3]); - void Set(const NzVector2& vec, T Z = 0.0); - template void Set(const NzVector3& vec); - - T SquaredDistance(const NzVector3& vec) const; - T SquaredLength() const; - - NzString ToString() const; - - operator NzString() const; - - operator T*(); - operator const T*() const; - - T& operator[](unsigned int i); - T operator[](unsigned int i) const; - - const NzVector3& operator+() const; - NzVector3 operator-() const; - - NzVector3 operator+(const NzVector3& vec) const; - NzVector3 operator-(const NzVector3& vec) const; - NzVector3 operator*(const NzVector3& vec) const; - NzVector3 operator*(T scale) const; - NzVector3 operator/(const NzVector3& vec) const; - NzVector3 operator/(T scale) const; - - NzVector3& operator+=(const NzVector3& vec); - NzVector3& operator-=(const NzVector3& vec); - NzVector3& operator*=(const NzVector3& vec); - NzVector3& operator*=(T scale); - NzVector3& operator/=(const NzVector3& vec); - NzVector3& operator/=(T scale); - - bool operator==(const NzVector3& vec) const; - bool operator!=(const NzVector3& vec) const; - bool operator<(const NzVector3& vec) const; - bool operator<=(const NzVector3& vec) const; - bool operator>(const NzVector3& vec) const; - bool operator>=(const NzVector3& vec) const; - - static NzVector3 UnitX(); - static NzVector3 UnitY(); - static NzVector3 UnitZ(); - static NzVector3 Zero(); - - T x, y, z; -}; - -template std::ostream& operator<<(std::ostream& out, const NzVector3& vec); - -template NzVector3 operator*(T scale, const NzVector3& vec); -template NzVector3 operator/(T scale, const NzVector3& vec); - -typedef NzVector3 NzVector3d; -typedef NzVector3 NzVector3f; -typedef NzVector3 NzVector3i; -typedef NzVector3 NzVector3ui; - -#include - -#endif // NAZARA_VECTOR3_HPP +// Copyright (C) 2012 Rémi Bèges - Jérôme Leclercq +// This file is part of the "Nazara Engine - Mathematics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_VECTOR3_HPP +#define NAZARA_VECTOR3_HPP + +#include +#include + +template class NzVector3 +{ + public: + NzVector3(); + NzVector3(T X, T Y, T Z); + explicit NzVector3(T scale); + NzVector3(T vec[3]); + NzVector3(const NzVector2& vec, T Z = 0.0); + template explicit NzVector3(const NzVector3& vec); + NzVector3(const NzVector3& vec) = default; + ~NzVector3() = default; + + T AbsDotProduct(const NzVector3& vec) const; + + NzVector3 CrossProduct(const NzVector3& vec) const; + + T Distance(const NzVector3& vec) const; + float Distancef(const NzVector3& vec) const; + + T DotProduct(const NzVector3& vec) const; + + NzVector3 GetNormal() const; + + T Length() const; + float Lengthf() const; + + void MakeForward(); + void MakeLeft(); + void MakeUnitX(); + void MakeUnitY(); + void MakeUnitZ(); + void MakeUp(); + void MakeZero(); + + void Maximize(const NzVector3& vec); + void Minimize(const NzVector3& vec); + + void Normalize(); + + void Set(T X, T Y, T Z); + void Set(T scale); + void Set(T vec[3]); + void Set(const NzVector2& vec, T Z = 0.0); + template void Set(const NzVector3& vec); + + T SquaredDistance(const NzVector3& vec) const; + T SquaredLength() const; + + NzString ToString() const; + + operator NzString() const; + + operator T*(); + operator const T*() const; + + T& operator[](unsigned int i); + T operator[](unsigned int i) const; + + const NzVector3& operator+() const; + NzVector3 operator-() const; + + NzVector3 operator+(const NzVector3& vec) const; + NzVector3 operator-(const NzVector3& vec) const; + NzVector3 operator*(const NzVector3& vec) const; + NzVector3 operator*(T scale) const; + NzVector3 operator/(const NzVector3& vec) const; + NzVector3 operator/(T scale) const; + + NzVector3& operator+=(const NzVector3& vec); + NzVector3& operator-=(const NzVector3& vec); + NzVector3& operator*=(const NzVector3& vec); + NzVector3& operator*=(T scale); + NzVector3& operator/=(const NzVector3& vec); + NzVector3& operator/=(T scale); + + bool operator==(const NzVector3& vec) const; + bool operator!=(const NzVector3& vec) const; + bool operator<(const NzVector3& vec) const; + bool operator<=(const NzVector3& vec) const; + bool operator>(const NzVector3& vec) const; + bool operator>=(const NzVector3& vec) const; + + static NzVector3 CrossProduct(const NzVector3& vec1, const NzVector3& vec2); + static T DotProduct(const NzVector3& vec1, const NzVector3& vec2); + static NzVector3 Forward(); + static NzVector3 Left(); + static NzVector3 Normalize(const NzVector3& vec); + static NzVector3 UnitX(); + static NzVector3 UnitY(); + static NzVector3 UnitZ(); + static NzVector3 Up(); + static NzVector3 Zero(); + + T x, y, z; +}; + +template std::ostream& operator<<(std::ostream& out, const NzVector3& vec); + +template NzVector3 operator*(T scale, const NzVector3& vec); +template NzVector3 operator/(T scale, const NzVector3& vec); + +typedef NzVector3 NzVector3d; +typedef NzVector3 NzVector3f; +typedef NzVector3 NzVector3i; +typedef NzVector3 NzVector3ui; + +#include + +#endif // NAZARA_VECTOR3_HPP diff --git a/include/Nazara/Math/Vector3.inl b/include/Nazara/Math/Vector3.inl index 671b3898e..294aceaac 100644 --- a/include/Nazara/Math/Vector3.inl +++ b/include/Nazara/Math/Vector3.inl @@ -1,536 +1,599 @@ -// Copyright (C) 2012 Rémi Bèges - Jérôme Leclercq -// This file is part of the "Nazara Engine - Mathematics module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include -#include - -#define F(a) static_cast(a) - -template -NzVector3::NzVector3() -{ -} - -template -NzVector3::NzVector3(T X, T Y, T Z) -{ - Set(X, Y, Z); -} - -template -NzVector3::NzVector3(T scale) -{ - Set(scale); -} - -template -NzVector3::NzVector3(T vec[3]) -{ - Set(vec); -} - -template -NzVector3::NzVector3(const NzVector2& vec, T Z) -{ - Set(vec, Z); -} - -template -template -NzVector3::NzVector3(const NzVector3& vec) -{ - Set(vec); -} - -template -T NzVector3::AbsDotProduct(const NzVector3& vec) const -{ - return std::fabs(x * vec.x) + std::fabs(y * vec.y) + std::fabs(z * vec.z); -} - -template<> -inline int NzVector3::AbsDotProduct(const NzVector3& vec) const -{ - return std::labs(x * vec.x) + std::labs(y * vec.y) + std::labs(z * vec.z); -} - -template<> -inline unsigned int NzVector3::AbsDotProduct(const NzVector3& vec) const -{ - return std::labs(x * vec.x) + std::labs(y * vec.y) + std::labs(z * vec.z); -} - -template -NzVector3 NzVector3::CrossProduct(const NzVector3& vec) const -{ - return NzVector3(y * vec.z - z * vec.y, z * vec.x - x * vec.y, x * vec.y - y * vec.x); -} - -template -T NzVector3::Distance(const NzVector3& vec) const -{ - return std::sqrt(SquaredDistance(vec)); -} - -template -float NzVector3::Distancef(const NzVector3& vec) const -{ - return std::sqrt(static_cast(SquaredDistance())); -} - -template -T NzVector3::DotProduct(const NzVector3& vec) const -{ - return x*vec.x + y*vec.y + z*vec.z; -} - -template -NzVector3 NzVector3::GetNormal() const -{ - NzVector3 vec(*this); - vec.Normalize(); - - return vec; -} - -template -void NzVector3::MakeCeil(const NzVector3& vec) -{ - if (vec.x > x) - x = vec.x; - - if (vec.y > y) - y = vec.y; - - if (vec.z > z) - z = vec.z; -} - -template -void NzVector3::MakeFloor(const NzVector3& vec) -{ - if (vec.x < x) - x = vec.x; - - if (vec.y < y) - y = vec.y; - - if (vec.z < z) - z = vec.z; -} - -template -void NzVector3::MakeUnitX() -{ - Set(F(1.0), F(0.0), F(0.0)); -} - -template -void NzVector3::MakeUnitY() -{ - Set(F(0.0), F(1.0), F(0.0)); -} - -template -void NzVector3::MakeUnitZ() -{ - Set(F(0.0), F(0.0), F(1.0)); -} - -template -void NzVector3::MakeZero() -{ - Set(F(0.0), F(0.0), F(0.0)); -} - -template -T NzVector3::Length() const -{ - return std::sqrt(SquaredLength()); -} - -template -float NzVector3::Lengthf() const -{ - return std::sqrt(static_cast(SquaredLength())); -} - -template -void NzVector3::Normalize() -{ - T squaredLength = SquaredLength(); - - if (squaredLength-F(1.0) > std::numeric_limits::epsilon()) - { - T length = std::sqrt(squaredLength); - - x /= length; - y /= length; - z /= length; - } -} - -template -void NzVector3::Set(T X, T Y, T Z) -{ - x = X; - y = Y; - z = Z; -} - -template -void NzVector3::Set(T scale) -{ - x = scale; - y = scale; - z = scale; -} - -template -void NzVector3::Set(T vec[3]) -{ - std::memcpy(&x, vec, 3*sizeof(T)); -} - -template -void NzVector3::Set(const NzVector2& vec, T Z) -{ - x = vec.x; - y = vec.y; - z = Z; -} - -template -template -void NzVector3::Set(const NzVector3& vec) -{ - x = F(vec.x); - y = F(vec.y); - z = F(vec.z); -} - -template -T NzVector3::SquaredDistance(const NzVector3& vec) const -{ - return operator-(vec).SquaredLength(); -} - -template -T NzVector3::SquaredLength() const -{ - return x*x + y*y + z*z; -} - -template -NzString NzVector3::ToString() const -{ - NzStringStream ss; - - return ss << "Vector3(" << x << ", " << y << ", " << z <<')'; -} - -template -NzVector3::operator NzString() const -{ - return ToString(); -} - -template -NzVector3::operator T*() -{ - return &x; -} - -template -NzVector3::operator const T*() const -{ - return &x; -} - -template -T& NzVector3::operator[](unsigned int i) -{ - #if NAZARA_MATH_SAFE - if (i >= 3) - { - NzStringStream ss; - ss << __FILE__ << ':' << __LINE__ << ": Index out of range (" << i << " >= 3)"; - - throw std::out_of_range(ss.ToString()); - } - #endif - - return *(&x+i); -} - -template -T NzVector3::operator[](unsigned int i) const -{ - #if NAZARA_MATH_SAFE - if (i >= 3) - { - NzStringStream ss; - ss << __FILE__ << ':' << __LINE__ << ": Index out of range (" << i << " >= 3)"; - - throw std::out_of_range(ss.ToString()); - } - #endif - - return *(&x+i); -} - -template -const NzVector3& NzVector3::operator+() const -{ - return *this; -} - -template -NzVector3 NzVector3::operator-() const -{ - return NzVector3(-x, -y, -z); -} - -template -NzVector3 NzVector3::operator+(const NzVector3& vec) const -{ - return NzVector3(x + vec.x, y + vec.y, z + vec.z); -} - -template -NzVector3 NzVector3::operator-(const NzVector3& vec) const -{ - return NzVector3(x - vec.x, y - vec.y, z - vec.z); -} - -template -NzVector3 NzVector3::operator*(const NzVector3& vec) const -{ - return NzVector3(x * vec.x, y * vec.y, z * vec.z); -} - -template -NzVector3 NzVector3::operator*(T scale) const -{ - return NzVector3(x * scale, y * scale, z * scale); -} - -template -NzVector3 NzVector3::operator/(const NzVector3& vec) const -{ - #if NAZARA_MATH_SAFE - if (NzNumberEquals(vec.x, F(0.0)) || NzNumberEquals(vec.y, F(0.0)) || NzNumberEquals(vec.z, F(0.0))) - { - NzStringStream ss; - ss << __FILE__ << ':' << __LINE__ << ": Division by zero"; - - throw std::domain_error(ss.ToString()); - } - #endif - - return NzVector3(x / vec.x, y / vec.y, z / vec.z); -} - -template -NzVector3 NzVector3::operator/(T scale) const -{ - #if NAZARA_MATH_SAFE - if (NzNumberEquals(scale, F(0.0))) - { - NzStringStream ss; - ss << __FILE__ << ':' << __LINE__ << ": Division by zero"; - - throw std::domain_error(ss.ToString()); - } - #endif - - return NzVector3(x / scale, y / scale, z / scale); -} - -template -NzVector3& NzVector3::operator+=(const NzVector3& vec) -{ - x += vec.x; - y += vec.y; - z += vec.z; - - return *this; -} - -template -NzVector3& NzVector3::operator-=(const NzVector3& vec) -{ - x -= vec.x; - y -= vec.y; - z -= vec.z; - - return *this; -} - -template -NzVector3& NzVector3::operator*=(const NzVector3& vec) -{ - x *= vec.x; - y *= vec.y; - z *= vec.z; - - return *this; -} - -template -NzVector3& NzVector3::operator*=(T scale) -{ - x *= scale; - y *= scale; - z *= scale; - - return *this; -} - -template -NzVector3& NzVector3::operator/=(const NzVector3& vec) -{ - if (NzNumberEquals(vec.x, F(0.0)) || NzNumberEquals(vec.y, F(0.0)) || NzNumberEquals(vec.z, F(0.0))) - { - NzStringStream ss; - ss << __FILE__ << ':' << __LINE__ << ": Division by zero"; - - throw std::domain_error(ss.ToString()); - } - - x /= vec.x; - y /= vec.y; - z /= vec.z; - - return *this; -} - -template -NzVector3& NzVector3::operator/=(T scale) -{ - if (NzNumberEquals(scale, F(0.0))) - { - NzStringStream ss; - ss << __FILE__ << ':' << __LINE__ << ": Division by zero"; - - throw std::domain_error(ss.ToString()); - } - - x /= scale; - y /= scale; - z /= scale; - - return *this; -} - -template -bool NzVector3::operator==(const NzVector3& vec) const -{ - return NzNumberEquals(x, vec.x) && - NzNumberEquals(y, vec.y) && - NzNumberEquals(z, vec.z); -} - -template -bool NzVector3::operator!=(const NzVector3& vec) const -{ - return !operator==(vec); -} - -template -bool NzVector3::operator<(const NzVector3& vec) const -{ - return x < vec.x && y < vec.y && z < vec.z; -} - -template -bool NzVector3::operator<=(const NzVector3& vec) const -{ - return operator<(vec) || operator==(vec); -} - -template -bool NzVector3::operator>(const NzVector3& vec) const -{ - return !operator<=(vec); -} - -template -bool NzVector3::operator>=(const NzVector3& vec) const -{ - return !operator<(vec); -} - -template -NzVector3 NzVector3::UnitX() -{ - NzVector3 vector; - vector.MakeUnitX(); - - return vector; -} - -template -NzVector3 NzVector3::UnitY() -{ - NzVector3 vector; - vector.MakeUnitY(); - - return vector; -} - -template -NzVector3 NzVector3::UnitZ() -{ - NzVector3 vector; - vector.MakeUnitZ(); - - return vector; -} - -template -NzVector3 NzVector3::Zero() -{ - NzVector3 vector; - vector.MakeZero(); - - return vector; -} - -template -std::ostream& operator<<(std::ostream& out, const NzVector3& vec) -{ - return out << vec.ToString(); -} - -template -NzVector3 operator*(T scale, const NzVector3& vec) -{ - return NzVector3(scale * vec.x, scale * vec.y, scale * vec.z); -} - -template -NzVector3 operator/(T scale, const NzVector3& vec) -{ - #if NAZARA_MATH_SAFE - if (NzNumberEquals(vec.x, F(0.0)) || NzNumberEquals(vec.y, F(0.0)) || NzNumberEquals(vec.z, F(0.0))) - { - NzStringStream ss; - ss << __FILE__ << ':' << __LINE__ << ": Division by zero"; - - throw std::domain_error(ss.ToString()); - } - #endif - - return NzVector3(scale / vec.x, scale / vec.y, scale / vec.z); -} - -#undef F - -#include +// Copyright (C) 2012 Rémi Bèges - Jérôme Leclercq +// This file is part of the "Nazara Engine - Mathematics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include + +#define F(a) static_cast(a) + +template +NzVector3::NzVector3() +{ +} + +template +NzVector3::NzVector3(T X, T Y, T Z) +{ + Set(X, Y, Z); +} + +template +NzVector3::NzVector3(T scale) +{ + Set(scale); +} + +template +NzVector3::NzVector3(T vec[3]) +{ + Set(vec); +} + +template +NzVector3::NzVector3(const NzVector2& vec, T Z) +{ + Set(vec, Z); +} + +template +template +NzVector3::NzVector3(const NzVector3& vec) +{ + Set(vec); +} + +template +T NzVector3::AbsDotProduct(const NzVector3& vec) const +{ + return std::fabs(x * vec.x) + std::fabs(y * vec.y) + std::fabs(z * vec.z); +} + +template<> +inline int NzVector3::AbsDotProduct(const NzVector3& vec) const +{ + return std::labs(x * vec.x) + std::labs(y * vec.y) + std::labs(z * vec.z); +} + +template<> +inline unsigned int NzVector3::AbsDotProduct(const NzVector3& vec) const +{ + return std::labs(x * vec.x) + std::labs(y * vec.y) + std::labs(z * vec.z); +} + +template +NzVector3 NzVector3::CrossProduct(const NzVector3& vec) const +{ + return NzVector3(y * vec.z - z * vec.y, z * vec.x - x * vec.z, x * vec.y - y * vec.x); +} + +template +T NzVector3::Distance(const NzVector3& vec) const +{ + return std::sqrt(SquaredDistance(vec)); +} + +template +float NzVector3::Distancef(const NzVector3& vec) const +{ + return std::sqrt(static_cast(SquaredDistance())); +} + +template +T NzVector3::DotProduct(const NzVector3& vec) const +{ + return x*vec.x + y*vec.y + z*vec.z; +} + +template +NzVector3 NzVector3::GetNormal() const +{ + NzVector3 vec(*this); + vec.Normalize(); + + return vec; +} + +template +T NzVector3::Length() const +{ + return std::sqrt(SquaredLength()); +} + +template +float NzVector3::Lengthf() const +{ + return std::sqrt(static_cast(SquaredLength())); +} + +template +void NzVector3::MakeForward() +{ + Set(F(0.0), F(0.0), F(-1.0)); +} + +template +void NzVector3::MakeLeft() +{ + Set(F(-1.0), F(0.0), F(0.0)); +} + +template +void NzVector3::MakeUnitX() +{ + Set(F(1.0), F(0.0), F(0.0)); +} + +template +void NzVector3::MakeUnitY() +{ + Set(F(0.0), F(1.0), F(0.0)); +} + +template +void NzVector3::MakeUnitZ() +{ + Set(F(0.0), F(0.0), F(1.0)); +} + +template +void NzVector3::MakeUp() +{ + Set(F(0.0), F(1.0), F(0.0)); +} + +template +void NzVector3::MakeZero() +{ + Set(F(0.0), F(0.0), F(0.0)); +} + +template +void NzVector3::Maximize(const NzVector3& vec) +{ + if (vec.x > x) + x = vec.x; + + if (vec.y > y) + y = vec.y; + + if (vec.z > z) + z = vec.z; +} + +template +void NzVector3::Minimize(const NzVector3& vec) +{ + if (vec.x < x) + x = vec.x; + + if (vec.y < y) + y = vec.y; + + if (vec.z < z) + z = vec.z; +} + +template +void NzVector3::Normalize() +{ + T squaredLength = SquaredLength(); + + if (!NzNumberEquals(squaredLength, F(1.0))) + { + T invLength = F(1.0) / std::sqrt(squaredLength); + + x *= invLength; + y *= invLength; + z *= invLength; + } +} + +template +void NzVector3::Set(T X, T Y, T Z) +{ + x = X; + y = Y; + z = Z; +} + +template +void NzVector3::Set(T scale) +{ + x = scale; + y = scale; + z = scale; +} + +template +void NzVector3::Set(T vec[3]) +{ + std::memcpy(&x, vec, 3*sizeof(T)); +} + +template +void NzVector3::Set(const NzVector2& vec, T Z) +{ + x = vec.x; + y = vec.y; + z = Z; +} + +template +template +void NzVector3::Set(const NzVector3& vec) +{ + x = F(vec.x); + y = F(vec.y); + z = F(vec.z); +} + +template +T NzVector3::SquaredDistance(const NzVector3& vec) const +{ + return operator-(vec).SquaredLength(); +} + +template +T NzVector3::SquaredLength() const +{ + return x*x + y*y + z*z; +} + +template +NzString NzVector3::ToString() const +{ + NzStringStream ss; + + return ss << "Vector3(" << x << ", " << y << ", " << z <<')'; +} + +template +NzVector3::operator NzString() const +{ + return ToString(); +} + +template +NzVector3::operator T*() +{ + return &x; +} + +template +NzVector3::operator const T*() const +{ + return &x; +} + +template +T& NzVector3::operator[](unsigned int i) +{ + #if NAZARA_MATH_SAFE + if (i >= 3) + { + NzStringStream ss; + ss << __FILE__ << ':' << __LINE__ << ": Index out of range (" << i << " >= 3)"; + + throw std::out_of_range(ss.ToString()); + } + #endif + + return *(&x+i); +} + +template +T NzVector3::operator[](unsigned int i) const +{ + #if NAZARA_MATH_SAFE + if (i >= 3) + { + NzStringStream ss; + ss << __FILE__ << ':' << __LINE__ << ": Index out of range (" << i << " >= 3)"; + + throw std::out_of_range(ss.ToString()); + } + #endif + + return *(&x+i); +} + +template +const NzVector3& NzVector3::operator+() const +{ + return *this; +} + +template +NzVector3 NzVector3::operator-() const +{ + return NzVector3(-x, -y, -z); +} + +template +NzVector3 NzVector3::operator+(const NzVector3& vec) const +{ + return NzVector3(x + vec.x, y + vec.y, z + vec.z); +} + +template +NzVector3 NzVector3::operator-(const NzVector3& vec) const +{ + return NzVector3(x - vec.x, y - vec.y, z - vec.z); +} + +template +NzVector3 NzVector3::operator*(const NzVector3& vec) const +{ + return NzVector3(x * vec.x, y * vec.y, z * vec.z); +} + +template +NzVector3 NzVector3::operator*(T scale) const +{ + return NzVector3(x * scale, y * scale, z * scale); +} + +template +NzVector3 NzVector3::operator/(const NzVector3& vec) const +{ + #if NAZARA_MATH_SAFE + if (NzNumberEquals(vec.x, F(0.0)) || NzNumberEquals(vec.y, F(0.0)) || NzNumberEquals(vec.z, F(0.0))) + { + NzStringStream ss; + ss << __FILE__ << ':' << __LINE__ << ": Division by zero"; + + throw std::domain_error(ss.ToString()); + } + #endif + + return NzVector3(x / vec.x, y / vec.y, z / vec.z); +} + +template +NzVector3 NzVector3::operator/(T scale) const +{ + #if NAZARA_MATH_SAFE + if (NzNumberEquals(scale, F(0.0))) + { + NzStringStream ss; + ss << __FILE__ << ':' << __LINE__ << ": Division by zero"; + + throw std::domain_error(ss.ToString()); + } + #endif + + return NzVector3(x / scale, y / scale, z / scale); +} + +template +NzVector3& NzVector3::operator+=(const NzVector3& vec) +{ + x += vec.x; + y += vec.y; + z += vec.z; + + return *this; +} + +template +NzVector3& NzVector3::operator-=(const NzVector3& vec) +{ + x -= vec.x; + y -= vec.y; + z -= vec.z; + + return *this; +} + +template +NzVector3& NzVector3::operator*=(const NzVector3& vec) +{ + x *= vec.x; + y *= vec.y; + z *= vec.z; + + return *this; +} + +template +NzVector3& NzVector3::operator*=(T scale) +{ + x *= scale; + y *= scale; + z *= scale; + + return *this; +} + +template +NzVector3& NzVector3::operator/=(const NzVector3& vec) +{ + if (NzNumberEquals(vec.x, F(0.0)) || NzNumberEquals(vec.y, F(0.0)) || NzNumberEquals(vec.z, F(0.0))) + { + NzStringStream ss; + ss << __FILE__ << ':' << __LINE__ << ": Division by zero"; + + throw std::domain_error(ss.ToString()); + } + + x /= vec.x; + y /= vec.y; + z /= vec.z; + + return *this; +} + +template +NzVector3& NzVector3::operator/=(T scale) +{ + if (NzNumberEquals(scale, F(0.0))) + { + NzStringStream ss; + ss << __FILE__ << ':' << __LINE__ << ": Division by zero"; + + throw std::domain_error(ss.ToString()); + } + + x /= scale; + y /= scale; + z /= scale; + + return *this; +} + +template +bool NzVector3::operator==(const NzVector3& vec) const +{ + return NzNumberEquals(x, vec.x) && + NzNumberEquals(y, vec.y) && + NzNumberEquals(z, vec.z); +} + +template +bool NzVector3::operator!=(const NzVector3& vec) const +{ + return !operator==(vec); +} + +template +bool NzVector3::operator<(const NzVector3& vec) const +{ + return x < vec.x && y < vec.y && z < vec.z; +} + +template +bool NzVector3::operator<=(const NzVector3& vec) const +{ + return operator<(vec) || operator==(vec); +} + +template +bool NzVector3::operator>(const NzVector3& vec) const +{ + return !operator<=(vec); +} + +template +bool NzVector3::operator>=(const NzVector3& vec) const +{ + return !operator<(vec); +} + +template +NzVector3 NzVector3::CrossProduct(const NzVector3& vec1, const NzVector3& vec2) +{ + return vec1.CrossProduct(vec2); +} + +template +T NzVector3::DotProduct(const NzVector3& vec1, const NzVector3& vec2) +{ + return vec1.DotProduct(vec2); +} + +template +NzVector3 NzVector3::Forward() +{ + NzVector3 vector; + vector.MakeForward(); + + return vector; +} + +template +NzVector3 NzVector3::Left() +{ + NzVector3 vector; + vector.MakeLeft(); + + return vector; +} + +template +NzVector3 NzVector3::Normalize(const NzVector3& vec) +{ + return vec.GetNormal(); +} + +template +NzVector3 NzVector3::UnitX() +{ + NzVector3 vector; + vector.MakeUnitX(); + + return vector; +} + +template +NzVector3 NzVector3::UnitY() +{ + NzVector3 vector; + vector.MakeUnitY(); + + return vector; +} + +template +NzVector3 NzVector3::UnitZ() +{ + NzVector3 vector; + vector.MakeUnitZ(); + + return vector; +} + +template +NzVector3 NzVector3::Up() +{ + NzVector3 vector; + vector.MakeUp(); + + return vector; +} + +template +NzVector3 NzVector3::Zero() +{ + NzVector3 vector; + vector.MakeZero(); + + return vector; +} + +template +std::ostream& operator<<(std::ostream& out, const NzVector3& vec) +{ + return out << vec.ToString(); +} + +template +NzVector3 operator*(T scale, const NzVector3& vec) +{ + return NzVector3(scale * vec.x, scale * vec.y, scale * vec.z); +} + +template +NzVector3 operator/(T scale, const NzVector3& vec) +{ + #if NAZARA_MATH_SAFE + if (NzNumberEquals(vec.x, F(0.0)) || NzNumberEquals(vec.y, F(0.0)) || NzNumberEquals(vec.z, F(0.0))) + { + NzStringStream ss; + ss << __FILE__ << ':' << __LINE__ << ": Division by zero"; + + throw std::domain_error(ss.ToString()); + } + #endif + + return NzVector3(scale / vec.x, scale / vec.y, scale / vec.z); +} + +#undef F + +#include diff --git a/include/Nazara/Math/Vector4.hpp b/include/Nazara/Math/Vector4.hpp index 3987a6b76..b48502127 100644 --- a/include/Nazara/Math/Vector4.hpp +++ b/include/Nazara/Math/Vector4.hpp @@ -1,97 +1,98 @@ -// Copyright (C) 2012 Rémi Bèges - Jérôme Leclercq -// This file is part of the "Nazara Engine - Mathematics module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef NAZARA_VECTOR4_HPP -#define NAZARA_VECTOR4_HPP - -#include -#include - -template class NzVector4 -{ - public: - NzVector4(); - NzVector4(T X, T Y, T Z, T W = 1.0); - explicit NzVector4(T scale); - NzVector4(T vec[4]); - NzVector4(const NzVector3& vec, T W = 1.0); - template explicit NzVector4(const NzVector4& vec); - NzVector4(const NzVector4& vec) = default; - ~NzVector4() = default; - - T AbsDotProduct(const NzVector4& vec) const; - - T DotProduct(const NzVector4& vec) const; - - void MakeCeil(const NzVector4& vec); - void MakeFloor(const NzVector4& vec); - void MakeUnitX(); - void MakeUnitY(); - void MakeUnitZ(); - void MakeZero(); - - void Normalize(); - - void Set(T X, T Y, T Z, T W = 1.0); - void Set(T scale); - void Set(T vec[4]); - void Set(const NzVector3& vec, T W = 1.0); - template void Set(const NzVector4& vec); - - NzString ToString() const; - - operator NzString() const; - - operator T*(); - operator const T*() const; - - T& operator[](unsigned int i); - T operator[](unsigned int i) const; - - const NzVector4& operator+() const; - NzVector4 operator-() const; - - NzVector4 operator+(const NzVector4& vec) const; - NzVector4 operator-(const NzVector4& vec) const; - NzVector4 operator*(const NzVector4& vec) const; - NzVector4 operator*(T scale) const; - NzVector4 operator/(const NzVector4& vec) const; - NzVector4 operator/(T scale) const; - - NzVector4& operator+=(const NzVector4& vec); - NzVector4& operator-=(const NzVector4& vec); - NzVector4& operator*=(const NzVector4& vec); - NzVector4& operator*=(T scale); - NzVector4& operator/=(const NzVector4& vec); - NzVector4& operator/=(T scale); - - bool operator==(const NzVector4& vec) const; - bool operator!=(const NzVector4& vec) const; - bool operator<(const NzVector4& vec) const; - bool operator<=(const NzVector4& vec) const; - bool operator>(const NzVector4& vec) const; - bool operator>=(const NzVector4& vec) const; - - static NzVector4 UnitX(); - static NzVector4 UnitY(); - static NzVector4 UnitZ(); - static NzVector4 Zero(); - - T x, y, z, w; -}; - -template std::ostream& operator<<(std::ostream& out, const NzVector4& vec); - -template NzVector4 operator*(T scale, const NzVector4& vec); -template NzVector4 operator/(T scale, const NzVector4& vec); - -typedef NzVector4 NzVector4d; -typedef NzVector4 NzVector4f; -typedef NzVector4 NzVector4i; - -#include - -#endif // NAZARA_VECTOR4_HPP +// Copyright (C) 2012 Rémi Bèges - Jérôme Leclercq +// This file is part of the "Nazara Engine - Mathematics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_VECTOR4_HPP +#define NAZARA_VECTOR4_HPP + +#include +#include + +template class NzVector4 +{ + public: + NzVector4(); + NzVector4(T X, T Y, T Z, T W = 1.0); + explicit NzVector4(T scale); + NzVector4(T vec[4]); + NzVector4(const NzVector3& vec, T W = 1.0); + template explicit NzVector4(const NzVector4& vec); + NzVector4(const NzVector4& vec) = default; + ~NzVector4() = default; + + T AbsDotProduct(const NzVector4& vec) const; + + T DotProduct(const NzVector4& vec) const; + + void MakeUnitX(); + void MakeUnitY(); + void MakeUnitZ(); + void MakeZero(); + + void Maximize(const NzVector4& vec); + void Minimize(const NzVector4& vec); + + void Normalize(); + + void Set(T X, T Y, T Z, T W = 1.0); + void Set(T scale); + void Set(T vec[4]); + void Set(const NzVector3& vec, T W = 1.0); + template void Set(const NzVector4& vec); + + NzString ToString() const; + + operator NzString() const; + + operator T*(); + operator const T*() const; + + T& operator[](unsigned int i); + T operator[](unsigned int i) const; + + const NzVector4& operator+() const; + NzVector4 operator-() const; + + NzVector4 operator+(const NzVector4& vec) const; + NzVector4 operator-(const NzVector4& vec) const; + NzVector4 operator*(const NzVector4& vec) const; + NzVector4 operator*(T scale) const; + NzVector4 operator/(const NzVector4& vec) const; + NzVector4 operator/(T scale) const; + + NzVector4& operator+=(const NzVector4& vec); + NzVector4& operator-=(const NzVector4& vec); + NzVector4& operator*=(const NzVector4& vec); + NzVector4& operator*=(T scale); + NzVector4& operator/=(const NzVector4& vec); + NzVector4& operator/=(T scale); + + bool operator==(const NzVector4& vec) const; + bool operator!=(const NzVector4& vec) const; + bool operator<(const NzVector4& vec) const; + bool operator<=(const NzVector4& vec) const; + bool operator>(const NzVector4& vec) const; + bool operator>=(const NzVector4& vec) const; + + static NzVector4 UnitX(); + static NzVector4 UnitY(); + static NzVector4 UnitZ(); + static NzVector4 Zero(); + + T x, y, z, w; +}; + +template std::ostream& operator<<(std::ostream& out, const NzVector4& vec); + +template NzVector4 operator*(T scale, const NzVector4& vec); +template NzVector4 operator/(T scale, const NzVector4& vec); + +typedef NzVector4 NzVector4d; +typedef NzVector4 NzVector4f; +typedef NzVector4 NzVector4i; + +#include + +#endif // NAZARA_VECTOR4_HPP diff --git a/include/Nazara/Math/Vector4.inl b/include/Nazara/Math/Vector4.inl index 18e915e96..e3245c0ac 100644 --- a/include/Nazara/Math/Vector4.inl +++ b/include/Nazara/Math/Vector4.inl @@ -1,503 +1,503 @@ -// Copyright (C) 2012 Rémi Bèges - Jérôme Leclercq -// This file is part of the "Nazara Engine - Mathematics module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include - -///FIXME: Les calculs effectués ici sont probablements tous faux, la composante W étant spéciale dans le monde de la 3D - -#define F(a) static_cast(a) - -template -NzVector4::NzVector4() -{ -} - -template -NzVector4::NzVector4(T X, T Y, T Z, T W) -{ - Set(X, Y, Z, W); -} - -template -NzVector4::NzVector4(T scale) -{ - Set(scale); -} - -template -NzVector4::NzVector4(T vec[4]) -{ - Set(vec); -} - -template -NzVector4::NzVector4(const NzVector3& vec, T W) -{ - Set(vec, W); -} - -template -template -NzVector4::NzVector4(const NzVector4& vec) -{ - Set(vec); -} - -template -T NzVector4::AbsDotProduct(const NzVector4& vec) const -{ - return std::fabs(x * vec.x) + std::fabs(y * vec.y) + std::fabs(z * vec.z) + std::fabs(w * vec.w); -} - -template<> -inline int NzVector4::AbsDotProduct(const NzVector4& vec) const -{ - return std::labs(x * vec.x) + std::labs(y * vec.y) + std::labs(z * vec.z) + std::labs(w * vec.w); -} - -template<> -inline unsigned int NzVector4::AbsDotProduct(const NzVector4& vec) const -{ - return std::labs(x * vec.x) + std::labs(y * vec.y) + std::labs(z * vec.z) + std::labs(w * vec.w); -} - -template -T NzVector4::DotProduct(const NzVector4& vec) const -{ - return x*vec.x + y*vec.y + z*vec.z + w*vec.w; -} - -template -void NzVector4::MakeCeil(const NzVector4& vec) -{ - if (vec.x > x) - x = vec.x; - - if (vec.y > y) - y = vec.y; - - if (vec.z > z) - z = vec.z; - - if (vec.w > w) - w = vec.w; -} - -template -void NzVector4::MakeFloor(const NzVector4& vec) -{ - if (vec.x < x) - x = vec.x; - - if (vec.y < y) - y = vec.y; - - if (vec.z < z) - z = vec.z; - - if (vec.w < w) - w = vec.w; -} - -template -void NzVector4::MakeUnitX() -{ - Set(F(1.0), F(0.0), F(0.0), F(1.0)); -} - -template -void NzVector4::MakeUnitY() -{ - Set(F(0.0), F(1.0), F(0.0), F(1.0)); -} - -template -void NzVector4::MakeUnitZ() -{ - Set(F(0.0), F(0.0), F(1.0), F(1.0)); -} - -template -void NzVector4::MakeZero() -{ - Set(F(0.0), F(0.0), F(0.0), F(0.0)); -} - -template -void NzVector4::Normalize() -{ - if (!NzNumberEquals(w, F(0.0))) - { - x /= w; - y /= w; - z /= w; - } -} - -template -void NzVector4::Set(T X, T Y, T Z, T W) -{ - w = W; - x = X; - y = Y; - z = Z; -} - -template -void NzVector4::Set(T scale) -{ - w = scale; - x = scale; - y = scale; - z = scale; -} - -template -void NzVector4::Set(T vec[4]) -{ - std::memcpy(&x, vec, 4*sizeof(T)); -} - -template -void NzVector4::Set(const NzVector3& vec, T W) -{ - w = W; - x = vec.x; - y = vec.y; - z = vec.z; -} - -template -template -void NzVector4::Set(const NzVector4& vec) -{ - w = F(vec.w); - x = F(vec.x); - y = F(vec.y); - z = F(vec.z); -} - -template -NzString NzVector4::ToString() const -{ - NzStringStream ss; - - return ss << "Vector4(" << x << ", " << y << ", " << z << ", " << w << ')'; -} - -template -NzVector4::operator NzString() const -{ - return ToString(); -} - -template -NzVector4::operator T*() -{ - return &x; -} - -template -NzVector4::operator const T*() const -{ - return &x; -} - -template -T& NzVector4::operator[](unsigned int i) -{ - #if NAZARA_MATH_SAFE - if (i >= 4) - { - NzStringStream ss; - ss << __FILE__ << ':' << __LINE__ << ": Index out of range (" << i << " >= 4)"; - - throw std::domain_error(ss.ToString()); - } - #endif - - return *(&x+i); -} - -template -T NzVector4::operator[](unsigned int i) const -{ - #if NAZARA_MATH_SAFE - if (i >= 4) - { - NzStringStream ss; - ss << __FILE__ << ':' << __LINE__ << ": Index out of range (" << i << " >= 4)"; - - throw std::domain_error(ss.ToString()); - } - #endif - - return *(&x+i); -} - -template -const NzVector4& NzVector4::operator+() const -{ - return *this; -} - -template -NzVector4 NzVector4::operator-() const -{ - return NzVector4(-x, -y, -z, -w); -} - -template -NzVector4 NzVector4::operator+(const NzVector4& vec) const -{ - return NzVector4(x + vec.x, y + vec.y, z + vec.z, w + vec.w); -} - -template -NzVector4 NzVector4::operator-(const NzVector4& vec) const -{ - return NzVector4(x - vec.x, y - vec.y, z - vec.z, w - vec.w); -} - -template -NzVector4 NzVector4::operator*(const NzVector4& vec) const -{ - return NzVector4(x * vec.x, y * vec.y, z * vec.z, w * vec.w); -} - -template -NzVector4 NzVector4::operator*(T scale) const -{ - return NzVector4(x * scale, y * scale, z * scale, w * scale); -} - -template -NzVector4 NzVector4::operator/(const NzVector4& vec) const -{ - #if NAZARA_MATH_SAFE - if (NzNumberEquals(vec.x, F(0.0)) || NzNumberEquals(vec.y, F(0.0)) || NzNumberEquals(vec.z, F(0.0)) || NzNumberEquals(vec.w, F(0.0))) - { - NzStringStream ss; - ss << __FILE__ << ':' << __LINE__ << ": Division by zero"; - - throw std::domain_error(ss.ToString()); - } - #endif - - return NzVector4(x / vec.x, y / vec.y, z / vec.z, w / vec.w); -} - -template -NzVector4 NzVector4::operator/(T scale) const -{ - #if NAZARA_MATH_SAFE - if (NzNumberEquals(scale, F(0.0))) - { - NzStringStream ss; - ss << __FILE__ << ':' << __LINE__ << ": Division by zero"; - - throw std::domain_error(ss.ToString()); - } - #endif - - return NzVector4(x / scale, y / scale, z / scale, w / scale); -} - -template -NzVector4& NzVector4::operator+=(const NzVector4& vec) -{ - x += vec.x; - y += vec.y; - z += vec.z; - w += vec.w; - - return *this; -} - -template -NzVector4& NzVector4::operator-=(const NzVector4& vec) -{ - x -= vec.x; - y -= vec.y; - z -= vec.z; - w -= vec.w; - - return *this; -} - -template -NzVector4& NzVector4::operator*=(const NzVector4& vec) -{ - x *= vec.x; - y *= vec.y; - z *= vec.z; - w *= vec.w; - - return *this; -} - -template -NzVector4& NzVector4::operator*=(T scale) -{ - x *= scale; - y *= scale; - z *= scale; - w *= scale; - - return *this; -} - -template -NzVector4& NzVector4::operator/=(const NzVector4& vec) -{ - #if NAZARA_MATH_SAFE - if (NzNumberEquals(vec.x, F(0.0)) || NzNumberEquals(vec.y, F(0.0)) || NzNumberEquals(vec.z, F(0.0)) || NzNumberEquals(vec.w, F(0.0))) - { - NzStringStream ss; - ss << __FILE__ << ':' << __LINE__ << ": Division by zero"; - - throw std::domain_error(ss.ToString()); - } - #endif - - x /= vec.x; - y /= vec.y; - z /= vec.z; - w /= vec.w; - - return *this; -} - -template -NzVector4& NzVector4::operator/=(T scale) -{ - #if NAZARA_MATH_SAFE - if (NzNumberEquals(scale, F(0.0))) - { - NzStringStream ss; - ss << __FILE__ << ':' << __LINE__ << ": Division by zero"; - - throw std::domain_error(ss.ToString()); - } - #endif - - x /= scale; - y /= scale; - z /= scale; - w /= scale; - - return *this; -} - -template -bool NzVector4::operator==(const NzVector4& vec) const -{ - return NzNumberEquals(x, vec.x) && - NzNumberEquals(y, vec.y) && - NzNumberEquals(z, vec.z) && - NzNumberEquals(w, vec.w); -} - -template -bool NzVector4::operator!=(const NzVector4& vec) const -{ - return !operator==(vec); -} - -template -bool NzVector4::operator<(const NzVector4& vec) const -{ - return x < vec.x && y < vec.y && z < vec.z && w < vec.w; -} - -template -bool NzVector4::operator<=(const NzVector4& vec) const -{ - return operator<(vec) || operator==(vec); -} - -template -bool NzVector4::operator>(const NzVector4& vec) const -{ - return !operator<=(vec); -} - -template -bool NzVector4::operator>=(const NzVector4& vec) const -{ - return !operator<(vec); -} - -template -NzVector4 NzVector4::UnitX() -{ - NzVector4 vector; - vector.MakeUnitX(); - - return vector; -} - -template -NzVector4 NzVector4::UnitY() -{ - NzVector4 vector; - vector.MakeUnitY(); - - return vector; -} - -template -NzVector4 NzVector4::UnitZ() -{ - NzVector4 vector; - vector.MakeUnitZ(); - - return vector; -} - -template -NzVector4 NzVector4::Zero() -{ - NzVector4 vector; - vector.MakeZero(); - - return vector; -} - -template -std::ostream& operator<<(std::ostream& out, const NzVector4& vec) -{ - return out << vec.ToString(); -} - -template -NzVector4 operator*(T scale, const NzVector4& vec) -{ - return NzVector4(scale * vec.x, scale * vec.y, scale * vec.z, scale * vec.w); -} - -template -NzVector4 operator/(T scale, const NzVector4& vec) -{ - #if NAZARA_MATH_SAFE - if (NzNumberEquals(vec.x, F(0.0)) || NzNumberEquals(vec.y, F(0.0)) || NzNumberEquals(vec.z, F(0.0)) || NzNumberEquals(vec.w, F(0.0))) - { - NzStringStream ss; - ss << __FILE__ << ':' << __LINE__ << ": Division by zero"; - - throw std::domain_error(ss.ToString()); - } - #endif - - return NzVector4(scale / vec.x, scale / vec.y, scale / vec.z, scale / vec.w); -} - -#undef F - -#include +// Copyright (C) 2012 Rémi Bèges - Jérôme Leclercq +// This file is part of the "Nazara Engine - Mathematics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include + +///FIXME: Les calculs effectués ici sont probablements tous faux, la composante W étant spéciale dans le monde de la 3D + +#define F(a) static_cast(a) + +template +NzVector4::NzVector4() +{ +} + +template +NzVector4::NzVector4(T X, T Y, T Z, T W) +{ + Set(X, Y, Z, W); +} + +template +NzVector4::NzVector4(T scale) +{ + Set(scale); +} + +template +NzVector4::NzVector4(T vec[4]) +{ + Set(vec); +} + +template +NzVector4::NzVector4(const NzVector3& vec, T W) +{ + Set(vec, W); +} + +template +template +NzVector4::NzVector4(const NzVector4& vec) +{ + Set(vec); +} + +template +T NzVector4::AbsDotProduct(const NzVector4& vec) const +{ + return std::fabs(x * vec.x) + std::fabs(y * vec.y) + std::fabs(z * vec.z) + std::fabs(w * vec.w); +} + +template<> +inline int NzVector4::AbsDotProduct(const NzVector4& vec) const +{ + return std::labs(x * vec.x) + std::labs(y * vec.y) + std::labs(z * vec.z) + std::labs(w * vec.w); +} + +template<> +inline unsigned int NzVector4::AbsDotProduct(const NzVector4& vec) const +{ + return std::labs(x * vec.x) + std::labs(y * vec.y) + std::labs(z * vec.z) + std::labs(w * vec.w); +} + +template +T NzVector4::DotProduct(const NzVector4& vec) const +{ + return x*vec.x + y*vec.y + z*vec.z + w*vec.w; +} + +template +void NzVector4::MakeUnitX() +{ + Set(F(1.0), F(0.0), F(0.0), F(1.0)); +} + +template +void NzVector4::MakeUnitY() +{ + Set(F(0.0), F(1.0), F(0.0), F(1.0)); +} + +template +void NzVector4::MakeUnitZ() +{ + Set(F(0.0), F(0.0), F(1.0), F(1.0)); +} + +template +void NzVector4::MakeZero() +{ + Set(F(0.0), F(0.0), F(0.0), F(0.0)); +} + +template +void NzVector4::Maximize(const NzVector4& vec) +{ + if (vec.x > x) + x = vec.x; + + if (vec.y > y) + y = vec.y; + + if (vec.z > z) + z = vec.z; + + if (vec.w > w) + w = vec.w; +} + +template +void NzVector4::Minimize(const NzVector4& vec) +{ + if (vec.x < x) + x = vec.x; + + if (vec.y < y) + y = vec.y; + + if (vec.z < z) + z = vec.z; + + if (vec.w < w) + w = vec.w; +} + +template +void NzVector4::Normalize() +{ + if (!NzNumberEquals(w, F(0.0))) + { + x /= w; + y /= w; + z /= w; + } +} + +template +void NzVector4::Set(T X, T Y, T Z, T W) +{ + w = W; + x = X; + y = Y; + z = Z; +} + +template +void NzVector4::Set(T scale) +{ + w = scale; + x = scale; + y = scale; + z = scale; +} + +template +void NzVector4::Set(T vec[4]) +{ + std::memcpy(&x, vec, 4*sizeof(T)); +} + +template +void NzVector4::Set(const NzVector3& vec, T W) +{ + w = W; + x = vec.x; + y = vec.y; + z = vec.z; +} + +template +template +void NzVector4::Set(const NzVector4& vec) +{ + w = F(vec.w); + x = F(vec.x); + y = F(vec.y); + z = F(vec.z); +} + +template +NzString NzVector4::ToString() const +{ + NzStringStream ss; + + return ss << "Vector4(" << x << ", " << y << ", " << z << ", " << w << ')'; +} + +template +NzVector4::operator NzString() const +{ + return ToString(); +} + +template +NzVector4::operator T*() +{ + return &x; +} + +template +NzVector4::operator const T*() const +{ + return &x; +} + +template +T& NzVector4::operator[](unsigned int i) +{ + #if NAZARA_MATH_SAFE + if (i >= 4) + { + NzStringStream ss; + ss << __FILE__ << ':' << __LINE__ << ": Index out of range (" << i << " >= 4)"; + + throw std::domain_error(ss.ToString()); + } + #endif + + return *(&x+i); +} + +template +T NzVector4::operator[](unsigned int i) const +{ + #if NAZARA_MATH_SAFE + if (i >= 4) + { + NzStringStream ss; + ss << __FILE__ << ':' << __LINE__ << ": Index out of range (" << i << " >= 4)"; + + throw std::domain_error(ss.ToString()); + } + #endif + + return *(&x+i); +} + +template +const NzVector4& NzVector4::operator+() const +{ + return *this; +} + +template +NzVector4 NzVector4::operator-() const +{ + return NzVector4(-x, -y, -z, -w); +} + +template +NzVector4 NzVector4::operator+(const NzVector4& vec) const +{ + return NzVector4(x + vec.x, y + vec.y, z + vec.z, w + vec.w); +} + +template +NzVector4 NzVector4::operator-(const NzVector4& vec) const +{ + return NzVector4(x - vec.x, y - vec.y, z - vec.z, w - vec.w); +} + +template +NzVector4 NzVector4::operator*(const NzVector4& vec) const +{ + return NzVector4(x * vec.x, y * vec.y, z * vec.z, w * vec.w); +} + +template +NzVector4 NzVector4::operator*(T scale) const +{ + return NzVector4(x * scale, y * scale, z * scale, w * scale); +} + +template +NzVector4 NzVector4::operator/(const NzVector4& vec) const +{ + #if NAZARA_MATH_SAFE + if (NzNumberEquals(vec.x, F(0.0)) || NzNumberEquals(vec.y, F(0.0)) || NzNumberEquals(vec.z, F(0.0)) || NzNumberEquals(vec.w, F(0.0))) + { + NzStringStream ss; + ss << __FILE__ << ':' << __LINE__ << ": Division by zero"; + + throw std::domain_error(ss.ToString()); + } + #endif + + return NzVector4(x / vec.x, y / vec.y, z / vec.z, w / vec.w); +} + +template +NzVector4 NzVector4::operator/(T scale) const +{ + #if NAZARA_MATH_SAFE + if (NzNumberEquals(scale, F(0.0))) + { + NzStringStream ss; + ss << __FILE__ << ':' << __LINE__ << ": Division by zero"; + + throw std::domain_error(ss.ToString()); + } + #endif + + return NzVector4(x / scale, y / scale, z / scale, w / scale); +} + +template +NzVector4& NzVector4::operator+=(const NzVector4& vec) +{ + x += vec.x; + y += vec.y; + z += vec.z; + w += vec.w; + + return *this; +} + +template +NzVector4& NzVector4::operator-=(const NzVector4& vec) +{ + x -= vec.x; + y -= vec.y; + z -= vec.z; + w -= vec.w; + + return *this; +} + +template +NzVector4& NzVector4::operator*=(const NzVector4& vec) +{ + x *= vec.x; + y *= vec.y; + z *= vec.z; + w *= vec.w; + + return *this; +} + +template +NzVector4& NzVector4::operator*=(T scale) +{ + x *= scale; + y *= scale; + z *= scale; + w *= scale; + + return *this; +} + +template +NzVector4& NzVector4::operator/=(const NzVector4& vec) +{ + #if NAZARA_MATH_SAFE + if (NzNumberEquals(vec.x, F(0.0)) || NzNumberEquals(vec.y, F(0.0)) || NzNumberEquals(vec.z, F(0.0)) || NzNumberEquals(vec.w, F(0.0))) + { + NzStringStream ss; + ss << __FILE__ << ':' << __LINE__ << ": Division by zero"; + + throw std::domain_error(ss.ToString()); + } + #endif + + x /= vec.x; + y /= vec.y; + z /= vec.z; + w /= vec.w; + + return *this; +} + +template +NzVector4& NzVector4::operator/=(T scale) +{ + #if NAZARA_MATH_SAFE + if (NzNumberEquals(scale, F(0.0))) + { + NzStringStream ss; + ss << __FILE__ << ':' << __LINE__ << ": Division by zero"; + + throw std::domain_error(ss.ToString()); + } + #endif + + x /= scale; + y /= scale; + z /= scale; + w /= scale; + + return *this; +} + +template +bool NzVector4::operator==(const NzVector4& vec) const +{ + return NzNumberEquals(x, vec.x) && + NzNumberEquals(y, vec.y) && + NzNumberEquals(z, vec.z) && + NzNumberEquals(w, vec.w); +} + +template +bool NzVector4::operator!=(const NzVector4& vec) const +{ + return !operator==(vec); +} + +template +bool NzVector4::operator<(const NzVector4& vec) const +{ + return x < vec.x && y < vec.y && z < vec.z && w < vec.w; +} + +template +bool NzVector4::operator<=(const NzVector4& vec) const +{ + return operator<(vec) || operator==(vec); +} + +template +bool NzVector4::operator>(const NzVector4& vec) const +{ + return !operator<=(vec); +} + +template +bool NzVector4::operator>=(const NzVector4& vec) const +{ + return !operator<(vec); +} + +template +NzVector4 NzVector4::UnitX() +{ + NzVector4 vector; + vector.MakeUnitX(); + + return vector; +} + +template +NzVector4 NzVector4::UnitY() +{ + NzVector4 vector; + vector.MakeUnitY(); + + return vector; +} + +template +NzVector4 NzVector4::UnitZ() +{ + NzVector4 vector; + vector.MakeUnitZ(); + + return vector; +} + +template +NzVector4 NzVector4::Zero() +{ + NzVector4 vector; + vector.MakeZero(); + + return vector; +} + +template +std::ostream& operator<<(std::ostream& out, const NzVector4& vec) +{ + return out << vec.ToString(); +} + +template +NzVector4 operator*(T scale, const NzVector4& vec) +{ + return NzVector4(scale * vec.x, scale * vec.y, scale * vec.z, scale * vec.w); +} + +template +NzVector4 operator/(T scale, const NzVector4& vec) +{ + #if NAZARA_MATH_SAFE + if (NzNumberEquals(vec.x, F(0.0)) || NzNumberEquals(vec.y, F(0.0)) || NzNumberEquals(vec.z, F(0.0)) || NzNumberEquals(vec.w, F(0.0))) + { + NzStringStream ss; + ss << __FILE__ << ':' << __LINE__ << ": Division by zero"; + + throw std::domain_error(ss.ToString()); + } + #endif + + return NzVector4(scale / vec.x, scale / vec.y, scale / vec.z, scale / vec.w); +} + +#undef F + +#include diff --git a/include/Nazara/Renderer.hpp b/include/Nazara/Renderer.hpp index f4c24dbfb..1b9a4dd0b 100644 --- a/include/Nazara/Renderer.hpp +++ b/include/Nazara/Renderer.hpp @@ -1,40 +1,41 @@ -// This file was automatically generated by Nazara - -/* - Nazara Engine - Renderer module - - Copyright (C) 2012 Jérôme "Lynix" Leclercq (Lynix680@gmail.com) - - Permission is hereby granted, free of charge, to any person obtaining a copy of - this software and associated documentation files (the "Software"), to deal in - the Software without restriction, including without limitation the rights to - use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - of the Software, and to permit persons to whom the Software is furnished to do - so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +// This file was automatically generated by Nazara + +/* + Nazara Engine - Renderer module + + Copyright (C) 2012 Jérôme "Lynix" Leclercq (Lynix680@gmail.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do + so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/include/Nazara/Renderer/Config.hpp b/include/Nazara/Renderer/Config.hpp index 5fa27a4f0..1fafa11e6 100644 --- a/include/Nazara/Renderer/Config.hpp +++ b/include/Nazara/Renderer/Config.hpp @@ -1,44 +1,41 @@ -/* - Nazara Engine - Renderer module - - Copyright (C) 2012 Jérôme "Lynix" Leclercq (Lynix680@gmail.com) - - Permission is hereby granted, free of charge, to any person obtaining a copy of - this software and associated documentation files (the "Software"), to deal in - the Software without restriction, including without limitation the rights to - use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - of the Software, and to permit persons to whom the Software is furnished to do - so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ - -#pragma once - -#ifndef NAZARA_CONFIG_RENDERER_HPP -#define NAZARA_CONFIG_RENDERER_HPP - -/// Chaque modification d'un paramètre du module nécessite une recompilation de celui-ci - -// Active une fenêtre de rendu (NzRenderWindow) lors de sa création -#define NAZARA_RENDERER_ACTIVATE_RENDERWINDOW_ON_CREATION 1 - -// Utilise un tracker pour repérer les éventuels leaks (Ralentit l'exécution) -#define NAZARA_RENDERER_MEMORYLEAKTRACKER 0 - -// Active le paramère debug des paramètres des contextes par défaut (Perte de performances mais capable de recevoir des messages d'OpenGL) -#define NAZARA_RENDERER_OPENGL_DEBUG 0 - -// Active les tests de sécurité basés sur le code (Conseillé pour le développement) -#define NAZARA_RENDERER_SAFE 1 - -#endif // NAZARA_CONFIG_MODULENAME_HPP +/* + Nazara Engine - Renderer module + + Copyright (C) 2012 Jérôme "Lynix" Leclercq (Lynix680@gmail.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do + so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#pragma once + +#ifndef NAZARA_CONFIG_RENDERER_HPP +#define NAZARA_CONFIG_RENDERER_HPP + +/// Chaque modification d'un paramètre du module nécessite une recompilation de celui-ci + +// Utilise un tracker pour repérer les éventuels leaks (Ralentit l'exécution) +#define NAZARA_RENDERER_MEMORYLEAKTRACKER 0 + +// Active le paramère debug des paramètres des contextes par défaut (Perte de performances mais capable de recevoir des messages d'OpenGL) +#define NAZARA_RENDERER_OPENGL_DEBUG 0 + +// Active les tests de sécurité basés sur le code (Conseillé pour le développement) +#define NAZARA_RENDERER_SAFE 1 + +#endif // NAZARA_CONFIG_MODULENAME_HPP diff --git a/include/Nazara/Renderer/Context.hpp b/include/Nazara/Renderer/Context.hpp index b24f1e106..f4bb5be7c 100644 --- a/include/Nazara/Renderer/Context.hpp +++ b/include/Nazara/Renderer/Context.hpp @@ -1,47 +1,49 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Renderer module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#ifdef NAZARA_RENDERER_COMMON -#error This file is not part of the common renderer interface, you must undefine NAZARA_RENDERER_COMMON to use it -#endif - -#pragma once - -#ifndef NAZARA_CONTEXT_HPP -#define NAZARA_CONTEXT_HPP - -#include - -class NzContextImpl; - -class NAZARA_API NzContext -{ - friend NzContextImpl; - - public: - NzContext(); - ~NzContext(); - - bool Create(const NzContextParameters& parameters = NzContextParameters()); - void Destroy(); - const NzContextParameters& GetParameters() const; - bool IsActive() const; - bool SetActive(bool active); - void SwapBuffers(); - - static bool EnsureContext(); - static NzContext* GetCurrent(); - static const NzContext* GetReference(); - static NzContext* GetThreadContext(); - static bool Initialize(); - static void Uninitialize(); - - private: - NzContextParameters m_parameters; - NzContextImpl* m_impl; - - static NzContext* s_reference; -}; - -#endif // NAZARA_CONTEXT_HPP +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_CONTEXT_HPP +#define NAZARA_CONTEXT_HPP + +#ifdef NAZARA_RENDERER_OPENGL + +#include +#include + +class NzContextImpl; + +class NAZARA_API NzContext : public NzResource +{ + friend NzContextImpl; + + public: + NzContext(); + ~NzContext(); + + bool Create(const NzContextParameters& parameters = NzContextParameters()); + void Destroy(); + + const NzContextParameters& GetParameters() const; + bool IsActive() const; + bool SetActive(bool active); + void SwapBuffers(); + + static bool EnsureContext(); + static NzContext* GetCurrent(); + static const NzContext* GetReference(); + static NzContext* GetThreadContext(); + static bool Initialize(); + static void Uninitialize(); + + private: + NzContextParameters m_parameters; + NzContextImpl* m_impl; + + static NzContext* s_reference; +}; + +#endif // NAZARA_RENDERER_OPENGL + +#endif // NAZARA_CONTEXT_HPP diff --git a/include/Nazara/Renderer/Enums.hpp b/include/Nazara/Renderer/Enums.hpp index 459ea4d4a..7abeca5ec 100644 --- a/include/Nazara/Renderer/Enums.hpp +++ b/include/Nazara/Renderer/Enums.hpp @@ -1,141 +1,174 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Renderer module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef NAZARA_ENUMS_RENDERER_HPP -#define NAZARA_ENUMS_RENDERER_HPP - -enum nzBlendFunc -{ - nzBlendFunc_DestAlpha, - nzBlendFunc_DestColor, - nzBlendFunc_SrcAlpha, - nzBlendFunc_SrcColor, - nzBlendFunc_InvDestAlpha, - nzBlendFunc_InvDestColor, - nzBlendFunc_InvSrcAlpha, - nzBlendFunc_InvSrcColor, - nzBlendFunc_One, - nzBlendFunc_Zero -}; - -enum nzFaceCulling -{ - nzFaceCulling_Back, - nzFaceCulling_Front, - nzFaceCulling_FrontAndBack -}; - -enum nzFaceFilling -{ - nzFaceFilling_Point, - nzFaceFilling_Line, - nzFaceFilling_Fill -}; - -enum nzMatrixType -{ - nzMatrixType_Projection, - nzMatrixType_View, - nzMatrixType_World, - - nzMatrixType_Max = nzMatrixType_World -}; - -enum nzPixelBufferType -{ - nzPixelBufferType_Pack, - nzPixelBufferType_Unpack -}; - -enum nzRendererCap -{ - nzRendererCap_AnisotropicFilter, - nzRendererCap_FP64, - nzRendererCap_HardwareBuffer, - nzRendererCap_MultipleRenderTargets, - nzRendererCap_OcclusionQuery, - nzRendererCap_PixelBufferObject, - nzRendererCap_Texture3D, - nzRendererCap_TextureCubemap, - nzRendererCap_TextureMulti, - nzRendererCap_TextureNPOT, - - nzRendererCap_Max = nzRendererCap_TextureNPOT -}; - -enum nzRendererClear -{ - nzRendererClear_Color = 0x01, - nzRendererClear_Depth = 0x02, - nzRendererClear_Stencil = 0x04 -}; - -enum nzRendererComparison -{ - nzRendererComparison_Always, - nzRendererComparison_Equal, - nzRendererComparison_Greater, - nzRendererComparison_GreaterOrEqual, - nzRendererComparison_Less, - nzRendererComparison_LessOrEqual, - nzRendererComparison_Never -}; - -enum nzRendererParameter -{ - nzRendererParameter_Blend, - nzRendererParameter_ColorWrite, - nzRendererParameter_DepthTest, - nzRendererParameter_DepthWrite, - nzRendererParameter_FaceCulling, - nzRendererParameter_Stencil -}; - -enum nzShaderLanguage -{ - nzShaderLanguage_Unknown, - - nzShaderLanguage_Cg, - nzShaderLanguage_GLSL -}; - -enum nzShaderType -{ - nzShaderType_Fragment, - nzShaderType_Geometry, - nzShaderType_Vertex, - - nzShaderType_Max = nzShaderType_Vertex -}; - -enum nzStencilOperation -{ - nzStencilOperation_Decrement, - nzStencilOperation_DecrementToSaturation, - nzStencilOperation_Increment, - nzStencilOperation_IncrementToSaturation, - nzStencilOperation_Invert, - nzStencilOperation_Keep, - nzStencilOperation_Replace, - nzStencilOperation_Zero -}; - -enum nzTextureFilter -{ - nzTextureFilter_Bilinear, - nzTextureFilter_Nearest, - nzTextureFilter_Trilinear, - nzTextureFilter_Unknown -}; - -enum nzTextureWrap -{ - nzTextureWrap_Clamp, - nzTextureWrap_Repeat, - nzTextureWrap_Unknown -}; - -#endif // NAZARA_ENUMS_RENDERER_HPP +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_ENUMS_RENDERER_HPP +#define NAZARA_ENUMS_RENDERER_HPP + +enum nzAttachmentPoint +{ + nzAttachmentPoint_Color, + nzAttachmentPoint_Depth, + nzAttachmentPoint_DepthStencil, + nzAttachmentPoint_Stencil, + + nzAttachmentPoint_Max = nzAttachmentPoint_Stencil +}; + +enum nzBlendFunc +{ + nzBlendFunc_DestAlpha, + nzBlendFunc_DestColor, + nzBlendFunc_SrcAlpha, + nzBlendFunc_SrcColor, + nzBlendFunc_InvDestAlpha, + nzBlendFunc_InvDestColor, + nzBlendFunc_InvSrcAlpha, + nzBlendFunc_InvSrcColor, + nzBlendFunc_One, + nzBlendFunc_Zero, + + nzBlendFunc_Max = nzBlendFunc_Zero +}; + +enum nzFaceCulling +{ + nzFaceCulling_Back, + nzFaceCulling_Front, + nzFaceCulling_FrontAndBack, + + nzFaceCulling_Max = nzFaceCulling_FrontAndBack +}; + +enum nzFaceFilling +{ + nzFaceFilling_Point, + nzFaceFilling_Line, + nzFaceFilling_Fill, + + nzFaceFilling_Max = nzFaceFilling_Fill +}; + +enum nzMatrixType +{ + nzMatrixType_Projection, + nzMatrixType_View, + nzMatrixType_World, + + nzMatrixType_Max = nzMatrixType_World +}; + +enum nzPixelBufferType +{ + nzPixelBufferType_Pack, + nzPixelBufferType_Unpack, + + nzPixelBufferType_Max = nzPixelBufferType_Unpack +}; + +enum nzRendererCap +{ + nzRendererCap_AnisotropicFilter, + nzRendererCap_FP64, + nzRendererCap_HardwareBuffer, + nzRendererCap_MultipleRenderTargets, + nzRendererCap_OcclusionQuery, + nzRendererCap_PixelBufferObject, + nzRendererCap_RenderTexture, + nzRendererCap_Texture3D, + nzRendererCap_TextureCubemap, + nzRendererCap_TextureMulti, + nzRendererCap_TextureNPOT, + + nzRendererCap_Max = nzRendererCap_TextureNPOT +}; + +enum nzRendererClearFlags +{ + nzRendererClear_Color = 0x01, + nzRendererClear_Depth = 0x02, + nzRendererClear_Stencil = 0x04 +}; + +enum nzRendererComparison +{ + nzRendererComparison_Always, + nzRendererComparison_Equal, + nzRendererComparison_Greater, + nzRendererComparison_GreaterOrEqual, + nzRendererComparison_Less, + nzRendererComparison_LessOrEqual, + nzRendererComparison_Never, + + nzRendererComparison_Max = nzRendererComparison_Never +}; + +enum nzRendererParameter +{ + nzRendererParameter_Blend, + nzRendererParameter_ColorWrite, + nzRendererParameter_DepthTest, + nzRendererParameter_DepthWrite, + nzRendererParameter_FaceCulling, + nzRendererParameter_Stencil, + + nzRendererParameter_Max = nzRendererParameter_Stencil +}; + +enum nzShaderLanguage +{ + nzShaderLanguage_Unknown = -1, + + nzShaderLanguage_Cg, + nzShaderLanguage_GLSL, + + nzShaderLanguage_Max = nzShaderLanguage_GLSL +}; + +enum nzShaderType +{ + nzShaderType_Fragment, + nzShaderType_Geometry, + nzShaderType_Vertex, + + nzShaderType_Max = nzShaderType_Vertex +}; + +enum nzStencilOperation +{ + nzStencilOperation_Decrement, + nzStencilOperation_DecrementToSaturation, + nzStencilOperation_Increment, + nzStencilOperation_IncrementToSaturation, + nzStencilOperation_Invert, + nzStencilOperation_Keep, + nzStencilOperation_Replace, + nzStencilOperation_Zero, + + nzStencilOperation_Max = nzStencilOperation_Zero +}; + +enum nzTextureFilter +{ + nzTextureFilter_Unknown = -1, + + nzTextureFilter_Bilinear, + nzTextureFilter_Nearest, + nzTextureFilter_Trilinear, + + nzTextureFilter_Max = nzTextureFilter_Trilinear +}; + +enum nzTextureWrap +{ + nzTextureWrap_Unknown = -1, + + nzTextureWrap_Clamp, + nzTextureWrap_Repeat, + + nzTextureWrap_Max = nzTextureWrap_Repeat +}; + +#endif // NAZARA_ENUMS_RENDERER_HPP diff --git a/include/Nazara/Renderer/OpenGL.hpp b/include/Nazara/Renderer/OpenGL.hpp index 1944054e6..ce4468096 100644 --- a/include/Nazara/Renderer/OpenGL.hpp +++ b/include/Nazara/Renderer/OpenGL.hpp @@ -1,192 +1,233 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Renderer module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#ifdef NAZARA_RENDERER_COMMON - #error This file is not part of the common renderer interface, you must undefine NAZARA_RENDERER_COMMON to use it -#endif - -#pragma once - -#ifndef NAZARA_OPENGL_HPP -#define NAZARA_OPENGL_HPP - -// gl3.h définit WIN32_LEAN_AND_MEAN qui entre en conflit avec la définition de Nazara et doit donc être inclut en premier -#include -#include -#include - -// Inclusion des extensions -#include -#if defined(NAZARA_PLATFORM_WINDOWS) - #include -#elif defined(NAZARA_PLATFORM_LINUX) - #include -#endif - -using NzOpenGLFunc = void (*)(); - -class NAZARA_API NzOpenGL -{ - public: - enum Extension - { - AnisotropicFilter, - DebugOutput, - FP64, - FrameBufferObject, - PixelBufferObject, - SeparateShaderObjects, - Texture3D, - TextureArray, - TextureCompression_s3tc, - TextureStorage, - VertexArrayObject, - - Max = VertexArrayObject - }; - - static NzOpenGLFunc GetEntry(const NzString& entryPoint); - static unsigned int GetVersion(); - static bool Initialize(); - static bool IsSupported(Extension extension); - static bool IsSupported(const NzString& string); - static void Uninitialize(); -}; - -NAZARA_API extern PFNGLACTIVETEXTUREPROC glActiveTexture; -NAZARA_API extern PFNGLATTACHSHADERPROC glAttachShader; -NAZARA_API extern PFNGLBEGINQUERYPROC glBeginQuery; -NAZARA_API extern PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation; -NAZARA_API extern PFNGLBINDBUFFERPROC glBindBuffer; -NAZARA_API extern PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer; -NAZARA_API extern PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer; -NAZARA_API extern PFNGLBINDTEXTUREPROC glBindTexture; -NAZARA_API extern PFNGLBINDVERTEXARRAYPROC glBindVertexArray; -NAZARA_API extern PFNGLBLENDFUNCPROC glBlendFunc; -NAZARA_API extern PFNGLBUFFERDATAPROC glBufferData; -NAZARA_API extern PFNGLBUFFERSUBDATAPROC glBufferSubData; -NAZARA_API extern PFNGLCLEARPROC glClear; -NAZARA_API extern PFNGLCLEARCOLORPROC glClearColor; -NAZARA_API extern PFNGLCLEARDEPTHPROC glClearDepth; -NAZARA_API extern PFNGLCLEARSTENCILPROC glClearStencil; -NAZARA_API extern PFNGLCREATEPROGRAMPROC glCreateProgram; -NAZARA_API extern PFNGLCREATESHADERPROC glCreateShader; -NAZARA_API extern PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus; -NAZARA_API extern PFNGLCOLORMASKPROC glColorMask; -NAZARA_API extern PFNGLCULLFACEPROC glCullFace; -NAZARA_API extern PFNGLCOMPILESHADERPROC glCompileShader; -NAZARA_API extern PFNGLCOPYTEXSUBIMAGE2DPROC glCopyTexSubImage2D; -NAZARA_API extern PFNGLDEBUGMESSAGECALLBACKPROC glDebugMessageCallback; -NAZARA_API extern PFNGLDEBUGMESSAGECONTROLPROC glDebugMessageControl; -NAZARA_API extern PFNGLDEBUGMESSAGEINSERTPROC glDebugMessageInsert; -NAZARA_API extern PFNGLDELETEBUFFERSPROC glDeleteBuffers; -NAZARA_API extern PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers; -NAZARA_API extern PFNGLDELETEPROGRAMPROC glDeleteProgram; -NAZARA_API extern PFNGLDELETEQUERIESPROC glDeleteQueries; -NAZARA_API extern PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers; -NAZARA_API extern PFNGLDELETESHADERPROC glDeleteShader; -NAZARA_API extern PFNGLDELETETEXTURESPROC glDeleteTextures; -NAZARA_API extern PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays; -NAZARA_API extern PFNGLDEPTHFUNCPROC glDepthFunc; -NAZARA_API extern PFNGLDEPTHMASKPROC glDepthMask; -NAZARA_API extern PFNGLDISABLEPROC glDisable; -NAZARA_API extern PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray; -NAZARA_API extern PFNGLDRAWARRAYSPROC glDrawArrays; -NAZARA_API extern PFNGLDRAWBUFFERPROC glDrawBuffer; -NAZARA_API extern PFNGLDRAWBUFFERSPROC glDrawBuffers; -NAZARA_API extern PFNGLDRAWELEMENTSPROC glDrawElements; -NAZARA_API extern PFNGLENDQUERYPROC glEndQuery; -NAZARA_API extern PFNGLFLUSHPROC glFlush; -NAZARA_API extern PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer; -NAZARA_API extern PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D; -NAZARA_API extern PFNGLENABLEPROC glEnable; -NAZARA_API extern PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray; -NAZARA_API extern PFNGLGENERATEMIPMAPPROC glGenerateMipmap; -NAZARA_API extern PFNGLGENBUFFERSPROC glGenBuffers; -NAZARA_API extern PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers; -NAZARA_API extern PFNGLGENQUERIESPROC glGenQueries; -NAZARA_API extern PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers; -NAZARA_API extern PFNGLGENTEXTURESPROC glGenTextures; -NAZARA_API extern PFNGLGENVERTEXARRAYSPROC glGenVertexArrays; -NAZARA_API extern PFNGLGETBUFFERPARAMETERIVPROC glGetBufferParameteriv; -NAZARA_API extern PFNGLGETDEBUGMESSAGELOGPROC glGetDebugMessageLog; -NAZARA_API extern PFNGLGETERRORPROC glGetError; -NAZARA_API extern PFNGLGETINTEGERVPROC glGetIntegerv; -NAZARA_API extern PFNGLGETPROGRAMIVPROC glGetProgramiv; -NAZARA_API extern PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog; -NAZARA_API extern PFNGLGETQUERYIVPROC glGetQueryiv; -NAZARA_API extern PFNGLGETQUERYOBJECTIVPROC glGetQueryObjectiv; -NAZARA_API extern PFNGLGETQUERYOBJECTUIVPROC glGetQueryObjectuiv; -NAZARA_API extern PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog; -NAZARA_API extern PFNGLGETSHADERIVPROC glGetShaderiv; -NAZARA_API extern PFNGLGETSHADERSOURCEPROC glGetShaderSource; -NAZARA_API extern PFNGLGETSTRINGPROC glGetString; -NAZARA_API extern PFNGLGETSTRINGIPROC glGetStringi; -NAZARA_API extern PFNGLGETTEXIMAGEPROC glGetTexImage; -NAZARA_API extern PFNGLGETTEXLEVELPARAMETERFVPROC glGetTexLevelParameterfv; -NAZARA_API extern PFNGLGETTEXLEVELPARAMETERIVPROC glGetTexLevelParameteriv; -NAZARA_API extern PFNGLGETTEXPARAMETERFVPROC glGetTexParameterfv; -NAZARA_API extern PFNGLGETTEXPARAMETERIVPROC glGetTexParameteriv; -NAZARA_API extern PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation; -NAZARA_API extern PFNGLLINKPROGRAMPROC glLinkProgram; -NAZARA_API extern PFNGLMAPBUFFERPROC glMapBuffer; -NAZARA_API extern PFNGLMAPBUFFERRANGEPROC glMapBufferRange; -NAZARA_API extern PFNGLPIXELSTOREIPROC glPixelStorei; -NAZARA_API extern PFNGLPOLYGONMODEPROC glPolygonMode; -NAZARA_API extern PFNGLPROGRAMUNIFORM1DPROC glProgramUniform1d; -NAZARA_API extern PFNGLPROGRAMUNIFORM1FPROC glProgramUniform1f; -NAZARA_API extern PFNGLPROGRAMUNIFORM1IPROC glProgramUniform1i; -NAZARA_API extern PFNGLPROGRAMUNIFORM2DVPROC glProgramUniform2dv; -NAZARA_API extern PFNGLPROGRAMUNIFORM2FVPROC glProgramUniform2fv; -NAZARA_API extern PFNGLPROGRAMUNIFORM3DVPROC glProgramUniform3dv; -NAZARA_API extern PFNGLPROGRAMUNIFORM3FVPROC glProgramUniform3fv; -NAZARA_API extern PFNGLPROGRAMUNIFORM4DVPROC glProgramUniform4dv; -NAZARA_API extern PFNGLPROGRAMUNIFORM4FVPROC glProgramUniform4fv; -NAZARA_API extern PFNGLPROGRAMUNIFORMMATRIX4DVPROC glProgramUniformMatrix4dv; -NAZARA_API extern PFNGLPROGRAMUNIFORMMATRIX4FVPROC glProgramUniformMatrix4fv; -NAZARA_API extern PFNGLREADPIXELSPROC glReadPixels; -NAZARA_API extern PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage; -NAZARA_API extern PFNGLSCISSORPROC glScissor; -NAZARA_API extern PFNGLSHADERSOURCEPROC glShaderSource; -NAZARA_API extern PFNGLSTENCILFUNCPROC glStencilFunc; -NAZARA_API extern PFNGLSTENCILOPPROC glStencilOp; -NAZARA_API extern PFNGLTEXIMAGE1DPROC glTexImage1D; -NAZARA_API extern PFNGLTEXIMAGE2DPROC glTexImage2D; -NAZARA_API extern PFNGLTEXIMAGE3DPROC glTexImage3D; -NAZARA_API extern PFNGLTEXPARAMETERFPROC glTexParameterf; -NAZARA_API extern PFNGLTEXPARAMETERIPROC glTexParameteri; -NAZARA_API extern PFNGLTEXSTORAGE1DPROC glTexStorage1D; -NAZARA_API extern PFNGLTEXSTORAGE2DPROC glTexStorage2D; -NAZARA_API extern PFNGLTEXSTORAGE3DPROC glTexStorage3D; -NAZARA_API extern PFNGLTEXSUBIMAGE1DPROC glTexSubImage1D; -NAZARA_API extern PFNGLTEXSUBIMAGE2DPROC glTexSubImage2D; -NAZARA_API extern PFNGLTEXSUBIMAGE3DPROC glTexSubImage3D; -NAZARA_API extern PFNGLUNIFORM1DPROC glUniform1d; -NAZARA_API extern PFNGLUNIFORM1FPROC glUniform1f; -NAZARA_API extern PFNGLUNIFORM1IPROC glUniform1i; -NAZARA_API extern PFNGLUNIFORM2DVPROC glUniform2dv; -NAZARA_API extern PFNGLUNIFORM2FVPROC glUniform2fv; -NAZARA_API extern PFNGLUNIFORM3DVPROC glUniform3dv; -NAZARA_API extern PFNGLUNIFORM3FVPROC glUniform3fv; -NAZARA_API extern PFNGLUNIFORM4DVPROC glUniform4dv; -NAZARA_API extern PFNGLUNIFORM4FVPROC glUniform4fv; -NAZARA_API extern PFNGLUNIFORMMATRIX4DVPROC glUniformMatrix4dv; -NAZARA_API extern PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv; -NAZARA_API extern PFNGLUNMAPBUFFERPROC glUnmapBuffer; -NAZARA_API extern PFNGLUSEPROGRAMPROC glUseProgram; -NAZARA_API extern PFNGLVERTEXATTRIB4FPROC glVertexAttrib4f; -NAZARA_API extern PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer; -NAZARA_API extern PFNGLVIEWPORTPROC glViewport; -#if defined(NAZARA_PLATFORM_WINDOWS) -NAZARA_API extern PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormat; -NAZARA_API extern PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribs; -NAZARA_API extern PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB; -NAZARA_API extern PFNWGLGETEXTENSIONSSTRINGEXTPROC wglGetExtensionsStringEXT; -NAZARA_API extern PFNWGLSWAPINTERVALEXTPROC wglSwapInterval; -#elif defined(NAZARA_PLATFORM_LINUX) -NAZARA_API extern PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribs; -NAZARA_API extern PFNGLXSWAPINTERVALSGIPROC glXSwapInterval; -#endif - -#endif // NAZARA_OPENGL_HPP +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_OPENGL_HPP +#define NAZARA_OPENGL_HPP + +#ifdef NAZARA_RENDERER_OPENGL + +// gl3.h définit WIN32_LEAN_AND_MEAN qui entre en conflit avec la définition de Nazara et doit donc être inclut en premier +#include +#include +#include +#include +#include + +// Inclusion des extensions +#include +#if defined(NAZARA_PLATFORM_WINDOWS) + #include +#elif defined(NAZARA_PLATFORM_LINUX) + #include +#endif + +enum nzOpenGLExtension +{ + nzOpenGLExtension_AnisotropicFilter, + nzOpenGLExtension_DebugOutput, + nzOpenGLExtension_FP64, + nzOpenGLExtension_FrameBufferObject, + nzOpenGLExtension_PixelBufferObject, + nzOpenGLExtension_SeparateShaderObjects, + nzOpenGLExtension_TextureArray, + nzOpenGLExtension_TextureCompression_s3tc, + nzOpenGLExtension_TextureStorage, + nzOpenGLExtension_VertexArrayObject, + + nzOpenGLExtension_Max = nzOpenGLExtension_VertexArrayObject +}; + +using NzOpenGLFunc = void (*)(); + +namespace NzOpenGL +{ + enum FormatType + { + FormatType_RenderBuffer, +// FormatType_MultisampleTexture, + FormatType_Texture + }; + + struct Format + { + GLenum dataFormat; + GLenum dataType; + GLint internalFormat; + }; + + NzOpenGLFunc GetEntry(const NzString& entryPoint); + unsigned int GetVersion(); + bool Initialize(); + bool IsSupported(nzOpenGLExtension extension); + bool IsSupported(const NzString& string); + bool TranslateFormat(nzPixelFormat pixelFormat, Format* format, FormatType target); + void Uninitialize(); + + extern GLenum Attachment[nzAttachmentPoint_Max+1]; + extern nzUInt8 AttributeIndex[nzElementUsage_Max+1]; + extern GLenum BlendFunc[nzBlendFunc_Max+1]; + extern GLenum BufferLock[nzBufferAccess_Max+1]; + extern GLenum BufferLockRange[nzBufferAccess_Max+1]; + extern GLenum BufferTarget[nzBufferType_Max+1]; + extern GLenum BufferTargetBinding[nzBufferType_Max+1]; + extern GLenum BufferUsage[nzBufferUsage_Max+1]; + extern GLenum CubemapFace[6]; // Un cube possède six faces et ça n'est pas prêt de changer + extern GLenum ElementType[nzElementType_Max+1]; + extern GLenum FaceCulling[nzFaceCulling_Max+1]; + extern GLenum FaceFilling[nzFaceFilling_Max+1]; + extern GLenum PrimitiveType[nzPrimitiveType_Max+1]; + extern GLenum RendererComparison[nzRendererComparison_Max+1]; + extern GLenum RendererParameter[nzRendererParameter_Max+1]; + extern GLenum ShaderType[nzShaderType_Max+1]; + extern GLenum StencilOperation[nzStencilOperation_Max+1]; + extern GLenum TextureTarget[nzImageType_Max+1]; + extern GLenum TextureTargetBinding[nzImageType_Max+1]; + extern GLenum TextureTargetProxy[nzImageType_Max+1]; +} + +NAZARA_API extern PFNGLACTIVETEXTUREPROC glActiveTexture; +NAZARA_API extern PFNGLATTACHSHADERPROC glAttachShader; +NAZARA_API extern PFNGLBEGINQUERYPROC glBeginQuery; +NAZARA_API extern PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation; +NAZARA_API extern PFNGLBINDBUFFERPROC glBindBuffer; +NAZARA_API extern PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer; +NAZARA_API extern PFNGLBINDFRAGDATALOCATIONPROC glBindFragDataLocation; +NAZARA_API extern PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer; +NAZARA_API extern PFNGLBINDTEXTUREPROC glBindTexture; +NAZARA_API extern PFNGLBINDVERTEXARRAYPROC glBindVertexArray; +NAZARA_API extern PFNGLBLENDFUNCPROC glBlendFunc; +NAZARA_API extern PFNGLBUFFERDATAPROC glBufferData; +NAZARA_API extern PFNGLBUFFERSUBDATAPROC glBufferSubData; +NAZARA_API extern PFNGLCLEARPROC glClear; +NAZARA_API extern PFNGLCLEARCOLORPROC glClearColor; +NAZARA_API extern PFNGLCLEARDEPTHPROC glClearDepth; +NAZARA_API extern PFNGLCLEARSTENCILPROC glClearStencil; +NAZARA_API extern PFNGLCREATEPROGRAMPROC glCreateProgram; +NAZARA_API extern PFNGLCREATESHADERPROC glCreateShader; +NAZARA_API extern PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus; +NAZARA_API extern PFNGLCOLORMASKPROC glColorMask; +NAZARA_API extern PFNGLCULLFACEPROC glCullFace; +NAZARA_API extern PFNGLCOMPILESHADERPROC glCompileShader; +NAZARA_API extern PFNGLCOPYTEXSUBIMAGE2DPROC glCopyTexSubImage2D; +NAZARA_API extern PFNGLDEBUGMESSAGECALLBACKPROC glDebugMessageCallback; +NAZARA_API extern PFNGLDEBUGMESSAGECONTROLPROC glDebugMessageControl; +NAZARA_API extern PFNGLDEBUGMESSAGEINSERTPROC glDebugMessageInsert; +NAZARA_API extern PFNGLDELETEBUFFERSPROC glDeleteBuffers; +NAZARA_API extern PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers; +NAZARA_API extern PFNGLDELETEPROGRAMPROC glDeleteProgram; +NAZARA_API extern PFNGLDELETEQUERIESPROC glDeleteQueries; +NAZARA_API extern PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers; +NAZARA_API extern PFNGLDELETESHADERPROC glDeleteShader; +NAZARA_API extern PFNGLDELETETEXTURESPROC glDeleteTextures; +NAZARA_API extern PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays; +NAZARA_API extern PFNGLDEPTHFUNCPROC glDepthFunc; +NAZARA_API extern PFNGLDEPTHMASKPROC glDepthMask; +NAZARA_API extern PFNGLDISABLEPROC glDisable; +NAZARA_API extern PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray; +NAZARA_API extern PFNGLDRAWARRAYSPROC glDrawArrays; +NAZARA_API extern PFNGLDRAWBUFFERPROC glDrawBuffer; +NAZARA_API extern PFNGLDRAWBUFFERSPROC glDrawBuffers; +NAZARA_API extern PFNGLDRAWELEMENTSPROC glDrawElements; +NAZARA_API extern PFNGLENDQUERYPROC glEndQuery; +NAZARA_API extern PFNGLFLUSHPROC glFlush; +NAZARA_API extern PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer; +NAZARA_API extern PFNGLFRAMEBUFFERTEXTUREPROC glFramebufferTexture; +NAZARA_API extern PFNGLFRAMEBUFFERTEXTURE1DPROC glFramebufferTexture1D; +NAZARA_API extern PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D; +NAZARA_API extern PFNGLFRAMEBUFFERTEXTURE3DPROC glFramebufferTexture3D; +NAZARA_API extern PFNGLFRAMEBUFFERTEXTURELAYERPROC glFramebufferTextureLayer; +NAZARA_API extern PFNGLENABLEPROC glEnable; +NAZARA_API extern PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray; +NAZARA_API extern PFNGLGENERATEMIPMAPPROC glGenerateMipmap; +NAZARA_API extern PFNGLGENBUFFERSPROC glGenBuffers; +NAZARA_API extern PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers; +NAZARA_API extern PFNGLGENQUERIESPROC glGenQueries; +NAZARA_API extern PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers; +NAZARA_API extern PFNGLGENTEXTURESPROC glGenTextures; +NAZARA_API extern PFNGLGENVERTEXARRAYSPROC glGenVertexArrays; +NAZARA_API extern PFNGLGETBUFFERPARAMETERIVPROC glGetBufferParameteriv; +NAZARA_API extern PFNGLGETDEBUGMESSAGELOGPROC glGetDebugMessageLog; +NAZARA_API extern PFNGLGETERRORPROC glGetError; +NAZARA_API extern PFNGLGETINTEGERVPROC glGetIntegerv; +NAZARA_API extern PFNGLGETPROGRAMIVPROC glGetProgramiv; +NAZARA_API extern PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog; +NAZARA_API extern PFNGLGETQUERYIVPROC glGetQueryiv; +NAZARA_API extern PFNGLGETQUERYOBJECTIVPROC glGetQueryObjectiv; +NAZARA_API extern PFNGLGETQUERYOBJECTUIVPROC glGetQueryObjectuiv; +NAZARA_API extern PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog; +NAZARA_API extern PFNGLGETSHADERIVPROC glGetShaderiv; +NAZARA_API extern PFNGLGETSHADERSOURCEPROC glGetShaderSource; +NAZARA_API extern PFNGLGETSTRINGPROC glGetString; +NAZARA_API extern PFNGLGETSTRINGIPROC glGetStringi; +NAZARA_API extern PFNGLGETTEXIMAGEPROC glGetTexImage; +NAZARA_API extern PFNGLGETTEXLEVELPARAMETERFVPROC glGetTexLevelParameterfv; +NAZARA_API extern PFNGLGETTEXLEVELPARAMETERIVPROC glGetTexLevelParameteriv; +NAZARA_API extern PFNGLGETTEXPARAMETERFVPROC glGetTexParameterfv; +NAZARA_API extern PFNGLGETTEXPARAMETERIVPROC glGetTexParameteriv; +NAZARA_API extern PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation; +NAZARA_API extern PFNGLLINKPROGRAMPROC glLinkProgram; +NAZARA_API extern PFNGLMAPBUFFERPROC glMapBuffer; +NAZARA_API extern PFNGLMAPBUFFERRANGEPROC glMapBufferRange; +NAZARA_API extern PFNGLPIXELSTOREIPROC glPixelStorei; +NAZARA_API extern PFNGLPOLYGONMODEPROC glPolygonMode; +NAZARA_API extern PFNGLPROGRAMUNIFORM1DPROC glProgramUniform1d; +NAZARA_API extern PFNGLPROGRAMUNIFORM1FPROC glProgramUniform1f; +NAZARA_API extern PFNGLPROGRAMUNIFORM1IPROC glProgramUniform1i; +NAZARA_API extern PFNGLPROGRAMUNIFORM2DVPROC glProgramUniform2dv; +NAZARA_API extern PFNGLPROGRAMUNIFORM2FVPROC glProgramUniform2fv; +NAZARA_API extern PFNGLPROGRAMUNIFORM3DVPROC glProgramUniform3dv; +NAZARA_API extern PFNGLPROGRAMUNIFORM3FVPROC glProgramUniform3fv; +NAZARA_API extern PFNGLPROGRAMUNIFORM4DVPROC glProgramUniform4dv; +NAZARA_API extern PFNGLPROGRAMUNIFORM4FVPROC glProgramUniform4fv; +NAZARA_API extern PFNGLPROGRAMUNIFORMMATRIX4DVPROC glProgramUniformMatrix4dv; +NAZARA_API extern PFNGLPROGRAMUNIFORMMATRIX4FVPROC glProgramUniformMatrix4fv; +NAZARA_API extern PFNGLREADPIXELSPROC glReadPixels; +NAZARA_API extern PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage; +NAZARA_API extern PFNGLSCISSORPROC glScissor; +NAZARA_API extern PFNGLSHADERSOURCEPROC glShaderSource; +NAZARA_API extern PFNGLSTENCILFUNCPROC glStencilFunc; +NAZARA_API extern PFNGLSTENCILOPPROC glStencilOp; +NAZARA_API extern PFNGLTEXIMAGE1DPROC glTexImage1D; +NAZARA_API extern PFNGLTEXIMAGE2DPROC glTexImage2D; +NAZARA_API extern PFNGLTEXIMAGE3DPROC glTexImage3D; +NAZARA_API extern PFNGLTEXPARAMETERFPROC glTexParameterf; +NAZARA_API extern PFNGLTEXPARAMETERIPROC glTexParameteri; +NAZARA_API extern PFNGLTEXSTORAGE1DPROC glTexStorage1D; +NAZARA_API extern PFNGLTEXSTORAGE2DPROC glTexStorage2D; +NAZARA_API extern PFNGLTEXSTORAGE3DPROC glTexStorage3D; +NAZARA_API extern PFNGLTEXSUBIMAGE1DPROC glTexSubImage1D; +NAZARA_API extern PFNGLTEXSUBIMAGE2DPROC glTexSubImage2D; +NAZARA_API extern PFNGLTEXSUBIMAGE3DPROC glTexSubImage3D; +NAZARA_API extern PFNGLUNIFORM1DPROC glUniform1d; +NAZARA_API extern PFNGLUNIFORM1FPROC glUniform1f; +NAZARA_API extern PFNGLUNIFORM1IPROC glUniform1i; +NAZARA_API extern PFNGLUNIFORM2DVPROC glUniform2dv; +NAZARA_API extern PFNGLUNIFORM2FVPROC glUniform2fv; +NAZARA_API extern PFNGLUNIFORM3DVPROC glUniform3dv; +NAZARA_API extern PFNGLUNIFORM3FVPROC glUniform3fv; +NAZARA_API extern PFNGLUNIFORM4DVPROC glUniform4dv; +NAZARA_API extern PFNGLUNIFORM4FVPROC glUniform4fv; +NAZARA_API extern PFNGLUNIFORMMATRIX4DVPROC glUniformMatrix4dv; +NAZARA_API extern PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv; +NAZARA_API extern PFNGLUNMAPBUFFERPROC glUnmapBuffer; +NAZARA_API extern PFNGLUSEPROGRAMPROC glUseProgram; +NAZARA_API extern PFNGLVERTEXATTRIB4FPROC glVertexAttrib4f; +NAZARA_API extern PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer; +NAZARA_API extern PFNGLVIEWPORTPROC glViewport; +#if defined(NAZARA_PLATFORM_WINDOWS) +NAZARA_API extern PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormat; +NAZARA_API extern PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribs; +NAZARA_API extern PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB; +NAZARA_API extern PFNWGLGETEXTENSIONSSTRINGEXTPROC wglGetExtensionsStringEXT; +NAZARA_API extern PFNWGLSWAPINTERVALEXTPROC wglSwapInterval; +#elif defined(NAZARA_PLATFORM_LINUX) +NAZARA_API extern PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribs; +NAZARA_API extern PFNGLXSWAPINTERVALSGIPROC glXSwapInterval; +#endif + +#endif // NAZARA_RENDERER_OPENGL + +#endif // NAZARA_OPENGL_HPP diff --git a/include/Nazara/Renderer/RenderTarget.hpp b/include/Nazara/Renderer/RenderTarget.hpp index 347bf90fb..1c7ddaab9 100644 --- a/include/Nazara/Renderer/RenderTarget.hpp +++ b/include/Nazara/Renderer/RenderTarget.hpp @@ -1,42 +1,41 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Renderer module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef NAZARA_RENDERTARGET_HPP -#define NAZARA_RENDERTARGET_HPP - -#include -#include -#include - -class NzRenderer; - -class NAZARA_API NzRenderTarget -{ - friend class NzRenderer; - - public: - NzRenderTarget() = default; - virtual ~NzRenderTarget(); - - #ifndef NAZARA_RENDERER_COMMON - virtual bool HasContext() const = 0; - #endif - - virtual unsigned int GetHeight() const = 0; - virtual NzRenderTargetParameters GetParameters() const = 0; - virtual unsigned int GetWidth() const = 0; - - bool IsActive() const; - virtual bool IsValid() const = 0; - - bool SetActive(bool active); - - protected: - virtual bool Activate() = 0; - virtual void Desactivate(); -}; - -#endif // NAZARA_RENDERTARGET_HPP +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_RENDERTARGET_HPP +#define NAZARA_RENDERTARGET_HPP + +#include +#include +#include + +class NzRenderer; + +class NAZARA_API NzRenderTarget +{ + friend class NzRenderer; + + public: + NzRenderTarget() = default; + virtual ~NzRenderTarget(); + + virtual unsigned int GetHeight() const = 0; + virtual NzRenderTargetParameters GetParameters() const = 0; + virtual unsigned int GetWidth() const = 0; + + bool IsActive() const; + virtual bool IsRenderable() const = 0; + + bool SetActive(bool active); + + // Fonctions OpenGL + virtual bool HasContext() const = 0; + + protected: + virtual bool Activate() = 0; + virtual void Desactivate(); +}; + +#endif // NAZARA_RENDERTARGET_HPP diff --git a/include/Nazara/Renderer/RenderTexture.hpp b/include/Nazara/Renderer/RenderTexture.hpp new file mode 100644 index 000000000..e7be88f88 --- /dev/null +++ b/include/Nazara/Renderer/RenderTexture.hpp @@ -0,0 +1,60 @@ +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_RENDERTEXTURE_HPP +#define NAZARA_RENDERTEXTURE_HPP + +#include +#include +#include +#include +#include +#include + +struct NzRenderTextureImpl; + +class NAZARA_API NzRenderTexture : public NzRenderTarget, NzResourceListener, NzNonCopyable +{ + public: + NzRenderTexture() = default; + ~NzRenderTexture(); + + bool AttachBuffer(nzAttachmentPoint attachmentPoint, nzUInt8 index, nzPixelFormat format); + bool AttachTexture(nzAttachmentPoint attachmentPoint, nzUInt8 index, NzTexture* texture); + bool AttachTexture(nzAttachmentPoint attachmentPoint, nzUInt8 index, NzTexture* texture, unsigned int z); + + bool Create(unsigned int width, unsigned int height, bool lock = false); + void Destroy(); + + void Detach(nzAttachmentPoint attachmentPoint, nzUInt8 index); + + unsigned int GetHeight() const; + NzRenderTargetParameters GetParameters() const; + unsigned int GetWidth() const; + + bool IsComplete() const; + bool IsRenderable() const; + bool IsValid() const; + + bool Lock() const; + void Unlock() const; + + // Fonctions OpenGL + bool HasContext() const; + + static bool IsSupported(); + + protected: + bool Activate() override; + void Desactivate() override; + + private: + void OnResourceDestroy(const NzResource* resource, int index) override; + + NzRenderTextureImpl* m_impl = nullptr; +}; + +#endif // NAZARA_RENDERTEXTURE_HPP diff --git a/include/Nazara/Renderer/RenderWindow.hpp b/include/Nazara/Renderer/RenderWindow.hpp index d9b932d09..fdef4b2c0 100644 --- a/include/Nazara/Renderer/RenderWindow.hpp +++ b/include/Nazara/Renderer/RenderWindow.hpp @@ -1,71 +1,68 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Renderer module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -// Interface inspirée de la SFML par Laurent Gomila - -#pragma once - -#ifndef NAZARA_RENDERWINDOW_HPP -#define NAZARA_RENDERWINDOW_HPP - -#include -#include -#include -#include -#include -#include - -class NzContext; -class NzImage; -class NzTexture; -struct NzContextParameters; - -class NAZARA_API NzRenderWindow : public NzRenderTarget, public NzWindow -{ - public: - NzRenderWindow() = default; - NzRenderWindow(NzVideoMode mode, const NzString& title, nzUInt32 style = nzWindowStyle_Default, const NzContextParameters& parameters = NzContextParameters()); - NzRenderWindow(NzWindowHandle handle, const NzContextParameters& parameters = NzContextParameters()); - virtual ~NzRenderWindow(); - - bool CopyToImage(NzImage* image); ///TODO: Const - bool CopyToTexture(NzTexture* texture); ///TODO: Const - - bool Create(NzVideoMode mode, const NzString& title, nzUInt32 style = nzWindowStyle_Default, const NzContextParameters& parameters = NzContextParameters()); - bool Create(NzWindowHandle handle, const NzContextParameters& parameters = NzContextParameters()); - - void Display(); - - void EnableVerticalSync(bool enabled); - - #ifndef NAZARA_RENDERER_COMMON - NzContextParameters GetContextParameters() const; - #endif - - unsigned int GetHeight() const; - NzRenderTargetParameters GetParameters() const; - unsigned int GetWidth() const; - - #ifndef NAZARA_RENDERER_COMMON - bool HasContext() const; - #endif - - bool IsValid() const; - - void SetFramerateLimit(unsigned int limit); - - protected: - virtual bool Activate() override; - - private: - virtual void OnWindowDestroying() override; - virtual bool OnWindowCreated() override; - - NzClock m_clock; - NzContextParameters m_parameters; - NzContext* m_context = nullptr; - unsigned int m_framerateLimit = 0; -}; - -#endif // NAZARA_RENDERWINDOW_HPP +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +// Interface inspirée de la SFML par Laurent Gomila + +#pragma once + +#ifndef NAZARA_RENDERWINDOW_HPP +#define NAZARA_RENDERWINDOW_HPP + +#include +#include +#include +#include +#include +#include + +class NzContext; +class NzImage; +class NzTexture; +struct NzContextParameters; + +class NAZARA_API NzRenderWindow : public NzRenderTarget, public NzWindow +{ + public: + NzRenderWindow() = default; + NzRenderWindow(NzVideoMode mode, const NzString& title, nzUInt32 style = nzWindowStyle_Default, const NzContextParameters& parameters = NzContextParameters()); + NzRenderWindow(NzWindowHandle handle, const NzContextParameters& parameters = NzContextParameters()); + virtual ~NzRenderWindow(); + + bool CopyToImage(NzImage* image); ///TODO: Const + bool CopyToTexture(NzTexture* texture); ///TODO: Const + + bool Create(NzVideoMode mode, const NzString& title, nzUInt32 style = nzWindowStyle_Default, const NzContextParameters& parameters = NzContextParameters()); + bool Create(NzWindowHandle handle, const NzContextParameters& parameters = NzContextParameters()); + + void Display(); + + void EnableVerticalSync(bool enabled); + + unsigned int GetHeight() const; + NzRenderTargetParameters GetParameters() const; + unsigned int GetWidth() const; + + bool IsRenderable() const; + bool IsValid() const; + + void SetFramerateLimit(unsigned int limit); + + // Fonctions OpenGL + NzContextParameters GetContextParameters() const; + bool HasContext() const; + + protected: + bool Activate() override; + + private: + bool OnWindowCreated() override; + void OnWindowDestroy() override; + + NzClock m_clock; + NzContextParameters m_parameters; + NzContext* m_context = nullptr; + unsigned int m_framerateLimit = 0; +}; + +#endif // NAZARA_RENDERWINDOW_HPP diff --git a/include/Nazara/Renderer/Texture.hpp b/include/Nazara/Renderer/Texture.hpp index 0d9611865..9dc2e6222 100644 --- a/include/Nazara/Renderer/Texture.hpp +++ b/include/Nazara/Renderer/Texture.hpp @@ -1,89 +1,92 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Renderer module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef NAZARA_TEXTURE_HPP -#define NAZARA_TEXTURE_HPP - -#include -#include -#include -#include -#include - -struct NzTextureImpl; - -class NAZARA_API NzTexture : public NzResource, NzNonCopyable -{ - friend class NzShader; - - public: - NzTexture(); - explicit NzTexture(const NzImage& image); - ~NzTexture(); - - #ifndef NAZARA_RENDERER_COMMON - bool Bind() const; - #endif - - bool Create(nzImageType type, nzPixelFormat format, unsigned int width, unsigned int height, unsigned int depth = 1, nzUInt8 levelCount = 1, bool lock = false); - void Destroy(); - - bool Download(NzImage* image) const; - - bool EnableMipmapping(bool enable); - - unsigned int GetAnisotropyLevel() const; - nzUInt8 GetBPP() const; - unsigned int GetDepth() const; - nzTextureFilter GetFilterMode() const; - nzPixelFormat GetFormat() const; - unsigned int GetHeight() const; - nzImageType GetType() const; - unsigned int GetWidth() const; - nzTextureWrap GetWrapMode() const; - - bool IsCompressed() const; - bool IsCubemap() const; - bool IsTarget() const; - bool IsValid() const; - - bool LoadFromFile(const NzString& filePath, const NzImageParams& params = NzImageParams()); - bool LoadFromImage(const NzImage& image); - bool LoadFromMemory(const void* data, std::size_t size, const NzImageParams& params = NzImageParams()); - bool LoadFromStream(NzInputStream& stream, const NzImageParams& params = NzImageParams()); - - bool Lock(); - - bool SetAnisotropyLevel(unsigned int anistropyLevel); - bool SetFilterMode(nzTextureFilter filter); - bool SetMipmapRange(nzUInt8 minLevel, nzUInt8 maxLevel); - bool SetWrapMode(nzTextureWrap wrap); - - bool Update(const NzImage& image, nzUInt8 level = 0); - bool Update(const NzImage& image, const NzRectui& rect, unsigned int z = 0, nzUInt8 level = 0); - bool Update(const NzImage& image, const NzCubeui& cube, nzUInt8 level = 0); - bool Update(const nzUInt8* pixels, nzUInt8 level = 0); - bool Update(const nzUInt8* pixels, const NzRectui& rect, unsigned int z = 0, nzUInt8 level = 0); - bool Update(const nzUInt8* pixels, const NzCubeui& cube, nzUInt8 level = 0); - bool UpdateFace(nzCubemapFace face, const NzImage& image, nzUInt8 level = 0); - bool UpdateFace(nzCubemapFace face, const NzImage& image, const NzRectui& rect, nzUInt8 level = 0); - bool UpdateFace(nzCubemapFace face, const nzUInt8* pixels, nzUInt8 level = 0); - bool UpdateFace(nzCubemapFace face, const nzUInt8* pixels, const NzRectui& rect, nzUInt8 level = 0); - - void Unlock(); - - static unsigned int GetValidSize(unsigned int size); - static bool IsFormatSupported(nzPixelFormat format); - static bool IsMipmappingSupported(); - static bool IsTypeSupported(nzImageType type); - - private: - void SetTarget(bool isTarget); - - NzTextureImpl* m_impl; -}; - -#endif // NAZARA_TEXTURE_HPP +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_TEXTURE_HPP +#define NAZARA_TEXTURE_HPP + +#include +#include +#include +#include +#include + +class NzRenderTexture; +struct NzTextureImpl; + +class NAZARA_API NzTexture : public NzResource, NzNonCopyable +{ + friend NzRenderTexture; + friend class NzShader; + + public: + NzTexture(); + explicit NzTexture(const NzImage& image); + ~NzTexture(); + + bool Create(nzImageType type, nzPixelFormat format, unsigned int width, unsigned int height, unsigned int depth = 1, nzUInt8 levelCount = 1, bool lock = false); + void Destroy(); + + bool Download(NzImage* image) const; + + bool EnableMipmapping(bool enable); + + unsigned int GetAnisotropyLevel() const; + nzUInt8 GetBPP() const; + unsigned int GetDepth() const; + nzTextureFilter GetFilterMode() const; + nzPixelFormat GetFormat() const; + unsigned int GetHeight() const; + nzImageType GetType() const; + unsigned int GetWidth() const; + nzTextureWrap GetWrapMode() const; + + bool IsCompressed() const; + bool IsCubemap() const; + bool IsTarget() const; + bool IsValid() const; + + bool LoadFromFile(const NzString& filePath, const NzImageParams& params = NzImageParams()); + bool LoadFromImage(const NzImage& image); + bool LoadFromMemory(const void* data, std::size_t size, const NzImageParams& params = NzImageParams()); + bool LoadFromStream(NzInputStream& stream, const NzImageParams& params = NzImageParams()); + + bool Lock(); + + bool SetAnisotropyLevel(unsigned int anistropyLevel); + bool SetFilterMode(nzTextureFilter filter); + bool SetMipmapRange(nzUInt8 minLevel, nzUInt8 maxLevel); + bool SetWrapMode(nzTextureWrap wrap); + + bool Update(const NzImage& image, nzUInt8 level = 0); + bool Update(const NzImage& image, const NzRectui& rect, unsigned int z = 0, nzUInt8 level = 0); + bool Update(const NzImage& image, const NzCubeui& cube, nzUInt8 level = 0); + bool Update(const nzUInt8* pixels, nzUInt8 level = 0); + bool Update(const nzUInt8* pixels, const NzRectui& rect, unsigned int z = 0, nzUInt8 level = 0); + bool Update(const nzUInt8* pixels, const NzCubeui& cube, nzUInt8 level = 0); + bool UpdateFace(nzCubemapFace face, const NzImage& image, nzUInt8 level = 0); + bool UpdateFace(nzCubemapFace face, const NzImage& image, const NzRectui& rect, nzUInt8 level = 0); + bool UpdateFace(nzCubemapFace face, const nzUInt8* pixels, nzUInt8 level = 0); + bool UpdateFace(nzCubemapFace face, const nzUInt8* pixels, const NzRectui& rect, nzUInt8 level = 0); + + void Unlock(); + + // Fonctions OpenGL + unsigned int GetOpenGLID() const; + bool Prepare() const; + + static unsigned int GetValidSize(unsigned int size); + static bool IsFormatSupported(nzPixelFormat format); + static bool IsMipmappingSupported(); + static bool IsTypeSupported(nzImageType type); + + private: + NzRenderTexture* GetRenderTexture() const; + void SetRenderTexture(NzRenderTexture* renderTexture); + + NzTextureImpl* m_impl; +}; + +#endif // NAZARA_TEXTURE_HPP diff --git a/include/Nazara/Utility/Animation.hpp b/include/Nazara/Utility/Animation.hpp index cc13bc7b2..c5403b9f3 100644 --- a/include/Nazara/Utility/Animation.hpp +++ b/include/Nazara/Utility/Animation.hpp @@ -1,77 +1,78 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Utility module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef NAZARA_ANIMATION_HPP -#define NAZARA_ANIMATION_HPP - -#include -#include -#include -#include -#include - -struct NzAnimationParams -{ - unsigned int endFrame = static_cast(-1); - unsigned int startFrame = 0; - - bool IsValid() const; -}; - -struct NzSequence -{ - NzString name; - unsigned int firstFrame; - unsigned int lastFrame; - unsigned int framePerSecond; -}; - -class NzAnimation; - -using NzAnimationLoader = NzResourceLoader; - -struct NzAnimationImpl; - -class NAZARA_API NzAnimation : public NzResource -{ - friend NzAnimationLoader; - - public: - NzAnimation() = default; - ~NzAnimation(); - - unsigned int AddSequence(const NzSequence& sequence); - - bool Create(nzAnimationType type, unsigned int frameCount); - void Destroy(); - - unsigned int GetFrameCount() const; - NzSequence* GetSequence(const NzString& sequenceName); - NzSequence* GetSequence(unsigned int index); - const NzSequence* GetSequence(const NzString& sequenceName) const; - const NzSequence* GetSequence(unsigned int index) const; - unsigned int GetSequenceCount() const; - nzAnimationType GetType() const; - - bool HasSequence(const NzString& sequenceName) const; - bool HasSequence(unsigned int index = 0) const; - - bool IsValid() const; - - bool LoadFromFile(const NzString& filePath, const NzAnimationParams& params = NzAnimationParams()); - bool LoadFromMemory(const void* data, std::size_t size, const NzAnimationParams& params = NzAnimationParams()); - bool LoadFromStream(NzInputStream& stream, const NzAnimationParams& params = NzAnimationParams()); - - void RemoveSequence(const NzString& sequenceName); - void RemoveSequence(unsigned int index); - - private: - NzAnimationImpl* m_impl = nullptr; - - static NzAnimationLoader::LoaderList s_loaders; -}; - -#endif // NAZARA_ANIMATION_HPP +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_ANIMATION_HPP +#define NAZARA_ANIMATION_HPP + +#include +#include +#include +#include +#include + +struct NzAnimationParams +{ + unsigned int endFrame = static_cast(-1); + unsigned int startFrame = 0; + + bool IsValid() const; +}; + +struct NzSequence +{ + NzString name; + unsigned int firstFrame; + unsigned int lastFrame; + unsigned int framePerSecond; +}; + +class NzAnimation; + +using NzAnimationLoader = NzResourceLoader; + +struct NzAnimationImpl; + +class NAZARA_API NzAnimation : public NzResource +{ + friend NzAnimationLoader; + + public: + NzAnimation() = default; + ~NzAnimation(); + + bool AddSequence(const NzSequence& sequence); + + bool Create(nzAnimationType type, unsigned int frameCount); + void Destroy(); + + unsigned int GetFrameCount() const; + NzSequence* GetSequence(const NzString& sequenceName); + NzSequence* GetSequence(unsigned int index); + const NzSequence* GetSequence(const NzString& sequenceName) const; + const NzSequence* GetSequence(unsigned int index) const; + unsigned int GetSequenceCount() const; + int GetSequenceIndex(const NzString& sequenceName) const; + nzAnimationType GetType() const; + + bool HasSequence(const NzString& sequenceName) const; + bool HasSequence(unsigned int index = 0) const; + + bool IsValid() const; + + bool LoadFromFile(const NzString& filePath, const NzAnimationParams& params = NzAnimationParams()); + bool LoadFromMemory(const void* data, std::size_t size, const NzAnimationParams& params = NzAnimationParams()); + bool LoadFromStream(NzInputStream& stream, const NzAnimationParams& params = NzAnimationParams()); + + void RemoveSequence(const NzString& sequenceName); + void RemoveSequence(unsigned int index); + + private: + NzAnimationImpl* m_impl = nullptr; + + static NzAnimationLoader::LoaderList s_loaders; +}; + +#endif // NAZARA_ANIMATION_HPP diff --git a/include/Nazara/Utility/Enums.hpp b/include/Nazara/Utility/Enums.hpp index 9d044c7c1..8cf5bd786 100644 --- a/include/Nazara/Utility/Enums.hpp +++ b/include/Nazara/Utility/Enums.hpp @@ -1,220 +1,250 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Utility module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef NAZARA_ENUMS_UTILITY_HPP -#define NAZARA_ENUMS_UTILITY_HPP - -enum nzAnimationType -{ - nzAnimationType_Keyframe, - nzAnimationType_Skeletal, - nzAnimationType_Static -}; - -enum nzBufferAccess -{ - nzBufferAccess_DiscardAndWrite, - nzBufferAccess_ReadOnly, - nzBufferAccess_ReadWrite, - nzBufferAccess_WriteOnly -}; - -enum nzBufferStorage -{ - //nzBufferStorage_Both, - nzBufferStorage_Hardware, - nzBufferStorage_Software, - - nzBufferStorage_Max = nzBufferStorage_Software -}; - -enum nzBufferType -{ - nzBufferType_Index, - nzBufferType_Vertex -}; - -enum nzBufferUsage -{ - nzBufferUsage_Dynamic, - nzBufferUsage_Static -}; - -enum nzCubemapFace -{ - // Cette énumération est prévue pour remplacer l'argument "z" des méthodes de NzImage contenant un cubemap - // L'ordre est X, -X, Y, -Y, Z, -Z - nzCubemapFace_PositiveX = 0, - nzCubemapFace_PositiveY = 2, - nzCubemapFace_PositiveZ = 4, - nzCubemapFace_NegativeX = 1, - nzCubemapFace_NegativeY = 3, - nzCubemapFace_NegativeZ = 5 -}; - -enum nzElementStream -{ - nzElementStream_VertexData, - nzElementStream_InstancedData, - - nzElementStream_Max = nzElementStream_InstancedData -}; - -enum nzElementType -{ - nzElementType_Color, - nzElementType_Double1, - nzElementType_Double2, - nzElementType_Double3, - nzElementType_Double4, - nzElementType_Float1, - nzElementType_Float2, - nzElementType_Float3, - nzElementType_Float4 -}; - -enum nzElementUsage -{ - nzElementUsage_Diffuse, - nzElementUsage_Normal, - nzElementUsage_Position, - nzElementUsage_Tangent, - nzElementUsage_TexCoord, - - nzElementUsage_Max = nzElementUsage_TexCoord -}; - -enum nzEventType -{ - nzEventType_GainedFocus, - nzEventType_LostFocus, - nzEventType_KeyPressed, - nzEventType_KeyReleased, - nzEventType_MouseButtonDoubleClicked, - nzEventType_MouseButtonPressed, - nzEventType_MouseButtonReleased, - nzEventType_MouseEntered, - nzEventType_MouseLeft, - nzEventType_MouseMoved, - nzEventType_MouseWheelMoved, - nzEventType_Moved, - nzEventType_Quit, - nzEventType_Resized, - nzEventType_TextEntered -}; - -enum nzExtend -{ - nzExtend_Finite, - nzExtend_Infinite, - nzExtend_Null -}; - -enum nzImageType -{ - nzImageType_1D, - nzImageType_1D_Array, - nzImageType_2D, - nzImageType_2D_Array, - nzImageType_3D, - nzImageType_Cubemap, - - nzImageType_Max = nzImageType_Cubemap -}; - -enum nzPixelFormat -{ - nzPixelFormat_Undefined = -1, - - nzPixelFormat_BGR8, // 3*nzUInt8 - nzPixelFormat_BGRA8, // 4*nzUInt8 - nzPixelFormat_DXT1, - nzPixelFormat_DXT3, - nzPixelFormat_DXT5, - nzPixelFormat_L8, // 1*nzUInt8 - nzPixelFormat_LA8, // 2*nzUInt8 - /* - nzPixelFormat_RGB16F, - nzPixelFormat_RGB16I, // 4*nzUInt16 - nzPixelFormat_RGB32F, - nzPixelFormat_RGB32I, // 4*nzUInt32 - nzPixelFormat_RGBA16F, - nzPixelFormat_RGBA16I, // 4*nzUInt16 - nzPixelFormat_RGBA32F, - nzPixelFormat_RGBA32I, // 4*nzUInt32 - */ - nzPixelFormat_RGBA4, // 1*nzUInt16 - nzPixelFormat_RGB5A1, // 1*nzUInt16 - nzPixelFormat_RGB8, // 3*nzUInt8 - nzPixelFormat_RGBA8, // 4*nzUInt8 - /* - nzPixelFormat_Depth16, - nzPixelFormat_Depth24, - nzPixelFormat_Depth24Stencil8, - nzPixelFormat_Depth32, - nzPixelFormat_Stencil1, - nzPixelFormat_Stencil4, - nzPixelFormat_Stencil8, - nzPixelFormat_Stencil16, - */ - - nzPixelFormat_Max = nzPixelFormat_RGBA8 -}; - -enum nzPixelFlipping -{ - nzPixelFlipping_Horizontally, - nzPixelFlipping_Vertically, - - nzPixelFlipping_Max = nzPixelFlipping_Vertically -}; - -enum nzPrimitiveType -{ - nzPrimitiveType_LineList, - nzPrimitiveType_LineStrip, - nzPrimitiveType_PointList, - nzPrimitiveType_TriangleList, - nzPrimitiveType_TriangleStrip, - nzPrimitiveType_TriangleFan -}; - -enum nzWindowCursor -{ - nzWindowCursor_None, - nzWindowCursor_Default, - - nzWindowCursor_Crosshair, - nzWindowCursor_Hand, - nzWindowCursor_Help, - nzWindowCursor_Move, - nzWindowCursor_Pointer, - nzWindowCursor_Progress, - nzWindowCursor_ResizeE, - nzWindowCursor_ResizeN, - nzWindowCursor_ResizeNE, - nzWindowCursor_ResizeNW, - nzWindowCursor_ResizeS, - nzWindowCursor_ResizeSE, - nzWindowCursor_ResizeSW, - nzWindowCursor_ResizeW, - nzWindowCursor_Text, - nzWindowCursor_Wait -}; - -enum nzWindowStyle -{ - nzWindowStyle_None = 0x0, - nzWindowStyle_Fullscreen = 0x1, - - nzWindowStyle_Closable = 0x2, - nzWindowStyle_Resizable = 0x4, - nzWindowStyle_Titlebar = 0x4, - - nzWindowStyle_Default = nzWindowStyle_Closable | nzWindowStyle_Resizable | nzWindowStyle_Titlebar -}; - -#endif // NAZARA_ENUMS_UTILITY_HPP +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_ENUMS_UTILITY_HPP +#define NAZARA_ENUMS_UTILITY_HPP + +enum nzAnimationType +{ + nzAnimationType_Keyframe, + nzAnimationType_Skeletal, + nzAnimationType_Static, + + nzAnimationType_Max = nzAnimationType_Static +}; + +enum nzBufferAccess +{ + nzBufferAccess_DiscardAndWrite, + nzBufferAccess_ReadOnly, + nzBufferAccess_ReadWrite, + nzBufferAccess_WriteOnly, + + nzBufferAccess_Max = nzBufferAccess_WriteOnly +}; + +enum nzBufferStorage +{ + //nzBufferStorage_Both, + nzBufferStorage_Hardware, + nzBufferStorage_Software, + + nzBufferStorage_Max = nzBufferStorage_Software +}; + +enum nzBufferType +{ + nzBufferType_Index, + nzBufferType_Vertex, + + nzBufferType_Max = nzBufferType_Vertex +}; + +enum nzBufferUsage +{ + nzBufferUsage_Dynamic, + nzBufferUsage_Static, + + nzBufferUsage_Max = nzBufferUsage_Static +}; + +enum nzCubemapFace +{ + // Cette énumération est prévue pour remplacer l'argument "z" des méthodes de NzImage contenant un cubemap + // L'ordre est X, -X, Y, -Y, Z, -Z + nzCubemapFace_PositiveX = 0, + nzCubemapFace_PositiveY = 2, + nzCubemapFace_PositiveZ = 4, + nzCubemapFace_NegativeX = 1, + nzCubemapFace_NegativeY = 3, + nzCubemapFace_NegativeZ = 5, + + nzCubemapFace_Max = nzCubemapFace_NegativeZ +}; + +enum nzElementStream +{ + nzElementStream_VertexData, + nzElementStream_InstancedData, + + nzElementStream_Max = nzElementStream_InstancedData +}; + +enum nzElementType +{ + nzElementType_Color, + nzElementType_Double1, + nzElementType_Double2, + nzElementType_Double3, + nzElementType_Double4, + nzElementType_Float1, + nzElementType_Float2, + nzElementType_Float3, + nzElementType_Float4, + + nzElementType_Max = nzElementType_Float4 +}; + +enum nzElementUsage +{ + nzElementUsage_Diffuse, + nzElementUsage_Normal, + nzElementUsage_Position, + nzElementUsage_Tangent, + nzElementUsage_TexCoord, + + nzElementUsage_Max = nzElementUsage_TexCoord +}; + +enum nzEventType +{ + nzEventType_GainedFocus, + nzEventType_LostFocus, + nzEventType_KeyPressed, + nzEventType_KeyReleased, + nzEventType_MouseButtonDoubleClicked, + nzEventType_MouseButtonPressed, + nzEventType_MouseButtonReleased, + nzEventType_MouseEntered, + nzEventType_MouseLeft, + nzEventType_MouseMoved, + nzEventType_MouseWheelMoved, + nzEventType_Moved, + nzEventType_Quit, + nzEventType_Resized, + nzEventType_TextEntered, + + nzEventType_Max = nzEventType_TextEntered +}; + +enum nzExtend +{ + nzExtend_Finite, + nzExtend_Infinite, + nzExtend_Null, + + nzExtend_Max = nzExtend_Null +}; + +enum nzImageType +{ + nzImageType_1D, + nzImageType_1D_Array, + nzImageType_2D, + nzImageType_2D_Array, + nzImageType_3D, + nzImageType_Cubemap, + + nzImageType_Max = nzImageType_Cubemap +}; + +enum nzPixelFormat +{ + nzPixelFormat_Undefined = -1, + + nzPixelFormat_BGR8, // 3*nzUInt8 + nzPixelFormat_BGRA8, // 4*nzUInt8 + nzPixelFormat_DXT1, + nzPixelFormat_DXT3, + nzPixelFormat_DXT5, + nzPixelFormat_L8, // 1*nzUInt8 + nzPixelFormat_LA8, // 2*nzUInt8 + /* + nzPixelFormat_RGB16F, + nzPixelFormat_RGB16I, // 4*nzUInt16 + nzPixelFormat_RGB32F, + nzPixelFormat_RGB32I, // 4*nzUInt32 + nzPixelFormat_RGBA16F, + nzPixelFormat_RGBA16I, // 4*nzUInt16 + nzPixelFormat_RGBA32F, + nzPixelFormat_RGBA32I, // 4*nzUInt32 + */ + nzPixelFormat_RGBA4, // 1*nzUInt16 + nzPixelFormat_RGB5A1, // 1*nzUInt16 + nzPixelFormat_RGB8, // 3*nzUInt8 + nzPixelFormat_RGBA8, // 4*nzUInt8 + nzPixelFormat_Depth16, + nzPixelFormat_Depth24, + nzPixelFormat_Depth24Stencil8, + nzPixelFormat_Depth32, + nzPixelFormat_Stencil1, + nzPixelFormat_Stencil4, + nzPixelFormat_Stencil8, + nzPixelFormat_Stencil16, + + nzPixelFormat_Max = nzPixelFormat_Stencil16 +}; + +enum nzPixelFormatType +{ + nzPixelFormatType_Undefined = -1, + + nzPixelFormatType_Color, + nzPixelFormatType_Depth, + nzPixelFormatType_DepthStencil, + nzPixelFormatType_Stencil, + + nzPixelFormatType_Max = nzPixelFormatType_Stencil +}; + +enum nzPixelFlipping +{ + nzPixelFlipping_Horizontally, + nzPixelFlipping_Vertically, + + nzPixelFlipping_Max = nzPixelFlipping_Vertically +}; + +enum nzPrimitiveType +{ + nzPrimitiveType_LineList, + nzPrimitiveType_LineStrip, + nzPrimitiveType_PointList, + nzPrimitiveType_TriangleList, + nzPrimitiveType_TriangleStrip, + nzPrimitiveType_TriangleFan, + + nzPrimitiveType_Max = nzPrimitiveType_TriangleFan +}; + +enum nzWindowCursor +{ + nzWindowCursor_None, + nzWindowCursor_Default, + + nzWindowCursor_Crosshair, + nzWindowCursor_Hand, + nzWindowCursor_Help, + nzWindowCursor_Move, + nzWindowCursor_Pointer, + nzWindowCursor_Progress, + nzWindowCursor_ResizeE, + nzWindowCursor_ResizeN, + nzWindowCursor_ResizeNE, + nzWindowCursor_ResizeNW, + nzWindowCursor_ResizeS, + nzWindowCursor_ResizeSE, + nzWindowCursor_ResizeSW, + nzWindowCursor_ResizeW, + nzWindowCursor_Text, + nzWindowCursor_Wait, + + nzWindowCursor_Max = nzWindowCursor_Wait +}; + +enum nzWindowStyleFlags +{ + nzWindowStyle_None = 0x0, + nzWindowStyle_Fullscreen = 0x1, + + nzWindowStyle_Closable = 0x2, + nzWindowStyle_Resizable = 0x4, + nzWindowStyle_Titlebar = 0x4, + + nzWindowStyle_Default = nzWindowStyle_Closable | nzWindowStyle_Resizable | nzWindowStyle_Titlebar +}; + +#endif // NAZARA_ENUMS_UTILITY_HPP diff --git a/include/Nazara/Utility/Mesh.hpp b/include/Nazara/Utility/Mesh.hpp index c8ca8b814..de8f071f7 100644 --- a/include/Nazara/Utility/Mesh.hpp +++ b/include/Nazara/Utility/Mesh.hpp @@ -1,93 +1,98 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Utility module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef NAZARA_MESH_HPP -#define NAZARA_MESH_HPP - -#include -#include -#include -#include -#include -#include -#include -#include - -class NzVertexDeclaration; - -struct NzMeshParams -{ - NzAnimationParams animation; - //const NzVertexDeclaration* declaration = nullptr; - nzBufferStorage storage = nzBufferStorage_Hardware; - bool loadAnimations = true; - - bool IsValid() const; -}; - -class NzMesh; - -using NzMeshLoader = NzResourceLoader; - -struct NzMeshImpl; - -class NAZARA_API NzMesh : public NzResource -{ - friend NzMeshLoader; - - public: - NzMesh() = default; - ~NzMesh(); - - unsigned int AddSkin(const NzString& skin, bool setDefault = false); - nzUInt8 AddSubMesh(NzSubMesh* subMesh); - nzUInt8 AddSubMesh(const NzString& identifier, NzSubMesh* subMesh); - - void Animate(unsigned int frameA, unsigned int frameB, float interpolation); - - bool Create(nzAnimationType type); - void Destroy(); - - const NzAxisAlignedBox& GetAABB() const; - const NzAnimation* GetAnimation() const; - nzAnimationType GetAnimationType() const; - unsigned int GetFrameCount() const; - NzString GetSkin(unsigned int index = 0) const; - unsigned int GetSkinCount() const; - NzSubMesh* GetSubMesh(const NzString& identifier); - NzSubMesh* GetSubMesh(nzUInt8 index); - const NzSubMesh* GetSubMesh(const NzString& identifier) const; - const NzSubMesh* GetSubMesh(nzUInt8 index) const; - nzUInt8 GetSubMeshCount() const; - unsigned int GetVertexCount() const; - - bool HasAnimation() const; - bool HasSkin(unsigned int index = 0) const; - bool HasSubMesh(const NzString& identifier) const; - bool HasSubMesh(nzUInt8 index = 0) const; - - void InvalidateAABB() const; - - bool IsAnimable() const; - bool IsValid() const; - - bool LoadFromFile(const NzString& filePath, const NzMeshParams& params = NzMeshParams()); - bool LoadFromMemory(const void* data, std::size_t size, const NzMeshParams& params = NzMeshParams()); - bool LoadFromStream(NzInputStream& stream, const NzMeshParams& params = NzMeshParams()); - - void RemoveSkin(unsigned int index = 0); - void RemoveSubMesh(const NzString& identifier); - void RemoveSubMesh(nzUInt8 index = 0); - - bool SetAnimation(const NzAnimation* animation); - - private: - NzMeshImpl* m_impl = nullptr; - - static NzMeshLoader::LoaderList s_loaders; -}; - -#endif // NAZARA_MESH_HPP +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_MESH_HPP +#define NAZARA_MESH_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class NzVertexDeclaration; + +struct NzMeshParams +{ + NzAnimationParams animation; + //const NzVertexDeclaration* declaration = nullptr; + nzBufferStorage storage = nzBufferStorage_Hardware; + bool loadAnimations = true; + + bool IsValid() const; +}; + +class NzMesh; + +using NzMeshLoader = NzResourceLoader; + +struct NzMeshImpl; + +class NAZARA_API NzMesh : public NzResource, NzResourceListener +{ + friend NzMeshLoader; + + public: + NzMesh() = default; + ~NzMesh(); + + bool AddSkin(const NzString& skin, bool setDefault = false); + bool AddSubMesh(NzSubMesh* subMesh); + bool AddSubMesh(const NzString& identifier, NzSubMesh* subMesh); + + void Animate(unsigned int frameA, unsigned int frameB, float interpolation); + + bool Create(nzAnimationType type); + void Destroy(); + + const NzAxisAlignedBox& GetAABB() const; + const NzAnimation* GetAnimation() const; + nzAnimationType GetAnimationType() const; + unsigned int GetFrameCount() const; + NzString GetSkin(unsigned int index = 0) const; + unsigned int GetSkinCount() const; + NzSubMesh* GetSubMesh(const NzString& identifier); + NzSubMesh* GetSubMesh(unsigned int index); + const NzSubMesh* GetSubMesh(const NzString& identifier) const; + const NzSubMesh* GetSubMesh(unsigned int index) const; + unsigned int GetSubMeshCount() const; + int GetSubMeshIndex(const NzString& identifier) const; + unsigned int GetVertexCount() const; + + bool HasAnimation() const; + bool HasSkin(unsigned int index = 0) const; + bool HasSubMesh(const NzString& identifier) const; + bool HasSubMesh(unsigned int index = 0) const; + + void InvalidateAABB() const; + + bool IsAnimable() const; + bool IsValid() const; + + bool LoadFromFile(const NzString& filePath, const NzMeshParams& params = NzMeshParams()); + bool LoadFromMemory(const void* data, std::size_t size, const NzMeshParams& params = NzMeshParams()); + bool LoadFromStream(NzInputStream& stream, const NzMeshParams& params = NzMeshParams()); + + void RemoveSkin(unsigned int index = 0); + void RemoveSubMesh(const NzString& identifier); + void RemoveSubMesh(unsigned int index = 0); + + bool SetAnimation(const NzAnimation* animation); + + private: + void OnResourceCreated(const NzResource* resource, int index) override; + void OnResourceReleased(const NzResource* resource, int index) override; + + NzMeshImpl* m_impl = nullptr; + + static NzMeshLoader::LoaderList s_loaders; +}; + +#endif // NAZARA_MESH_HPP diff --git a/include/Nazara/Utility/PixelFormat.hpp b/include/Nazara/Utility/PixelFormat.hpp index 396fa8056..646f91e55 100644 --- a/include/Nazara/Utility/PixelFormat.hpp +++ b/include/Nazara/Utility/PixelFormat.hpp @@ -1,53 +1,54 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Utility module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef NAZARA_PIXELFORMAT_HPP -#define NAZARA_PIXELFORMAT_HPP - -#include -#include -#include -#include - -class NzUtility; - -class NzPixelFormat -{ - friend class NzUtility; - - public: - using ConvertFunction = nzUInt8* (*)(const nzUInt8* start, const nzUInt8* end, nzUInt8* dst); - using FlipFunction = void (*)(unsigned int width, unsigned int height, unsigned int depth, const nzUInt8* src, nzUInt8* dst); - - static bool Convert(nzPixelFormat srcFormat, nzPixelFormat dstFormat, const void* src, void* dst); - static bool Convert(nzPixelFormat srcFormat, nzPixelFormat dstFormat, const void* start, const void* end, void* dst); - - static bool Flip(nzPixelFlipping flipping, nzPixelFormat format, unsigned int width, unsigned int height, unsigned int depth, const void* src, void* dst); - - static nzUInt8 GetBPP(nzPixelFormat format); - - static bool HasAlpha(nzPixelFormat format); - - static bool IsCompressed(nzPixelFormat format); - static bool IsConversionSupported(nzPixelFormat srcFormat, nzPixelFormat dstFormat); - static bool IsValid(nzPixelFormat format); - - static void SetConvertFunction(nzPixelFormat srcFormat, nzPixelFormat dstFormat, ConvertFunction func); - static void SetFlipFunction(nzPixelFlipping flipping, nzPixelFormat format, FlipFunction func); - - static NzString ToString(nzPixelFormat format); - - private: - static bool Initialize(); - static void Uninitialize(); - - static NAZARA_API ConvertFunction s_convertFunctions[nzPixelFormat_Max+1][nzPixelFormat_Max+1]; - static NAZARA_API std::map s_flipFunctions[nzPixelFlipping_Max+1]; -}; - -#include - -#endif // NAZARA_PIXELFORMAT_HPP +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_PIXELFORMAT_HPP +#define NAZARA_PIXELFORMAT_HPP + +#include +#include +#include +#include + +class NzUtility; + +class NzPixelFormat +{ + friend class NzUtility; + + public: + using ConvertFunction = nzUInt8* (*)(const nzUInt8* start, const nzUInt8* end, nzUInt8* dst); + using FlipFunction = void (*)(unsigned int width, unsigned int height, unsigned int depth, const nzUInt8* src, nzUInt8* dst); + + static bool Convert(nzPixelFormat srcFormat, nzPixelFormat dstFormat, const void* src, void* dst); + static bool Convert(nzPixelFormat srcFormat, nzPixelFormat dstFormat, const void* start, const void* end, void* dst); + + static bool Flip(nzPixelFlipping flipping, nzPixelFormat format, unsigned int width, unsigned int height, unsigned int depth, const void* src, void* dst); + + static nzUInt8 GetBPP(nzPixelFormat format); + static nzPixelFormatType GetType(nzPixelFormat format); + + static bool HasAlpha(nzPixelFormat format); + + static bool IsCompressed(nzPixelFormat format); + static bool IsConversionSupported(nzPixelFormat srcFormat, nzPixelFormat dstFormat); + static bool IsValid(nzPixelFormat format); + + static void SetConvertFunction(nzPixelFormat srcFormat, nzPixelFormat dstFormat, ConvertFunction func); + static void SetFlipFunction(nzPixelFlipping flipping, nzPixelFormat format, FlipFunction func); + + static NzString ToString(nzPixelFormat format); + + private: + static bool Initialize(); + static void Uninitialize(); + + static NAZARA_API ConvertFunction s_convertFunctions[nzPixelFormat_Max+1][nzPixelFormat_Max+1]; + static NAZARA_API std::map s_flipFunctions[nzPixelFlipping_Max+1]; +}; + +#include + +#endif // NAZARA_PIXELFORMAT_HPP diff --git a/include/Nazara/Utility/PixelFormat.inl b/include/Nazara/Utility/PixelFormat.inl index 9660a3d63..8f3335af0 100644 --- a/include/Nazara/Utility/PixelFormat.inl +++ b/include/Nazara/Utility/PixelFormat.inl @@ -1,371 +1,487 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Utility module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include - -inline bool NzPixelFormat::Convert(nzPixelFormat srcFormat, nzPixelFormat dstFormat, const void* src, void* dst) -{ - if (srcFormat == dstFormat) - { - std::memcpy(dst, src, GetBPP(srcFormat)); - return true; - } - - #if NAZARA_UTILITY_SAFE - if (IsCompressed(srcFormat)) - { - NazaraError("Cannot convert single pixel from compressed format"); - return false; - } - - if (IsCompressed(dstFormat)) - { - NazaraError("Cannot convert single pixel to compressed format"); - return false; - } - #endif - - ConvertFunction func = s_convertFunctions[srcFormat][dstFormat]; - if (!func) - { - NazaraError("Pixel format conversion from " + ToString(srcFormat) + " to " + ToString(dstFormat) + " is not supported"); - return false; - } - - if (!func(reinterpret_cast(src), reinterpret_cast(src) + GetBPP(srcFormat), reinterpret_cast(dst))) - { - NazaraError("Pixel format conversion from " + ToString(srcFormat) + " to " + ToString(dstFormat) + " failed"); - return false; - } - - return true; -} - -inline bool NzPixelFormat::Convert(nzPixelFormat srcFormat, nzPixelFormat dstFormat, const void* start, const void* end, void* dst) -{ - if (srcFormat == dstFormat) - { - std::memcpy(dst, start, reinterpret_cast(end)-reinterpret_cast(start)); - return true; - } - - ConvertFunction func = s_convertFunctions[srcFormat][dstFormat]; - if (!func) - { - NazaraError("Pixel format conversion from " + ToString(srcFormat) + " to " + ToString(dstFormat) + " is not supported"); - return false; - } - - if (!func(reinterpret_cast(start), reinterpret_cast(end), reinterpret_cast(dst))) - { - NazaraError("Pixel format conversion from " + ToString(srcFormat) + " to " + ToString(dstFormat) + " failed"); - return false; - } - - return true; -} - -inline bool NzPixelFormat::Flip(nzPixelFlipping flipping, nzPixelFormat format, unsigned int width, unsigned int height, unsigned int depth, const void* src, void* dst) -{ - #if NAZARA_UTILITY_SAFE - if (!IsValid(format)) - { - NazaraError("Invalid pixel format"); - return false; - } - #endif - - auto it = s_flipFunctions[flipping].find(format); - if (it != s_flipFunctions[flipping].end()) - it->second(width, height, depth, reinterpret_cast(src), reinterpret_cast(dst)); - else - { - // Flipping générique - - #if NAZARA_UTILITY_SAFE - if (IsCompressed(format)) - { - NazaraError("No function to flip compressed format"); - return false; - } - #endif - - nzUInt8 bpp = GetBPP(format); - unsigned int lineStride = width*bpp; - switch (flipping) - { - case nzPixelFlipping_Horizontally: - { - if (src == dst) - { - for (unsigned int z = 0; z < depth; ++z) - { - nzUInt8* ptr = reinterpret_cast(dst) + width*height*z; - for (unsigned int y = 0; y < height; ++y) - { - for (unsigned int x = 0; x < width/2; ++x) - std::swap_ranges(&ptr[x*bpp], &ptr[(x+1)*bpp], &ptr[(width-x)*bpp]); - - ptr += lineStride; - } - } - } - else - { - for (unsigned int z = 0; z < depth; ++z) - { - nzUInt8* ptr = reinterpret_cast(dst) + width*height*z; - for (unsigned int y = 0; y < height; ++y) - { - for (unsigned int x = 0; x < width; ++x) - std::memcpy(&ptr[x*bpp], &ptr[(width-x)*bpp], bpp); - - ptr += lineStride; - } - } - } - break; - } - - case nzPixelFlipping_Vertically: - { - if (src == dst) - { - for (unsigned int z = 0; z < depth; ++z) - { - nzUInt8* ptr = reinterpret_cast(dst) + width*height*z; - for (unsigned int y = 0; y < height/2; ++y) - std::swap_ranges(&ptr[y*lineStride], &ptr[(y+1)*lineStride-1], &ptr[(height-y-1)*lineStride]); - } - } - else - { - for (unsigned int z = 0; z < depth; ++z) - { - const nzUInt8* srcPtr = reinterpret_cast(src); - nzUInt8* dstPtr = reinterpret_cast(dst) + (width-1)*height*depth*bpp; - for (unsigned int y = 0; y < height; ++y) - { - std::memcpy(dstPtr, srcPtr, lineStride); - - srcPtr += lineStride; - dstPtr -= lineStride; - } - } - } - break; - } - } - } - - return true; -} - -inline nzUInt8 NzPixelFormat::GetBPP(nzPixelFormat format) -{ - switch (format) - { - case nzPixelFormat_BGR8: - return 3; - - case nzPixelFormat_BGRA8: - return 4; - - case nzPixelFormat_DXT1: - return 1; - - case nzPixelFormat_DXT3: - return 2; - - case nzPixelFormat_DXT5: - return 2; - - case nzPixelFormat_L8: - return 1; - - case nzPixelFormat_LA8: - return 2; -/* - case nzPixelFormat_RGB16F: - return 6; - - case nzPixelFormat_RGB16I: - return 6; - - case nzPixelFormat_RGB32F: - return 12; - - case nzPixelFormat_RGB32I: - return 12; - - case nzPixelFormat_RGBA16F: - return 8; - - case nzPixelFormat_RGBA16I: - return 8; - - case nzPixelFormat_RGBA32F: - return 16; - - case nzPixelFormat_RGBA32I: - return 16; -*/ - case nzPixelFormat_RGBA4: - return 2; - - case nzPixelFormat_RGB5A1: - return 2; - - case nzPixelFormat_RGB8: - return 3; - - case nzPixelFormat_RGBA8: - return 4; - - case nzPixelFormat_Undefined: - NazaraError("Invalid pixel format"); - return 0; - } - - NazaraInternalError("Invalid pixel format"); - - return 0; -} - -inline bool NzPixelFormat::HasAlpha(nzPixelFormat format) -{ - switch (format) - { - case nzPixelFormat_BGRA8: - case nzPixelFormat_DXT3: - case nzPixelFormat_DXT5: - case nzPixelFormat_LA8: - case nzPixelFormat_RGB5A1: - case nzPixelFormat_RGBA4: - case nzPixelFormat_RGBA8: - return true; - - case nzPixelFormat_BGR8: - case nzPixelFormat_DXT1: - case nzPixelFormat_L8: - case nzPixelFormat_RGB8: - return false; - - case nzPixelFormat_Undefined: - break; - } - - NazaraError("Invalid pixel format"); - return false; -} - -inline bool NzPixelFormat::IsCompressed(nzPixelFormat format) -{ - switch (format) - { - case nzPixelFormat_DXT1: - case nzPixelFormat_DXT3: - case nzPixelFormat_DXT5: - return true; - - default: - return false; - } -} - -inline bool NzPixelFormat::IsConversionSupported(nzPixelFormat srcFormat, nzPixelFormat dstFormat) -{ - if (srcFormat == dstFormat) - return true; - - return s_convertFunctions[srcFormat][dstFormat] != nullptr; -} - -inline bool NzPixelFormat::IsValid(nzPixelFormat format) -{ - return format != nzPixelFormat_Undefined; -} - -inline void NzPixelFormat::SetConvertFunction(nzPixelFormat srcFormat, nzPixelFormat dstFormat, ConvertFunction func) -{ - s_convertFunctions[srcFormat][dstFormat] = func; -} - -inline void NzPixelFormat::SetFlipFunction(nzPixelFlipping flipping, nzPixelFormat format, FlipFunction func) -{ - s_flipFunctions[flipping][format] = func; -} - -inline NzString NzPixelFormat::ToString(nzPixelFormat format) -{ - switch (format) - { - case nzPixelFormat_BGR8: - return "BGR8"; - - case nzPixelFormat_BGRA8: - return "BGRA8"; - - case nzPixelFormat_DXT1: - return "DXT1"; - - case nzPixelFormat_DXT3: - return "DXT3"; - - case nzPixelFormat_DXT5: - return "DXT5"; - - case nzPixelFormat_L8: - return "L8"; - - case nzPixelFormat_LA8: - return "LA8"; -/* - case nzPixelFormat_RGB16F: - return "RGB16F"; - - case nzPixelFormat_RGB16I: - return "RGB16I"; - - case nzPixelFormat_RGB32F: - return "RGB32F"; - - case nzPixelFormat_RGB32I: - return "RGB32I"; - - case nzPixelFormat_RGBA16F: - return "RGBA16F"; - - case nzPixelFormat_RGBA16I: - return "RGBA16I"; - - case nzPixelFormat_RGBA32F: - return "RGBA32F"; - - case nzPixelFormat_RGBA32I: - return "RGBA32I"; -*/ - case nzPixelFormat_RGBA4: - return "RGBA4"; - - case nzPixelFormat_RGB5A1: - return "RGB5A1"; - - case nzPixelFormat_RGB8: - return "RGB8"; - - case nzPixelFormat_RGBA8: - return "RGBA8"; - - case nzPixelFormat_Undefined: - return "Undefined"; - } - - NazaraError("Invalid pixel format"); - return "Invalid format"; -} - -#include +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include + +inline bool NzPixelFormat::Convert(nzPixelFormat srcFormat, nzPixelFormat dstFormat, const void* src, void* dst) +{ + if (srcFormat == dstFormat) + { + std::memcpy(dst, src, GetBPP(srcFormat)); + return true; + } + + #if NAZARA_UTILITY_SAFE + if (IsCompressed(srcFormat)) + { + NazaraError("Cannot convert single pixel from compressed format"); + return false; + } + + if (IsCompressed(dstFormat)) + { + NazaraError("Cannot convert single pixel to compressed format"); + return false; + } + #endif + + ConvertFunction func = s_convertFunctions[srcFormat][dstFormat]; + if (!func) + { + NazaraError("Pixel format conversion from " + ToString(srcFormat) + " to " + ToString(dstFormat) + " is not supported"); + return false; + } + + if (!func(reinterpret_cast(src), reinterpret_cast(src) + GetBPP(srcFormat), reinterpret_cast(dst))) + { + NazaraError("Pixel format conversion from " + ToString(srcFormat) + " to " + ToString(dstFormat) + " failed"); + return false; + } + + return true; +} + +inline bool NzPixelFormat::Convert(nzPixelFormat srcFormat, nzPixelFormat dstFormat, const void* start, const void* end, void* dst) +{ + if (srcFormat == dstFormat) + { + std::memcpy(dst, start, reinterpret_cast(end)-reinterpret_cast(start)); + return true; + } + + ConvertFunction func = s_convertFunctions[srcFormat][dstFormat]; + if (!func) + { + NazaraError("Pixel format conversion from " + ToString(srcFormat) + " to " + ToString(dstFormat) + " is not supported"); + return false; + } + + if (!func(reinterpret_cast(start), reinterpret_cast(end), reinterpret_cast(dst))) + { + NazaraError("Pixel format conversion from " + ToString(srcFormat) + " to " + ToString(dstFormat) + " failed"); + return false; + } + + return true; +} + +inline bool NzPixelFormat::Flip(nzPixelFlipping flipping, nzPixelFormat format, unsigned int width, unsigned int height, unsigned int depth, const void* src, void* dst) +{ + #if NAZARA_UTILITY_SAFE + if (!IsValid(format)) + { + NazaraError("Invalid pixel format"); + return false; + } + #endif + + auto it = s_flipFunctions[flipping].find(format); + if (it != s_flipFunctions[flipping].end()) + it->second(width, height, depth, reinterpret_cast(src), reinterpret_cast(dst)); + else + { + // Flipping générique + + #if NAZARA_UTILITY_SAFE + if (IsCompressed(format)) + { + NazaraError("No function to flip compressed format"); + return false; + } + #endif + + nzUInt8 bpp = GetBPP(format); + unsigned int lineStride = width*bpp; + switch (flipping) + { + case nzPixelFlipping_Horizontally: + { + if (src == dst) + { + for (unsigned int z = 0; z < depth; ++z) + { + nzUInt8* ptr = reinterpret_cast(dst) + width*height*z; + for (unsigned int y = 0; y < height; ++y) + { + for (unsigned int x = 0; x < width/2; ++x) + std::swap_ranges(&ptr[x*bpp], &ptr[(x+1)*bpp], &ptr[(width-x)*bpp]); + + ptr += lineStride; + } + } + } + else + { + for (unsigned int z = 0; z < depth; ++z) + { + nzUInt8* ptr = reinterpret_cast(dst) + width*height*z; + for (unsigned int y = 0; y < height; ++y) + { + for (unsigned int x = 0; x < width; ++x) + std::memcpy(&ptr[x*bpp], &ptr[(width-x)*bpp], bpp); + + ptr += lineStride; + } + } + } + break; + } + + case nzPixelFlipping_Vertically: + { + if (src == dst) + { + for (unsigned int z = 0; z < depth; ++z) + { + nzUInt8* ptr = reinterpret_cast(dst) + width*height*z; + for (unsigned int y = 0; y < height/2; ++y) + std::swap_ranges(&ptr[y*lineStride], &ptr[(y+1)*lineStride-1], &ptr[(height-y-1)*lineStride]); + } + } + else + { + for (unsigned int z = 0; z < depth; ++z) + { + const nzUInt8* srcPtr = reinterpret_cast(src); + nzUInt8* dstPtr = reinterpret_cast(dst) + (width-1)*height*depth*bpp; + for (unsigned int y = 0; y < height; ++y) + { + std::memcpy(dstPtr, srcPtr, lineStride); + + srcPtr += lineStride; + dstPtr -= lineStride; + } + } + } + break; + } + } + } + + return true; +} + +inline nzUInt8 NzPixelFormat::GetBPP(nzPixelFormat format) +{ + switch (format) + { + case nzPixelFormat_BGR8: + return 3; + + case nzPixelFormat_BGRA8: + return 4; + + case nzPixelFormat_DXT1: + return 1; + + case nzPixelFormat_DXT3: + return 2; + + case nzPixelFormat_DXT5: + return 2; + + case nzPixelFormat_L8: + return 1; + + case nzPixelFormat_LA8: + return 2; +/* + case nzPixelFormat_RGB16F: + return 6; + + case nzPixelFormat_RGB16I: + return 6; + + case nzPixelFormat_RGB32F: + return 12; + + case nzPixelFormat_RGB32I: + return 12; + + case nzPixelFormat_RGBA16F: + return 8; + + case nzPixelFormat_RGBA16I: + return 8; + + case nzPixelFormat_RGBA32F: + return 16; + + case nzPixelFormat_RGBA32I: + return 16; +*/ + case nzPixelFormat_RGBA4: + return 2; + + case nzPixelFormat_RGB5A1: + return 2; + + case nzPixelFormat_RGB8: + return 3; + + case nzPixelFormat_RGBA8: + return 4; + + case nzPixelFormat_Depth16: + return 2; + + case nzPixelFormat_Depth24: + return 3; + + case nzPixelFormat_Depth24Stencil8: + return 4; + + case nzPixelFormat_Depth32: + return 4; + + case nzPixelFormat_Stencil1: + NazaraWarning("This format uses less than one byte per pixel"); + return 0; + + case nzPixelFormat_Stencil4: + NazaraWarning("This format uses less than one byte per pixel"); + return 0; + + case nzPixelFormat_Stencil8: + return 1; + + case nzPixelFormat_Stencil16: + return 2; + + case nzPixelFormat_Undefined: + break; + } + + NazaraError("Invalid pixel format"); + return 0; +} + +inline nzPixelFormatType NzPixelFormat::GetType(nzPixelFormat format) +{ + switch (format) + { + case nzPixelFormat_BGR8: + case nzPixelFormat_BGRA8: + case nzPixelFormat_DXT1: + case nzPixelFormat_DXT3: + case nzPixelFormat_DXT5: + case nzPixelFormat_L8: + case nzPixelFormat_LA8: + case nzPixelFormat_RGB5A1: + case nzPixelFormat_RGB8: + case nzPixelFormat_RGBA4: + case nzPixelFormat_RGBA8: + return nzPixelFormatType_Color; + + case nzPixelFormat_Depth16: + case nzPixelFormat_Depth24: + case nzPixelFormat_Depth32: + return nzPixelFormatType_Depth; + + case nzPixelFormat_Depth24Stencil8: + return nzPixelFormatType_DepthStencil; + + case nzPixelFormat_Stencil1: + case nzPixelFormat_Stencil4: + case nzPixelFormat_Stencil8: + case nzPixelFormat_Stencil16: + return nzPixelFormatType_Stencil; + + case nzPixelFormat_Undefined: + break; + } + + NazaraError("Invalid pixel format"); + return nzPixelFormatType_Undefined; +} + +inline bool NzPixelFormat::HasAlpha(nzPixelFormat format) +{ + switch (format) + { + case nzPixelFormat_BGRA8: + case nzPixelFormat_DXT3: + case nzPixelFormat_DXT5: + case nzPixelFormat_LA8: + case nzPixelFormat_RGB5A1: + case nzPixelFormat_RGBA4: + case nzPixelFormat_RGBA8: + return true; + + case nzPixelFormat_BGR8: + case nzPixelFormat_DXT1: + case nzPixelFormat_L8: + case nzPixelFormat_RGB8: + case nzPixelFormat_Depth16: + case nzPixelFormat_Depth24: + case nzPixelFormat_Depth24Stencil8: + case nzPixelFormat_Depth32: + case nzPixelFormat_Stencil1: + case nzPixelFormat_Stencil4: + case nzPixelFormat_Stencil8: + case nzPixelFormat_Stencil16: + return false; + + case nzPixelFormat_Undefined: + break; + } + + NazaraError("Invalid pixel format"); + return false; +} + +inline bool NzPixelFormat::IsCompressed(nzPixelFormat format) +{ + switch (format) + { + case nzPixelFormat_DXT1: + case nzPixelFormat_DXT3: + case nzPixelFormat_DXT5: + return true; + + case nzPixelFormat_BGRA8: + case nzPixelFormat_LA8: + case nzPixelFormat_RGB5A1: + case nzPixelFormat_RGBA4: + case nzPixelFormat_RGBA8: + case nzPixelFormat_BGR8: + case nzPixelFormat_L8: + case nzPixelFormat_RGB8: + case nzPixelFormat_Depth16: + case nzPixelFormat_Depth24: + case nzPixelFormat_Depth24Stencil8: + case nzPixelFormat_Depth32: + case nzPixelFormat_Stencil1: + case nzPixelFormat_Stencil4: + case nzPixelFormat_Stencil8: + case nzPixelFormat_Stencil16: + return false; + + case nzPixelFormat_Undefined: + break; + } + + NazaraError("Invalid pixel format"); + return false; +} + +inline bool NzPixelFormat::IsConversionSupported(nzPixelFormat srcFormat, nzPixelFormat dstFormat) +{ + if (srcFormat == dstFormat) + return true; + + return s_convertFunctions[srcFormat][dstFormat] != nullptr; +} + +inline bool NzPixelFormat::IsValid(nzPixelFormat format) +{ + return format != nzPixelFormat_Undefined; +} + +inline void NzPixelFormat::SetConvertFunction(nzPixelFormat srcFormat, nzPixelFormat dstFormat, ConvertFunction func) +{ + s_convertFunctions[srcFormat][dstFormat] = func; +} + +inline void NzPixelFormat::SetFlipFunction(nzPixelFlipping flipping, nzPixelFormat format, FlipFunction func) +{ + s_flipFunctions[flipping][format] = func; +} + +inline NzString NzPixelFormat::ToString(nzPixelFormat format) +{ + switch (format) + { + case nzPixelFormat_BGR8: + return "BGR8"; + + case nzPixelFormat_BGRA8: + return "BGRA8"; + + case nzPixelFormat_DXT1: + return "DXT1"; + + case nzPixelFormat_DXT3: + return "DXT3"; + + case nzPixelFormat_DXT5: + return "DXT5"; + + case nzPixelFormat_L8: + return "L8"; + + case nzPixelFormat_LA8: + return "LA8"; +/* + case nzPixelFormat_RGB16F: + return "RGB16F"; + + case nzPixelFormat_RGB16I: + return "RGB16I"; + + case nzPixelFormat_RGB32F: + return "RGB32F"; + + case nzPixelFormat_RGB32I: + return "RGB32I"; + + case nzPixelFormat_RGBA16F: + return "RGBA16F"; + + case nzPixelFormat_RGBA16I: + return "RGBA16I"; + + case nzPixelFormat_RGBA32F: + return "RGBA32F"; + + case nzPixelFormat_RGBA32I: + return "RGBA32I"; +*/ + case nzPixelFormat_RGBA4: + return "RGBA4"; + + case nzPixelFormat_RGB5A1: + return "RGB5A1"; + + case nzPixelFormat_RGB8: + return "RGB8"; + + case nzPixelFormat_RGBA8: + return "RGBA8"; + + case nzPixelFormat_Depth16: + return "Depth16"; + + case nzPixelFormat_Depth24: + return "Depth24"; + + case nzPixelFormat_Depth24Stencil8: + return "Depth24Stencil8"; + + case nzPixelFormat_Depth32: + return "Depth32"; + + case nzPixelFormat_Stencil1: + return "Stencil1"; + + case nzPixelFormat_Stencil4: + return "Stencil4"; + + case nzPixelFormat_Stencil8: + return "Stencil8"; + + case nzPixelFormat_Stencil16: + return "Stencil16"; + + case nzPixelFormat_Undefined: + return "Undefined"; + } + + NazaraError("Invalid pixel format"); + return "Invalid format"; +} + +#include diff --git a/include/Nazara/Utility/StaticMesh.hpp b/include/Nazara/Utility/StaticMesh.hpp index 2d8ae3ec1..745ec1471 100644 --- a/include/Nazara/Utility/StaticMesh.hpp +++ b/include/Nazara/Utility/StaticMesh.hpp @@ -1,49 +1,51 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Utility module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef NAZARA_STATICMESH_HPP -#define NAZARA_STATICMESH_HPP - -#include -#include - -class NAZARA_API NzStaticMesh final : public NzSubMesh -{ - public: - NzStaticMesh(const NzMesh* parent); - NzStaticMesh(const NzMesh* parent, const NzVertexDeclaration* vertexDeclaration, NzVertexBuffer* vertexBuffer, NzIndexBuffer* indexBuffer = nullptr); - virtual ~NzStaticMesh(); - - bool Create(const NzVertexDeclaration* vertexDeclaration, NzVertexBuffer* vertexBuffer, NzIndexBuffer* indexBuffer = nullptr); - void Destroy(); - - bool GenerateAABB(); - - const NzAxisAlignedBox& GetAABB() const; - nzAnimationType GetAnimationType() const; - unsigned int GetFrameCount() const; - const NzIndexBuffer* GetIndexBuffer() const; - nzPrimitiveType GetPrimitiveType() const; - const NzVertexBuffer* GetVertexBuffer() const; - const NzVertexDeclaration* GetVertexDeclaration() const; - - bool IsAnimated() const; - bool IsValid() const; - - void SetAABB(const NzAxisAlignedBox& aabb); - void SetPrimitiveType(nzPrimitiveType primitiveType); - - private: - void AnimateImpl(unsigned int frameA, unsigned int frameB, float interpolation); - - nzPrimitiveType m_primitiveType = nzPrimitiveType_TriangleList; - NzAxisAlignedBox m_aabb; - NzIndexBuffer* m_indexBuffer = nullptr; - NzVertexBuffer* m_vertexBuffer = nullptr; - const NzVertexDeclaration* m_vertexDeclaration = nullptr; -}; - -#endif // NAZARA_STATICMESH_HPP +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_STATICMESH_HPP +#define NAZARA_STATICMESH_HPP + +#include +#include +#include + +class NAZARA_API NzStaticMesh final : public NzSubMesh, NzResourceListener +{ + public: + NzStaticMesh(const NzMesh* parent); + NzStaticMesh(const NzMesh* parent, const NzVertexDeclaration* vertexDeclaration, NzVertexBuffer* vertexBuffer, NzIndexBuffer* indexBuffer = nullptr); + virtual ~NzStaticMesh(); + + bool Create(const NzVertexDeclaration* vertexDeclaration, NzVertexBuffer* vertexBuffer, NzIndexBuffer* indexBuffer = nullptr); + void Destroy(); + + bool GenerateAABB(); + + const NzAxisAlignedBox& GetAABB() const; + nzAnimationType GetAnimationType() const; + unsigned int GetFrameCount() const; + const NzIndexBuffer* GetIndexBuffer() const; + nzPrimitiveType GetPrimitiveType() const; + const NzVertexBuffer* GetVertexBuffer() const; + const NzVertexDeclaration* GetVertexDeclaration() const; + + bool IsAnimated() const; + bool IsValid() const; + + void SetAABB(const NzAxisAlignedBox& aabb); + void SetPrimitiveType(nzPrimitiveType primitiveType); + + private: + void AnimateImpl(unsigned int frameA, unsigned int frameB, float interpolation); + void OnResourceReleased(const NzResource* resource, int index) override; + + nzPrimitiveType m_primitiveType = nzPrimitiveType_TriangleList; + NzAxisAlignedBox m_aabb; + NzIndexBuffer* m_indexBuffer = nullptr; + NzVertexBuffer* m_vertexBuffer = nullptr; + const NzVertexDeclaration* m_vertexDeclaration = nullptr; +}; + +#endif // NAZARA_STATICMESH_HPP diff --git a/include/Nazara/Utility/VertexDeclaration.hpp b/include/Nazara/Utility/VertexDeclaration.hpp index 09977ebe9..a13e40e00 100644 --- a/include/Nazara/Utility/VertexDeclaration.hpp +++ b/include/Nazara/Utility/VertexDeclaration.hpp @@ -1,53 +1,56 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Utility module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#ifndef NAZARA_VERTEXDECLARATION_HPP -#define NAZARA_VERTEXDECLARATION_HPP - -#include -#include -#include - -struct NzVertexElement -{ - unsigned int offset; - unsigned int usageIndex = 0; - nzElementStream stream = nzElementStream_VertexData; - nzElementType type; - nzElementUsage usage; -}; - -struct NzVertexDeclarationImpl; - -class NAZARA_API NzVertexDeclaration : public NzResource -{ - public: - NzVertexDeclaration() = default; - NzVertexDeclaration(const NzVertexElement* elements, unsigned int elementCount); - NzVertexDeclaration(const NzVertexDeclaration& declaration); - NzVertexDeclaration(NzVertexDeclaration&& declaration) noexcept; - ~NzVertexDeclaration(); - - bool Create(const NzVertexElement* elements, unsigned int elementCount); - void Destroy(); - - const NzVertexElement* GetElement(unsigned int i) const; - const NzVertexElement* GetElement(nzElementStream stream, unsigned int i) const; - const NzVertexElement* GetElement(nzElementStream stream, nzElementUsage usage, unsigned int usageIndex = 0) const; - unsigned int GetElementCount() const; - unsigned int GetElementCount(nzElementStream stream) const; - unsigned int GetStride(nzElementStream stream) const; - - bool HasStream(nzElementStream stream) const; - - bool IsValid() const; - - NzVertexDeclaration& operator=(const NzVertexDeclaration& declaration); - NzVertexDeclaration& operator=(NzVertexDeclaration&& declaration) noexcept; - - private: - NzVertexDeclarationImpl* m_sharedImpl = nullptr; -}; - -#endif // NAZARA_VERTEXDECLARATION_HPP +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#ifndef NAZARA_VERTEXDECLARATION_HPP +#define NAZARA_VERTEXDECLARATION_HPP + +#include +#include +#include + +struct NzVertexElement +{ + unsigned int offset; + unsigned int usageIndex = 0; + nzElementStream stream = nzElementStream_VertexData; + nzElementType type; + nzElementUsage usage; +}; + +struct NzVertexDeclarationImpl; + +class NAZARA_API NzVertexDeclaration : public NzResource +{ + public: + NzVertexDeclaration() = default; + NzVertexDeclaration(const NzVertexElement* elements, unsigned int elementCount); + NzVertexDeclaration(const NzVertexDeclaration& declaration); + NzVertexDeclaration(NzVertexDeclaration&& declaration) noexcept; + ~NzVertexDeclaration(); + + bool Create(const NzVertexElement* elements, unsigned int elementCount); + void Destroy(); + + const NzVertexElement* GetElement(unsigned int i) const; + const NzVertexElement* GetElement(nzElementStream stream, unsigned int i) const; + const NzVertexElement* GetElement(nzElementStream stream, nzElementUsage usage, unsigned int usageIndex = 0) const; + unsigned int GetElementCount() const; + unsigned int GetElementCount(nzElementStream stream) const; + unsigned int GetStride(nzElementStream stream) const; + + bool HasStream(nzElementStream stream) const; + + bool IsValid() const; + + NzVertexDeclaration& operator=(const NzVertexDeclaration& declaration); + NzVertexDeclaration& operator=(NzVertexDeclaration&& declaration) noexcept; + + static unsigned int GetElementCount(nzElementType type); + static unsigned int GetElementSize(nzElementType type); + + private: + NzVertexDeclarationImpl* m_sharedImpl = nullptr; +}; + +#endif // NAZARA_VERTEXDECLARATION_HPP diff --git a/include/Nazara/Utility/Window.hpp b/include/Nazara/Utility/Window.hpp index 2e14e3913..7864d64a1 100644 --- a/include/Nazara/Utility/Window.hpp +++ b/include/Nazara/Utility/Window.hpp @@ -1,111 +1,112 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Utility module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -// Interface inspirée de la SFML par Laurent Gomila - -#pragma once - -#ifndef NAZARA_WINDOW_HPP -#define NAZARA_WINDOW_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if NAZARA_UTILITY_THREADED_WINDOW -#include -#include -#endif - -class NzCursor; -class NzImage; -class NzIcon; -class NzWindowImpl; - -class NAZARA_API NzWindow : NzNonCopyable -{ - friend NzWindowImpl; - friend class NzMouse; - friend class NzUtility; - - public: - NzWindow(); - NzWindow(NzVideoMode mode, const NzString& title, nzUInt32 style = nzWindowStyle_Default); - NzWindow(NzWindowHandle handle); - virtual ~NzWindow(); - - bool Create(NzVideoMode mode, const NzString& title, nzUInt32 style = nzWindowStyle_Default); - bool Create(NzWindowHandle handle); - - void Destroy(); - - void EnableKeyRepeat(bool enable); - void EnableSmoothScrolling(bool enable); - - NzWindowHandle GetHandle() const; - unsigned int GetHeight() const; - NzVector2i GetPosition() const; - NzVector2ui GetSize() const; - NzString GetTitle() const; - unsigned int GetWidth() const; - - bool HasFocus() const; - - bool IsMinimized() const; - bool IsOpen() const; - bool IsVisible() const; - - bool PollEvent(NzEvent* event); - - void SetCursor(nzWindowCursor cursor); - void SetCursor(const NzCursor& cursor); - void SetEventListener(bool listener); - void SetFocus(); - void SetIcon(const NzIcon& icon); - void SetMaximumSize(const NzVector2i& maxSize); - void SetMaximumSize(int width, int height); - void SetMinimumSize(const NzVector2i& minSize); - void SetMinimumSize(int width, int height); - void SetPosition(const NzVector2i& position); - void SetPosition(int x, int y); - void SetSize(const NzVector2i& size); - void SetSize(unsigned int width, unsigned int height); - void SetStayOnTop(bool stayOnTop); - void SetTitle(const NzString& title); - void SetVisible(bool visible); - - bool WaitEvent(NzEvent* event); - - protected: - virtual void OnWindowDestroying(); - virtual bool OnWindowCreated(); - - NzWindowImpl* m_impl; - - private: - void IgnoreNextMouseEvent(int mouseX, int mouseY) const; - void PushEvent(const NzEvent& event); - - static bool Initialize(); - static void Uninitialize(); - - std::queue m_events; - #if NAZARA_UTILITY_THREADED_WINDOW - NzConditionVariable m_eventCondition; - NzMutex m_eventMutex; - NzMutex m_eventConditionMutex; - bool m_eventListener; - bool m_waitForEvent; - #endif - bool m_ownsWindow; -}; - -#endif // NAZARA_WINDOW_HPP +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +// Interface inspirée de la SFML par Laurent Gomila + +#pragma once + +#ifndef NAZARA_WINDOW_HPP +#define NAZARA_WINDOW_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if NAZARA_UTILITY_THREADED_WINDOW +#include +#include +#endif + +class NzCursor; +class NzImage; +class NzIcon; +class NzWindowImpl; + +class NAZARA_API NzWindow : NzNonCopyable +{ + friend NzWindowImpl; + friend class NzMouse; + friend class NzUtility; + + public: + NzWindow(); + NzWindow(NzVideoMode mode, const NzString& title, nzUInt32 style = nzWindowStyle_Default); + NzWindow(NzWindowHandle handle); + virtual ~NzWindow(); + + bool Create(NzVideoMode mode, const NzString& title, nzUInt32 style = nzWindowStyle_Default); + bool Create(NzWindowHandle handle); + + void Destroy(); + + void EnableKeyRepeat(bool enable); + void EnableSmoothScrolling(bool enable); + + NzWindowHandle GetHandle() const; + unsigned int GetHeight() const; + NzVector2i GetPosition() const; + NzVector2ui GetSize() const; + NzString GetTitle() const; + unsigned int GetWidth() const; + + bool HasFocus() const; + + bool IsMinimized() const; + bool IsOpen() const; + bool IsValid() const; + bool IsVisible() const; + + bool PollEvent(NzEvent* event); + + void SetCursor(nzWindowCursor cursor); + void SetCursor(const NzCursor& cursor); + void SetEventListener(bool listener); + void SetFocus(); + void SetIcon(const NzIcon& icon); + void SetMaximumSize(const NzVector2i& maxSize); + void SetMaximumSize(int width, int height); + void SetMinimumSize(const NzVector2i& minSize); + void SetMinimumSize(int width, int height); + void SetPosition(const NzVector2i& position); + void SetPosition(int x, int y); + void SetSize(const NzVector2i& size); + void SetSize(unsigned int width, unsigned int height); + void SetStayOnTop(bool stayOnTop); + void SetTitle(const NzString& title); + void SetVisible(bool visible); + + bool WaitEvent(NzEvent* event); + + protected: + virtual bool OnWindowCreated(); + virtual void OnWindowDestroy(); + + NzWindowImpl* m_impl; + + private: + void IgnoreNextMouseEvent(int mouseX, int mouseY) const; + void PushEvent(const NzEvent& event); + + static bool Initialize(); + static void Uninitialize(); + + std::queue m_events; + #if NAZARA_UTILITY_THREADED_WINDOW + NzConditionVariable m_eventCondition; + NzMutex m_eventMutex; + NzMutex m_eventConditionMutex; + bool m_eventListener; + bool m_waitForEvent; + #endif + bool m_ownsWindow; +}; + +#endif // NAZARA_WINDOW_HPP diff --git a/src/Nazara/Audio/SoundBuffer.cpp b/src/Nazara/Audio/SoundBuffer.cpp index 6c5bdd1ac..950c57da1 100644 --- a/src/Nazara/Audio/SoundBuffer.cpp +++ b/src/Nazara/Audio/SoundBuffer.cpp @@ -1,223 +1,229 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Audio module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include -#include -#include - -///FIXME: Adapter la création - -bool NzSoundBufferParams::IsValid() const -{ - return true; -} - -struct NzSoundBufferImpl -{ - ALuint buffer; - nzAudioFormat format; - nzUInt32 duration; - nzInt16* samples; - unsigned int sampleCount; - unsigned int sampleRate; -}; - -NzSoundBuffer::NzSoundBuffer(nzAudioFormat format, unsigned int sampleCount, unsigned int sampleRate, const nzInt16* samples) -{ - Create(format, sampleCount, sampleRate, samples); - - #ifdef NAZARA_DEBUG - if (!m_impl) - { - NazaraError("Failed to create sound buffer"); - throw std::runtime_error("Constructor failed"); - } - #endif -} - -NzSoundBuffer::~NzSoundBuffer() -{ - Destroy(); -} - -bool NzSoundBuffer::Create(nzAudioFormat format, unsigned int sampleCount, unsigned int sampleRate, const nzInt16* samples) -{ - Destroy(); - - if (sampleCount == 0) - return true; - - #if NAZARA_AUDIO_SAFE - if (!IsFormatSupported(format)) - { - NazaraError("Audio format is not supported"); - return false; - } - - if (sampleRate == 0) - { - NazaraError("Sample rate must be different from zero"); - return false; - } - - if (!samples) - { - NazaraError("Invalid sample source"); - return false; - } - #endif - - // On vide le stack d'erreurs - while (alGetError() != AL_NO_ERROR); - - ALuint buffer; - alGenBuffers(1, &buffer); - - if (alGetError() != AL_NO_ERROR) - { - NazaraError("Failed to create OpenAL buffer"); - return false; - } - - alBufferData(buffer, NzAudio::GetOpenALFormat(format), samples, sampleCount*sizeof(nzInt16), sampleRate); - - if (alGetError() != AL_NO_ERROR) - { - NazaraError("Failed to set OpenAL buffer"); - alDeleteBuffers(1, &buffer); - - return false; - } - - m_impl = new NzSoundBufferImpl; - m_impl->buffer = buffer; - m_impl->duration = 1000 * (sampleCount / (format * sampleRate)); - m_impl->format = format; - m_impl->sampleCount = sampleCount; - m_impl->sampleRate = sampleRate; - m_impl->samples = new nzInt16[sampleCount]; - std::memcpy(&m_impl->samples[0], samples, sampleCount*sizeof(nzInt16)); - - return true; -} - -void NzSoundBuffer::Destroy() -{ - if (m_impl) - { - delete[] m_impl->samples; - delete m_impl; - m_impl = nullptr; - } -} - -nzUInt32 NzSoundBuffer::GetDuration() const -{ - #if NAZARA_AUDIO_SAFE - if (!m_impl) - { - NazaraError("Sound buffer not created"); - return 0; - } - #endif - - return m_impl->duration; -} - -nzAudioFormat NzSoundBuffer::GetFormat() const -{ - #if NAZARA_AUDIO_SAFE - if (!m_impl) - { - NazaraError("Sound buffer not created"); - return nzAudioFormat_Unknown; - } - #endif - - return m_impl->format; -} - -const nzInt16* NzSoundBuffer::GetSamples() const -{ - #if NAZARA_AUDIO_SAFE - if (!m_impl) - { - NazaraError("Sound buffer not created"); - return nullptr; - } - #endif - - return m_impl->samples; -} - -unsigned int NzSoundBuffer::GetSampleCount() const -{ - #if NAZARA_AUDIO_SAFE - if (!m_impl) - { - NazaraError("Sound buffer not created"); - return 0; - } - #endif - - return m_impl->sampleCount; -} - -unsigned int NzSoundBuffer::GetSampleRate() const -{ - #if NAZARA_AUDIO_SAFE - if (!m_impl) - { - NazaraError("Sound buffer not created"); - return 0; - } - #endif - - return m_impl->sampleRate; -} - -bool NzSoundBuffer::IsValid() const -{ - return m_impl != nullptr; -} - -bool NzSoundBuffer::LoadFromFile(const NzString& filePath, const NzSoundBufferParams& params) -{ - return NzSoundBufferLoader::LoadFromFile(this, filePath, params); -} - -bool NzSoundBuffer::LoadFromMemory(const void* data, std::size_t size, const NzSoundBufferParams& params) -{ - return NzSoundBufferLoader::LoadFromMemory(this, data, size, params); -} - -bool NzSoundBuffer::LoadFromStream(NzInputStream& stream, const NzSoundBufferParams& params) -{ - return NzSoundBufferLoader::LoadFromStream(this, stream, params); -} - -bool NzSoundBuffer::IsFormatSupported(nzAudioFormat format) -{ - return NzAudio::IsFormatSupported(format); -} - -unsigned int NzSoundBuffer::GetOpenALBuffer() const -{ - #ifdef NAZARA_DEBUG - if (!m_impl) - { - NazaraInternalError("Sound buffer not created"); - return AL_NONE; - } - #endif - - return m_impl->buffer; -} - -NzSoundBufferLoader::LoaderList NzSoundBuffer::s_loaders; +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Audio module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include +#include + +///FIXME: Adapter la création + +bool NzSoundBufferParams::IsValid() const +{ + return true; +} + +struct NzSoundBufferImpl +{ + ALuint buffer; + nzAudioFormat format; + nzUInt32 duration; + nzInt16* samples; + unsigned int sampleCount; + unsigned int sampleRate; +}; + +NzSoundBuffer::NzSoundBuffer(nzAudioFormat format, unsigned int sampleCount, unsigned int sampleRate, const nzInt16* samples) +{ + Create(format, sampleCount, sampleRate, samples); + + #ifdef NAZARA_DEBUG + if (!m_impl) + { + NazaraError("Failed to create sound buffer"); + throw std::runtime_error("Constructor failed"); + } + #endif +} + +NzSoundBuffer::~NzSoundBuffer() +{ + Destroy(); +} + +bool NzSoundBuffer::Create(nzAudioFormat format, unsigned int sampleCount, unsigned int sampleRate, const nzInt16* samples) +{ + Destroy(); + + #if NAZARA_AUDIO_SAFE + if (!IsFormatSupported(format)) + { + NazaraError("Audio format is not supported"); + return false; + } + + if (sampleCount == 0) + { + NazaraError("Sample rate must be different from zero"); + return false; + } + + if (sampleRate == 0) + { + NazaraError("Sample rate must be different from zero"); + return false; + } + + if (!samples) + { + NazaraError("Invalid sample source"); + return false; + } + #endif + + // On vide le stack d'erreurs + while (alGetError() != AL_NO_ERROR); + + ALuint buffer; + alGenBuffers(1, &buffer); + + if (alGetError() != AL_NO_ERROR) + { + NazaraError("Failed to create OpenAL buffer"); + return false; + } + + alBufferData(buffer, NzAudio::GetOpenALFormat(format), samples, sampleCount*sizeof(nzInt16), sampleRate); + + if (alGetError() != AL_NO_ERROR) + { + NazaraError("Failed to set OpenAL buffer"); + alDeleteBuffers(1, &buffer); + + return false; + } + + m_impl = new NzSoundBufferImpl; + m_impl->buffer = buffer; + m_impl->duration = 1000 * (sampleCount / (format * sampleRate)); + m_impl->format = format; + m_impl->sampleCount = sampleCount; + m_impl->sampleRate = sampleRate; + m_impl->samples = new nzInt16[sampleCount]; + std::memcpy(&m_impl->samples[0], samples, sampleCount*sizeof(nzInt16)); + + NotifyCreated(); + return true; +} + +void NzSoundBuffer::Destroy() +{ + if (m_impl) + { + NotifyDestroy(); + + delete[] m_impl->samples; + delete m_impl; + m_impl = nullptr; + } +} + +nzUInt32 NzSoundBuffer::GetDuration() const +{ + #if NAZARA_AUDIO_SAFE + if (!m_impl) + { + NazaraError("Sound buffer not created"); + return 0; + } + #endif + + return m_impl->duration; +} + +nzAudioFormat NzSoundBuffer::GetFormat() const +{ + #if NAZARA_AUDIO_SAFE + if (!m_impl) + { + NazaraError("Sound buffer not created"); + return nzAudioFormat_Unknown; + } + #endif + + return m_impl->format; +} + +const nzInt16* NzSoundBuffer::GetSamples() const +{ + #if NAZARA_AUDIO_SAFE + if (!m_impl) + { + NazaraError("Sound buffer not created"); + return nullptr; + } + #endif + + return m_impl->samples; +} + +unsigned int NzSoundBuffer::GetSampleCount() const +{ + #if NAZARA_AUDIO_SAFE + if (!m_impl) + { + NazaraError("Sound buffer not created"); + return 0; + } + #endif + + return m_impl->sampleCount; +} + +unsigned int NzSoundBuffer::GetSampleRate() const +{ + #if NAZARA_AUDIO_SAFE + if (!m_impl) + { + NazaraError("Sound buffer not created"); + return 0; + } + #endif + + return m_impl->sampleRate; +} + +bool NzSoundBuffer::IsValid() const +{ + return m_impl != nullptr; +} + +bool NzSoundBuffer::LoadFromFile(const NzString& filePath, const NzSoundBufferParams& params) +{ + return NzSoundBufferLoader::LoadFromFile(this, filePath, params); +} + +bool NzSoundBuffer::LoadFromMemory(const void* data, std::size_t size, const NzSoundBufferParams& params) +{ + return NzSoundBufferLoader::LoadFromMemory(this, data, size, params); +} + +bool NzSoundBuffer::LoadFromStream(NzInputStream& stream, const NzSoundBufferParams& params) +{ + return NzSoundBufferLoader::LoadFromStream(this, stream, params); +} + +bool NzSoundBuffer::IsFormatSupported(nzAudioFormat format) +{ + return NzAudio::IsFormatSupported(format); +} + +unsigned int NzSoundBuffer::GetOpenALBuffer() const +{ + #ifdef NAZARA_DEBUG + if (!m_impl) + { + NazaraInternalError("Sound buffer not created"); + return AL_NONE; + } + #endif + + return m_impl->buffer; +} + +NzSoundBufferLoader::LoaderList NzSoundBuffer::s_loaders; diff --git a/src/Nazara/Core/ByteArray.cpp b/src/Nazara/Core/ByteArray.cpp index d390d6611..f5508af3e 100644 --- a/src/Nazara/Core/ByteArray.cpp +++ b/src/Nazara/Core/ByteArray.cpp @@ -1,265 +1,265 @@ -/* -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Core module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include - -inline unsigned int nzPow2(unsigned int n) -{ - unsigned int x = 1; - - while(x <= n) - x <<= 1; - - return x; -} -// Cet algorithme est inspiré de la documentation de Qt -inline unsigned int nzGetNewSize(unsigned int newSize) -{ - if (newSize < 20) - return newSize+4; - else - { - if (newSize < (1 << 12)-12) - return nzPow2(newSize << 1)-12; - else - return newSize + (1 << 11); - } -} - -NzByteArray::NzByteArray() : -m_sharedArray(&emptyArray) -{ -} - -NzByteArray::NzByteArray(const nzUInt8* buffer, unsigned int bufferLength) -{ - if (bufferLength > 0) - { - m_sharedArray = new SharedArray; - m_sharedArray->buffer = new nzUInt8[bufferLength]; - m_sharedArray->capacity = bufferLength; - m_sharedArray->size = bufferLength; - std::memcpy(m_sharedArray->buffer, buffer, bufferLength); - } - else - m_sharedArray = &emptyArray; -} - -NzByteArray::NzByteArray(const NzByteArray& buffer) : -m_sharedArray(buffer.m_sharedArray) -{ - if (m_sharedArray != &emptyArray) - { - NazaraMutexLock(m_sharedArray->mutex); - m_sharedArray->refCount++; - NazaraMutexUnlock(m_sharedArray->mutex); - } -} - -NzByteArray::NzByteArray(NzByteArray&& buffer) : -m_sharedArray(buffer.m_sharedArray) -{ - buffer.m_sharedArray = &emptyArray; -} - -NzByteArray::NzByteArray(SharedArray* sharedArray) : -m_sharedArray(sharedArray) -{ -} - -NzByteArray::~NzByteArray() -{ - ReleaseArray(); -} - -NzByteArray& NzByteArray::Append(const NzByteArray& byteArray) -{ - if (byteArray.m_sharedArray->size == 0) - return *this; - - if (m_sharedArray->size == 0 && m_sharedArray->capacity < byteArray.m_sharedArray->size) - return operator=(byteArray); - - if (m_sharedArray->capacity >= m_sharedArray->size + byteArray.m_sharedArray->size) - { - EnsureOwnership(); - - std::memcpy(&m_sharedArray->buffer[m_sharedArray->size], byteArray.m_sharedArray->buffer, byteArray.m_sharedArray->size); - m_sharedArray->size += byteArray.m_sharedArray->size; - } - else - { - unsigned int newSize = m_sharedArray->size + byteArray.m_sharedArray->size; - unsigned int bufferSize = nzGetNewSize(newSize); - - nzUInt8* buffer = new nzUInt8[bufferSize+1]; - std::memcpy(buffer, m_sharedArray->buffer, m_sharedArray->size); - std::memcpy(&buffer[m_sharedArray->size], byteArray.m_sharedArray->buffer, byteArray.m_sharedArray->size); - - ReleaseArray(); - m_sharedArray = new SharedArray; - m_sharedArray->buffer = buffer; - m_sharedArray->capacity = bufferSize; - m_sharedArray->size = newSize; - } - - return *this; -} - -void NzByteArray::Clear() -{ - ReleaseArray(); -} - -nzUInt8* NzByteArray::GetBuffer() -{ - EnsureOwnership(); - - return m_sharedArray->buffer; -} - -unsigned int NzByteArray::GetCapacity() const -{ - return m_sharedArray->capacity; -} - -const nzUInt8* NzByteArray::GetConstBuffer() const -{ - return m_sharedArray->buffer; -} - -unsigned int NzByteArray::GetSize() const -{ - return m_sharedArray->size; -} - -NzByteArray& NzByteArray::Insert(int pos, const nzUInt8* buffer, unsigned int bufferLength) -{ - if (bufferLength == 0) - return *this; - - if (m_sharedArray->size == 0) - return operator=(string); - - if (pos < 0) - pos = std::max(static_cast(m_sharedArray->size + pos), 0); - - unsigned int start = std::min(static_cast(pos), m_sharedArray->size); - - // Si le buffer est déjà suffisamment grand - if (m_sharedArray->capacity >= m_sharedArray->size + bufferLength) - { - EnsureOwnership(); - - std::memmove(&m_sharedArray->buffer[start+bufferLength], &m_sharedArray->buffer[start], m_sharedArray->size); - std::memcpy(&m_sharedArray->buffer[start], buffer, bufferLength); - - m_sharedArray->size += bufferLength; - } - else - { - unsigned int newSize = m_sharedArray->size+bufferLength; - nzUInt8* newBuffer = new nzUInt8[newSize+1]; - - nzUInt8* ptr = newBuffer; - const nzUInt8* s = m_sharedArray->buffer; - - while (ptr != &newBuffer[start]) - *ptr++ = *s++; - - while (ptr != &newBuffer[start+bufferLength]) - *ptr++ = *buffer++; - - std::strcpy(ptr, s); - - ReleaseString(); - m_sharedArray = new SharedString; - m_sharedArray->allocatedSize = newSize; - m_sharedArray->buffer = newBuffer; - m_sharedArray->size = newSize; - } - - return *this; -} - -NzByteArray& NzByteArray::Insert(int pos, const NzByteArray& byteArray) -{ - if (string.m_sharedArray->size == 0) - return *this; - - if (m_sharedArray->size == 0 && m_sharedArray->capacity < string.m_sharedArray->size) - return operator=(string); - - if (pos < 0) - pos = std::max(static_cast(m_sharedArray->size + pos), 0); - - unsigned int start = std::min(static_cast(pos), m_sharedArray->size); - - // Si le buffer est déjà suffisamment grand - if (m_sharedArray->capacity >= m_sharedArray->size + string.m_sharedArray->size) - { - EnsureOwnership(); - - std::memmove(&m_sharedArray->string[start+string.m_sharedArray->size], &m_sharedArray->string[start], m_sharedArray->size*sizeof(char)); - std::memcpy(&m_sharedArray->string[start], string.m_sharedArray->string, string.m_sharedArray->size*sizeof(char)); - - m_sharedArray->size += string.m_sharedArray->size; - } - else - { - unsigned int newSize = m_sharedArray->size+string.m_sharedArray->size; - char* newString = new char[newSize+1]; - - char* ptr = newString; - const char* s = m_sharedArray->string; - - while (ptr != &newString[start]) - *ptr++ = *s++; - - const char* p = string.m_sharedArray->string; - while (ptr != &newString[start+string.m_sharedArray->size]) - *ptr++ = *p++; - - std::strcpy(ptr, s); - - ReleaseString(); - m_sharedArray = new SharedString; - m_sharedArray->capacity = newSize; - m_sharedArray->size = newSize; - m_sharedArray->string = newString; - } - - return *this; -} - -bool NzByteArray::IsEmpty() const -{ - return m_sharedArray->size == 0; -} - -void NzByteArray::Reserve(unsigned int bufferSize) -{ - if (m_sharedArray->allocatedSize >= bufferSize) - return; - - nzUInt8* ptr = new nzUInt8[bufferSize+1]; - if (m_sharedArray->size > 0) - std::memcpy(ptr, m_sharedArray->buffer, m_sharedArray->size); - - unsigned int size = m_sharedArray->size; - - ReleaseArray(); - m_sharedArray = new SharedString; - m_sharedArray->allocatedSize = bufferSize; - m_sharedArray->buffer = ptr; - m_sharedArray->size = size; -} - -NzByteArray::SharedString NzByteArray::emptyArray(0, 0, 0, nullptr); -unsigned int NzByteArray::npos(static_cast(-1)); -*/ +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +/* +#include +#include +#include +#include + +inline unsigned int nzPow2(unsigned int n) +{ + unsigned int x = 1; + + while(x <= n) + x <<= 1; + + return x; +} +// Cet algorithme est inspiré de la documentation de Qt +inline unsigned int nzGetNewSize(unsigned int newSize) +{ + if (newSize < 20) + return newSize+4; + else + { + if (newSize < (1 << 12)-12) + return nzPow2(newSize << 1)-12; + else + return newSize + (1 << 11); + } +} + +NzByteArray::NzByteArray() : +m_sharedArray(&emptyArray) +{ +} + +NzByteArray::NzByteArray(const nzUInt8* buffer, unsigned int bufferLength) +{ + if (bufferLength > 0) + { + m_sharedArray = new SharedArray; + m_sharedArray->buffer = new nzUInt8[bufferLength]; + m_sharedArray->capacity = bufferLength; + m_sharedArray->size = bufferLength; + std::memcpy(m_sharedArray->buffer, buffer, bufferLength); + } + else + m_sharedArray = &emptyArray; +} + +NzByteArray::NzByteArray(const NzByteArray& buffer) : +m_sharedArray(buffer.m_sharedArray) +{ + if (m_sharedArray != &emptyArray) + { + NazaraMutexLock(m_sharedArray->mutex); + m_sharedArray->refCount++; + NazaraMutexUnlock(m_sharedArray->mutex); + } +} + +NzByteArray::NzByteArray(NzByteArray&& buffer) noexcept : +m_sharedArray(buffer.m_sharedArray) +{ + buffer.m_sharedArray = &emptyArray; +} + +NzByteArray::NzByteArray(SharedArray* sharedArray) : +m_sharedArray(sharedArray) +{ +} + +NzByteArray::~NzByteArray() +{ + ReleaseArray(); +} + +NzByteArray& NzByteArray::Append(const NzByteArray& byteArray) +{ + if (byteArray.m_sharedArray->size == 0) + return *this; + + if (m_sharedArray->size == 0 && m_sharedArray->capacity < byteArray.m_sharedArray->size) + return operator=(byteArray); + + if (m_sharedArray->capacity >= m_sharedArray->size + byteArray.m_sharedArray->size) + { + EnsureOwnership(); + + std::memcpy(&m_sharedArray->buffer[m_sharedArray->size], byteArray.m_sharedArray->buffer, byteArray.m_sharedArray->size); + m_sharedArray->size += byteArray.m_sharedArray->size; + } + else + { + unsigned int newSize = m_sharedArray->size + byteArray.m_sharedArray->size; + unsigned int bufferSize = nzGetNewSize(newSize); + + nzUInt8* buffer = new nzUInt8[bufferSize+1]; + std::memcpy(buffer, m_sharedArray->buffer, m_sharedArray->size); + std::memcpy(&buffer[m_sharedArray->size], byteArray.m_sharedArray->buffer, byteArray.m_sharedArray->size); + + ReleaseArray(); + m_sharedArray = new SharedArray; + m_sharedArray->buffer = buffer; + m_sharedArray->capacity = bufferSize; + m_sharedArray->size = newSize; + } + + return *this; +} + +void NzByteArray::Clear() +{ + ReleaseArray(); +} + +nzUInt8* NzByteArray::GetBuffer() +{ + EnsureOwnership(); + + return m_sharedArray->buffer; +} + +unsigned int NzByteArray::GetCapacity() const +{ + return m_sharedArray->capacity; +} + +const nzUInt8* NzByteArray::GetConstBuffer() const +{ + return m_sharedArray->buffer; +} + +unsigned int NzByteArray::GetSize() const +{ + return m_sharedArray->size; +} + +NzByteArray& NzByteArray::Insert(int pos, const nzUInt8* buffer, unsigned int bufferLength) +{ + if (bufferLength == 0) + return *this; + + if (m_sharedArray->size == 0) + return operator=(string); + + if (pos < 0) + pos = std::max(static_cast(m_sharedArray->size + pos), 0); + + unsigned int start = std::min(static_cast(pos), m_sharedArray->size); + + // Si le buffer est déjà suffisamment grand + if (m_sharedArray->capacity >= m_sharedArray->size + bufferLength) + { + EnsureOwnership(); + + std::memmove(&m_sharedArray->buffer[start+bufferLength], &m_sharedArray->buffer[start], m_sharedArray->size); + std::memcpy(&m_sharedArray->buffer[start], buffer, bufferLength); + + m_sharedArray->size += bufferLength; + } + else + { + unsigned int newSize = m_sharedArray->size+bufferLength; + nzUInt8* newBuffer = new nzUInt8[newSize+1]; + + nzUInt8* ptr = newBuffer; + const nzUInt8* s = m_sharedArray->buffer; + + while (ptr != &newBuffer[start]) + *ptr++ = *s++; + + while (ptr != &newBuffer[start+bufferLength]) + *ptr++ = *buffer++; + + std::strcpy(ptr, s); + + ReleaseString(); + m_sharedArray = new SharedString; + m_sharedArray->allocatedSize = newSize; + m_sharedArray->buffer = newBuffer; + m_sharedArray->size = newSize; + } + + return *this; +} + +NzByteArray& NzByteArray::Insert(int pos, const NzByteArray& byteArray) +{ + if (string.m_sharedArray->size == 0) + return *this; + + if (m_sharedArray->size == 0 && m_sharedArray->capacity < string.m_sharedArray->size) + return operator=(string); + + if (pos < 0) + pos = std::max(static_cast(m_sharedArray->size + pos), 0); + + unsigned int start = std::min(static_cast(pos), m_sharedArray->size); + + // Si le buffer est déjà suffisamment grand + if (m_sharedArray->capacity >= m_sharedArray->size + string.m_sharedArray->size) + { + EnsureOwnership(); + + std::memmove(&m_sharedArray->string[start+string.m_sharedArray->size], &m_sharedArray->string[start], m_sharedArray->size*sizeof(char)); + std::memcpy(&m_sharedArray->string[start], string.m_sharedArray->string, string.m_sharedArray->size*sizeof(char)); + + m_sharedArray->size += string.m_sharedArray->size; + } + else + { + unsigned int newSize = m_sharedArray->size+string.m_sharedArray->size; + char* newString = new char[newSize+1]; + + char* ptr = newString; + const char* s = m_sharedArray->string; + + while (ptr != &newString[start]) + *ptr++ = *s++; + + const char* p = string.m_sharedArray->string; + while (ptr != &newString[start+string.m_sharedArray->size]) + *ptr++ = *p++; + + std::strcpy(ptr, s); + + ReleaseString(); + m_sharedArray = new SharedString; + m_sharedArray->capacity = newSize; + m_sharedArray->size = newSize; + m_sharedArray->string = newString; + } + + return *this; +} + +bool NzByteArray::IsEmpty() const +{ + return m_sharedArray->size == 0; +} + +void NzByteArray::Reserve(unsigned int bufferSize) +{ + if (m_sharedArray->allocatedSize >= bufferSize) + return; + + nzUInt8* ptr = new nzUInt8[bufferSize+1]; + if (m_sharedArray->size > 0) + std::memcpy(ptr, m_sharedArray->buffer, m_sharedArray->size); + + unsigned int size = m_sharedArray->size; + + ReleaseArray(); + m_sharedArray = new SharedString; + m_sharedArray->allocatedSize = bufferSize; + m_sharedArray->buffer = ptr; + m_sharedArray->size = size; +} + +NzByteArray::SharedString NzByteArray::emptyArray(0, 0, 0, nullptr); +unsigned int NzByteArray::npos(static_cast(-1)); +*/ diff --git a/src/Nazara/Core/Debug/MemoryLeakTracker.cpp b/src/Nazara/Core/Debug/MemoryLeakTracker.cpp index b88073725..c507ba42c 100644 --- a/src/Nazara/Core/Debug/MemoryLeakTracker.cpp +++ b/src/Nazara/Core/Debug/MemoryLeakTracker.cpp @@ -1,264 +1,271 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Core module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include - -#if defined(NAZARA_PLATFORM_WINDOWS) -#include -#elif defined(NAZARA_PLATFORM_POSIX) -#include -#endif - -namespace -{ - struct Block - { - std::size_t size; - const char* file; - Block* prev; - Block* next; - bool array; - unsigned int line; - unsigned int magic; - }; - - bool initialized = false; - const unsigned int magic = 0x51429EE; - const char* MLTFileName = "NazaraLeaks.log"; - const char* nextFreeFile = "Internal error"; - unsigned int nextFreeLine = 0; - - Block ptrList = - { - 0, - nullptr, - &ptrList, - &ptrList, - false, - 0, - magic - }; - - #if defined(NAZARA_PLATFORM_WINDOWS) - CRITICAL_SECTION mutex; - #elif defined(NAZARA_PLATFORM_POSIX) - pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; - #endif -} - -NzMemoryManager::NzMemoryManager() -{ -} - -NzMemoryManager::~NzMemoryManager() -{ - Uninitialize(); -} - -void* NzMemoryManager::Allocate(std::size_t size, bool multi, const char* file, unsigned int line) -{ - if (!initialized) - Initialize(); - - #if defined(NAZARA_PLATFORM_WINDOWS) - EnterCriticalSection(&mutex); - #elif defined(NAZARA_PLATFORM_POSIX) - pthread_mutex_lock(&mutex); - #endif - - Block* ptr = reinterpret_cast(std::malloc(size+sizeof(Block))); - if (!ptr) - return nullptr; // Impossible d'envoyer une exception car cela allouerait de la mémoire avec new (boucle infinie) - - ptr->array = multi; - ptr->file = file; - ptr->line = line; - ptr->size = size; - ptr->magic = magic; - - ptr->prev = ptrList.prev; - ptr->next = &ptrList; - ptrList.prev->next = ptr; - ptrList.prev = ptr; - - #if defined(NAZARA_PLATFORM_WINDOWS) - LeaveCriticalSection(&mutex); - #elif defined(NAZARA_PLATFORM_POSIX) - pthread_mutex_unlock(&mutex); - #endif - - return reinterpret_cast(ptr)+sizeof(Block); -} - -void NzMemoryManager::Free(void* pointer, bool multi) -{ - if (!pointer) - return; - - Block* ptr = reinterpret_cast(reinterpret_cast(pointer)-sizeof(Block)); - if (ptr->magic != magic) - return; - - #if defined(NAZARA_PLATFORM_WINDOWS) - EnterCriticalSection(&mutex); - #elif defined(NAZARA_PLATFORM_POSIX) - pthread_mutex_lock(&mutex); - #endif - - if (ptr->array != multi) - { - char* time = TimeInfo(); - - FILE* log = std::fopen(MLTFileName, "a"); - - if (nextFreeFile) - { - if (multi) - std::fprintf(log, "%s Warning: delete[] after new at %s:%d\n", time, nextFreeFile, nextFreeLine); - else - std::fprintf(log, "%s Warning: delete after new[] at %s:%d\n", time, nextFreeFile, nextFreeLine); - } - else - { - if (multi) - std::fprintf(log, "%s Warning: delete[] after new at unknown position\n", time); - else - std::fprintf(log, "%s Warning: delete after new[] at unknown position\n", time); - } - - std::fclose(log); - - std::free(time); - } - - ptr->magic = 0; - ptr->prev->next = ptr->next; - ptr->next->prev = ptr->prev; - - std::free(ptr); - - nextFreeFile = nullptr; - nextFreeLine = 0; - - #if defined(NAZARA_PLATFORM_WINDOWS) - LeaveCriticalSection(&mutex); - #elif defined(NAZARA_PLATFORM_POSIX) - pthread_mutex_unlock(&mutex); - #endif -} - -void NzMemoryManager::NextFree(const char* file, unsigned int line) -{ - nextFreeFile = file; - nextFreeLine = line; -} - -void NzMemoryManager::Initialize() -{ - char* time = TimeInfo(); - - FILE* file = std::fopen(MLTFileName, "w"); - std::fprintf(file, "%s ==============================\n", time); - std::fprintf(file, "%s Nazara Memory Leak Tracker \n", time); - std::fprintf(file, "%s ==============================\n", time); - std::fclose(file); - - std::free(time); - - if (std::atexit(Uninitialize) != 0) - { - static NzMemoryManager manager; - } - - #ifdef NAZARA_PLATFORM_WINDOWS - InitializeCriticalSection(&mutex); - #endif - - initialized = true; -} - -char* NzMemoryManager::TimeInfo() -{ - char* buffer = reinterpret_cast(std::malloc(23*sizeof(char))); - - time_t currentTime = std::time(nullptr); - std::strftime(buffer, 23, "%d/%m/%Y - %H:%M:%S:", std::localtime(¤tTime)); - - return buffer; -} - -void NzMemoryManager::Uninitialize() -{ - #ifdef NAZARA_PLATFORM_WINDOWS - DeleteCriticalSection(&mutex); - #endif - - FILE* log = std::fopen(MLTFileName, "a"); - - char* time = TimeInfo(); - - std::fprintf(log, "%s Application finished, checking leaks...\n", time); - - if (ptrList.next == &ptrList) - { - std::fprintf(log, "%s ==============================\n", time); - std::fprintf(log, "%s No leak detected \n", time); - std::fprintf(log, "%s ==============================", time); - } - else - { - std::fprintf(log, "%s ==============================\n", time); - std::fprintf(log, "%s Leaks have been detected \n", time); - std::fprintf(log, "%s ==============================\n\n", time); - std::fputs("Leak list:\n", log); - - Block* ptr = ptrList.next; - unsigned int count = 0; - unsigned int totalSize = 0; - while (ptr != &ptrList) - { - count++; - totalSize += ptr->size; - if (ptr->file) - std::fprintf(log, "-0x%p -> %d bytes allocated at %s:%d\n", reinterpret_cast(ptr)+sizeof(Block), ptr->size, ptr->file, ptr->line); - else - std::fprintf(log, "-0x%p -> %d bytes allocated at unknown position\n", reinterpret_cast(ptr)+sizeof(Block), ptr->size); - - void* pointer = ptr; - ptr = ptr->next; - std::free(pointer); - } - - std::fprintf(log, "\n%d blocks leaked (%d bytes)", count, totalSize); - } - - std::free(time); - std::fclose(log); -} - -void* operator new(std::size_t size, const char* file, unsigned int line) -{ - return NzMemoryManager::Allocate(size, false, file, line); -} - -void* operator new[](std::size_t size, const char* file, unsigned int line) -{ - return NzMemoryManager::Allocate(size, true, file, line); -} - -void operator delete(void* ptr, const char* file, unsigned int line) throw() -{ - NzMemoryManager::NextFree(file, line); - NzMemoryManager::Free(ptr, false); -} - -void operator delete[](void* ptr, const char* file, unsigned int line) throw() -{ - NzMemoryManager::NextFree(file, line); - NzMemoryManager::Free(ptr, true); -} +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include + +#if defined(NAZARA_PLATFORM_WINDOWS) +#include +#elif defined(NAZARA_PLATFORM_POSIX) +#include +#endif + +namespace +{ + struct Block + { + std::size_t size; + const char* file; + Block* prev; + Block* next; + bool array; + unsigned int line; + unsigned int magic; + }; + + bool initialized = false; + const unsigned int magic = 0x51429EE; + const char* MLTFileName = "NazaraLeaks.log"; + const char* nextFreeFile = "Internal error"; + unsigned int nextFreeLine = 0; + + Block ptrList = + { + 0, + nullptr, + &ptrList, + &ptrList, + false, + 0, + magic + }; + + #if defined(NAZARA_PLATFORM_WINDOWS) + CRITICAL_SECTION mutex; + #elif defined(NAZARA_PLATFORM_POSIX) + pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + #endif +} + +NzMemoryManager::NzMemoryManager() +{ +} + +NzMemoryManager::~NzMemoryManager() +{ + Uninitialize(); +} + +void* NzMemoryManager::Allocate(std::size_t size, bool multi, const char* file, unsigned int line) +{ + if (!initialized) + Initialize(); + + #if defined(NAZARA_PLATFORM_WINDOWS) + EnterCriticalSection(&mutex); + #elif defined(NAZARA_PLATFORM_POSIX) + pthread_mutex_lock(&mutex); + #endif + + Block* ptr = reinterpret_cast(std::malloc(size+sizeof(Block))); + if (!ptr) + { + // Pas d'information de temps (Car nécessitant une allocation) + FILE* log = std::fopen(MLTFileName, "a"); + std::fprintf(log, "Failed to allocate memory (%d bytes)\n", size); + std::fclose(log); + + return nullptr; // Impossible d'envoyer une exception car cela allouerait de la mémoire avec new (boucle infinie) + } + + ptr->array = multi; + ptr->file = file; + ptr->line = line; + ptr->size = size; + ptr->magic = magic; + + ptr->prev = ptrList.prev; + ptr->next = &ptrList; + ptrList.prev->next = ptr; + ptrList.prev = ptr; + + #if defined(NAZARA_PLATFORM_WINDOWS) + LeaveCriticalSection(&mutex); + #elif defined(NAZARA_PLATFORM_POSIX) + pthread_mutex_unlock(&mutex); + #endif + + return reinterpret_cast(ptr)+sizeof(Block); +} + +void NzMemoryManager::Free(void* pointer, bool multi) +{ + if (!pointer) + return; + + Block* ptr = reinterpret_cast(reinterpret_cast(pointer)-sizeof(Block)); + if (ptr->magic != magic) + return; + + #if defined(NAZARA_PLATFORM_WINDOWS) + EnterCriticalSection(&mutex); + #elif defined(NAZARA_PLATFORM_POSIX) + pthread_mutex_lock(&mutex); + #endif + + if (ptr->array != multi) + { + char* time = TimeInfo(); + + FILE* log = std::fopen(MLTFileName, "a"); + + if (nextFreeFile) + { + if (multi) + std::fprintf(log, "%s Warning: delete[] after new at %s:%d\n", time, nextFreeFile, nextFreeLine); + else + std::fprintf(log, "%s Warning: delete after new[] at %s:%d\n", time, nextFreeFile, nextFreeLine); + } + else + { + if (multi) + std::fprintf(log, "%s Warning: delete[] after new at unknown position\n", time); + else + std::fprintf(log, "%s Warning: delete after new[] at unknown position\n", time); + } + + std::fclose(log); + + std::free(time); + } + + ptr->magic = 0; + ptr->prev->next = ptr->next; + ptr->next->prev = ptr->prev; + + std::free(ptr); + + nextFreeFile = nullptr; + nextFreeLine = 0; + + #if defined(NAZARA_PLATFORM_WINDOWS) + LeaveCriticalSection(&mutex); + #elif defined(NAZARA_PLATFORM_POSIX) + pthread_mutex_unlock(&mutex); + #endif +} + +void NzMemoryManager::NextFree(const char* file, unsigned int line) +{ + nextFreeFile = file; + nextFreeLine = line; +} + +void NzMemoryManager::Initialize() +{ + char* time = TimeInfo(); + + FILE* file = std::fopen(MLTFileName, "w"); + std::fprintf(file, "%s ==============================\n", time); + std::fprintf(file, "%s Nazara Memory Leak Tracker \n", time); + std::fprintf(file, "%s ==============================\n", time); + std::fclose(file); + + std::free(time); + + if (std::atexit(Uninitialize) != 0) + { + static NzMemoryManager manager; + } + + #ifdef NAZARA_PLATFORM_WINDOWS + InitializeCriticalSection(&mutex); + #endif + + initialized = true; +} + +char* NzMemoryManager::TimeInfo() +{ + char* buffer = reinterpret_cast(std::malloc(23*sizeof(char))); + + time_t currentTime = std::time(nullptr); + std::strftime(buffer, 23, "%d/%m/%Y - %H:%M:%S:", std::localtime(¤tTime)); + + return buffer; +} + +void NzMemoryManager::Uninitialize() +{ + #ifdef NAZARA_PLATFORM_WINDOWS + DeleteCriticalSection(&mutex); + #endif + + FILE* log = std::fopen(MLTFileName, "a"); + + char* time = TimeInfo(); + + std::fprintf(log, "%s Application finished, checking leaks...\n", time); + + if (ptrList.next == &ptrList) + { + std::fprintf(log, "%s ==============================\n", time); + std::fprintf(log, "%s No leak detected \n", time); + std::fprintf(log, "%s ==============================", time); + } + else + { + std::fprintf(log, "%s ==============================\n", time); + std::fprintf(log, "%s Leaks have been detected \n", time); + std::fprintf(log, "%s ==============================\n\n", time); + std::fputs("Leak list:\n", log); + + Block* ptr = ptrList.next; + unsigned int count = 0; + unsigned int totalSize = 0; + while (ptr != &ptrList) + { + count++; + totalSize += ptr->size; + if (ptr->file) + std::fprintf(log, "-0x%p -> %d bytes allocated at %s:%d\n", reinterpret_cast(ptr)+sizeof(Block), ptr->size, ptr->file, ptr->line); + else + std::fprintf(log, "-0x%p -> %d bytes allocated at unknown position\n", reinterpret_cast(ptr)+sizeof(Block), ptr->size); + + void* pointer = ptr; + ptr = ptr->next; + std::free(pointer); + } + + std::fprintf(log, "\n%d blocks leaked (%d bytes)", count, totalSize); + } + + std::free(time); + std::fclose(log); +} + +void* operator new(std::size_t size, const char* file, unsigned int line) +{ + return NzMemoryManager::Allocate(size, false, file, line); +} + +void* operator new[](std::size_t size, const char* file, unsigned int line) +{ + return NzMemoryManager::Allocate(size, true, file, line); +} + +void operator delete(void* ptr, const char* file, unsigned int line) throw() +{ + NzMemoryManager::NextFree(file, line); + NzMemoryManager::Free(ptr, false); +} + +void operator delete[](void* ptr, const char* file, unsigned int line) throw() +{ + NzMemoryManager::NextFree(file, line); + NzMemoryManager::Free(ptr, true); +} diff --git a/src/Nazara/Core/Resource.cpp b/src/Nazara/Core/Resource.cpp index c83346d78..8014220e1 100644 --- a/src/Nazara/Core/Resource.cpp +++ b/src/Nazara/Core/Resource.cpp @@ -1,54 +1,138 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Core module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include - -NzResource::NzResource(bool persistent) : -m_resourcePersistent(persistent), -m_resourceReferenceCount(0) -{ -} - -NzResource::NzResource(const NzResource& resource) : -m_resourcePersistent(resource.m_resourcePersistent), -m_resourceReferenceCount(0) -{ -} - -NzResource::~NzResource() = default; - -void NzResource::AddResourceReference() const -{ - m_resourceReferenceCount++; -} - -bool NzResource::IsPersistent() const -{ - return m_resourcePersistent; -} - -void NzResource::RemoveResourceReference() const -{ - #if NAZARA_CORE_SAFE - if (m_resourceReferenceCount == 0) - { - NazaraError("Impossible to remove reference (Ref. counter is already 0)"); - return; - } - #endif - - if (--m_resourceReferenceCount == 0 && !m_resourcePersistent) - delete this; -} - -void NzResource::SetPersistent(bool persistent) -{ - m_resourcePersistent = persistent; - - if (!persistent && m_resourceReferenceCount == 0) - delete this; -} +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include + +NzResource::NzResource(bool persistent) : +m_resourcePersistent(persistent), +m_resourceReferenceCount(0) +{ +} + +NzResource::NzResource(const NzResource& resource) : +m_resourcePersistent(resource.m_resourcePersistent), +m_resourceReferenceCount(0) +{ +} + +NzResource::~NzResource() +{ + EnsureResourceListenerUpdate(); + + for (auto it = m_resourceListenersCache.begin(); it != m_resourceListenersCache.end(); ++it) + (*it).listener->OnResourceReleased(this, (*it).index); +} + +void NzResource::AddResourceListener(NzResourceListener* listener, int index) const +{ + NazaraLock(m_mutex) + + if (m_resourceListeners.insert(NzResourceEntry(listener, index)).second) + { + m_resourceListenerUpdated = false; + + // AddResourceReference() + m_resourceReferenceCount++; + } +} + +void NzResource::AddResourceReference() const +{ + NazaraLock(m_mutex) + + m_resourceReferenceCount++; +} + +bool NzResource::IsPersistent() const +{ + NazaraLock(m_mutex) + + return m_resourcePersistent; +} + +void NzResource::RemoveResourceListener(NzResourceListener* listener) const +{ + NazaraLock(m_mutex) + + if (m_resourceListeners.erase(listener) != 0) + m_resourceListenerUpdated = false; + else + NazaraError(NzString::Pointer(listener) + " is not listening to " + NzString::Pointer(this)); + + RemoveResourceReference(); +} + +void NzResource::RemoveResourceReference() const +{ + NazaraMutexLock(m_mutex); + + #if NAZARA_CORE_SAFE + if (m_resourceReferenceCount == 0) + { + NazaraError("Impossible to remove reference (Ref. counter is already 0)"); + return; + } + #endif + + if (--m_resourceReferenceCount == 0 && !m_resourcePersistent) + { + NazaraMutexUnlock(m_mutex); + delete this; + } + else + { + NazaraMutexUnlock(m_mutex); + } +} + +void NzResource::SetPersistent(bool persistent) +{ + NazaraMutexLock(m_mutex); + + m_resourcePersistent = persistent; + + if (!persistent && m_resourceReferenceCount == 0) + { + NazaraMutexUnlock(m_mutex); + delete this; + } + else + { + NazaraMutexUnlock(m_mutex); + } +} + +void NzResource::NotifyCreated() +{ + NazaraLock(m_mutex) + + EnsureResourceListenerUpdate(); + + for (auto it = m_resourceListenersCache.begin(); it != m_resourceListenersCache.end(); ++it) + (*it).listener->OnResourceCreated(this, (*it).index); +} + +void NzResource::NotifyDestroy() +{ + NazaraLock(m_mutex) + + EnsureResourceListenerUpdate(); + + for (auto it = m_resourceListenersCache.begin(); it != m_resourceListenersCache.end(); ++it) + (*it).listener->OnResourceDestroy(this, (*it).index); +} + +void NzResource::EnsureResourceListenerUpdate() const +{ + // Déjà bloqué par une mutex + if (!m_resourceListenerUpdated) + { + m_resourceListenersCache = m_resourceListeners; + m_resourceListenerUpdated = true; + } +} diff --git a/src/Nazara/Core/ResourceListener.cpp b/src/Nazara/Core/ResourceListener.cpp new file mode 100644 index 000000000..981872bb6 --- /dev/null +++ b/src/Nazara/Core/ResourceListener.cpp @@ -0,0 +1,26 @@ +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +NzResourceListener::~NzResourceListener() = default; + +void NzResourceListener::OnResourceCreated(const NzResource* resource, int index) +{ + NazaraUnused(resource); + NazaraUnused(index); +} + +void NzResourceListener::OnResourceDestroy(const NzResource* resource, int index) +{ + NazaraUnused(resource); + NazaraUnused(index); +} + +void NzResourceListener::OnResourceReleased(const NzResource* resource, int index) +{ + NazaraUnused(resource); + NazaraUnused(index); +} diff --git a/src/Nazara/Core/String.cpp b/src/Nazara/Core/String.cpp index 419e748de..e9c5b6e83 100644 --- a/src/Nazara/Core/String.cpp +++ b/src/Nazara/Core/String.cpp @@ -1,5162 +1,5163 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Core module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -inline unsigned int nzPow2(unsigned int n) -{ - unsigned int x = 1; - - // Tant que x est plus petit que n, on décale ses bits vers la gauche, ce qui revient à multiplier par deux - while(x <= n) - x <<= 1; - - return x; -} -// Cet algorithme est inspiré de la documentation de Qt -inline unsigned int nzGetNewSize(unsigned int newSize) -{ - if (newSize < 20) - return newSize+4; - else - { - if (newSize < (1 << 12)-12) - return nzPow2(newSize << 1)-12; - else - return newSize + (1 << 11); - } -} - -inline char nzToLower(char character) -{ - if (character >= 'A' && character <= 'Z') - return character + ('a' - 'A'); - else - return character; -} - -inline char nzToUpper(char character) -{ - if (character >= 'a' && character <= 'z') - return character + ('A' - 'a'); - else - return character; -} - -inline int nzStrcasecmp(const char* s1, const char* s2) -{ - int ret = 0; - - while (!(ret = static_cast(nzToLower(*s1)) - static_cast(nzToLower(*s2))) && *s2) - ++s1, ++s2; - - return ret != 0 ? (ret > 0 ? 1 : -1) : 0; -} - -inline int nzUnicodecasecmp(const char* s1, const char* s2) -{ - int ret = 0; - utf8::unchecked::iterator it1(s1); - utf8::unchecked::iterator it2(s2); - - while (!(ret = NzUnicode::GetLowercase(*it1) - NzUnicode::GetLowercase(*it2)) && *it2) - ++it1, ++it2; - - return ret != 0 ? (ret > 0 ? 1 : -1) : 0; -} - -NzString::NzString() : -m_sharedString(&emptyString) -{ -} - -NzString::NzString(char character) -{ - if (character == '\0') - m_sharedString = &emptyString; - else - { - m_sharedString = new SharedString; - m_sharedString->capacity = 1; - m_sharedString->size = 1; - m_sharedString->string = new char[2]; - m_sharedString->string[0] = character; - m_sharedString->string[1] = '\0'; - } -} - -NzString::NzString(const char* string) -{ - if (string) - { - unsigned int size = std::strlen(string); - if (size > 0) - { - m_sharedString = new SharedString; - m_sharedString->capacity = size; - m_sharedString->size = size; - m_sharedString->string = new char[size+1]; - std::strcpy(m_sharedString->string, string); - } - else - m_sharedString = &emptyString; - } - else - m_sharedString = &emptyString; -} - -NzString::NzString(const std::string& string) -{ - if (string.size() > 0) - { - m_sharedString = new SharedString; - m_sharedString->capacity = string.capacity(); - m_sharedString->size = string.size(); - m_sharedString->string = new char[string.capacity()+1]; - std::strcpy(m_sharedString->string, string.c_str()); - } - else - m_sharedString = &emptyString; -} - -NzString::NzString(const NzString& string) : -m_sharedString(string.m_sharedString) -{ - if (m_sharedString != &emptyString) - { - NazaraMutexLock(m_sharedString->mutex); - m_sharedString->refCount++; - NazaraMutexUnlock(m_sharedString->mutex); - } -} - -NzString::NzString(NzString&& string) noexcept : -m_sharedString(string.m_sharedString) -{ - string.m_sharedString = &emptyString; -} - -NzString::NzString(SharedString* sharedString) : -m_sharedString(sharedString) -{ -} - -NzString::~NzString() -{ - ReleaseString(); -} - -NzString& NzString::Append(char character) -{ - if (character == '\0') - return *this; - - if (m_sharedString->size == 0 && m_sharedString->capacity == 0) - return operator=(character); - - if (m_sharedString->capacity > m_sharedString->size) - { - EnsureOwnership(); - - m_sharedString->string[m_sharedString->size] = character; - m_sharedString->string[m_sharedString->size+1] = '\0'; - m_sharedString->size++; - } - else - { - unsigned int newSize = m_sharedString->size+1; - unsigned int bufferSize = nzGetNewSize(newSize); - - char* str = new char[bufferSize+1]; - std::memcpy(str, m_sharedString->string, m_sharedString->size*sizeof(char)); - str[m_sharedString->size] = character; - str[newSize] = '\0'; - - ReleaseString(); - m_sharedString = new SharedString; - m_sharedString->capacity = bufferSize; - m_sharedString->size = newSize; - m_sharedString->string = str; - } - - return *this; -} - -NzString& NzString::Append(const char* string) -{ - if (!string || !string[0]) - return *this; - - unsigned int length = std::strlen(string); - if (length == 0) - return *this; - - if (m_sharedString->capacity >= m_sharedString->size + length) - { - EnsureOwnership(); - - std::strcpy(&m_sharedString->string[m_sharedString->size], string); - m_sharedString->size += length; - } - else - { - unsigned int newSize = m_sharedString->size + length; - unsigned int bufferSize = nzGetNewSize(newSize); - - char* str = new char[bufferSize+1]; - std::memcpy(str, m_sharedString->string, m_sharedString->size*sizeof(char)); - std::strcpy(&str[m_sharedString->size], string); - - ReleaseString(); - m_sharedString = new SharedString; - m_sharedString->capacity = bufferSize; - m_sharedString->size = newSize; - m_sharedString->string = str; - } - - return *this; -} - -NzString& NzString::Append(const NzString& string) -{ - if (string.m_sharedString->size == 0) - return *this; - - if (m_sharedString->size == 0 && m_sharedString->capacity < string.m_sharedString->size) - return operator=(string); - - if (m_sharedString->capacity >= m_sharedString->size + string.m_sharedString->size) - { - EnsureOwnership(); - - std::strcpy(&m_sharedString->string[m_sharedString->size], string.m_sharedString->string); - m_sharedString->size += string.m_sharedString->size; - } - else - { - unsigned int newSize = m_sharedString->size + string.m_sharedString->size; - unsigned int bufferSize = nzGetNewSize(newSize); - - char* str = new char[bufferSize+1]; - std::memcpy(str, m_sharedString->string, m_sharedString->size*sizeof(char)); - std::strcpy(&str[m_sharedString->size], string.m_sharedString->string); - - ReleaseString(); - m_sharedString = new SharedString; - m_sharedString->capacity = bufferSize; - m_sharedString->size = newSize; - m_sharedString->string = str; - } - - return *this; -} - -void NzString::Clear(bool keepBuffer) -{ - if (keepBuffer) - { - EnsureOwnership(); - m_sharedString->size = 0; - } - else - ReleaseString(); -} - -bool NzString::Contains(char character, int start, nzUInt32 flags) const -{ - return Find(character, start, flags) != npos; -} - -bool NzString::Contains(const char* string, int start, nzUInt32 flags) const -{ - return Find(string, start, flags) != npos; -} - -bool NzString::Contains(const NzString& string, int start, nzUInt32 flags) const -{ - return Find(string, start, flags) != npos; -} - -unsigned int NzString::Count(char character, int start, nzUInt32 flags) const -{ - if (character == '\0' || m_sharedString->size == 0) - return 0; - - if (start < 0) - start = std::max(static_cast(m_sharedString->size + start), 0); - - unsigned int pos = static_cast(start); - if (pos >= m_sharedString->size) - return 0; - - char* str = &m_sharedString->string[pos]; - unsigned int count = 0; - if (flags & CaseInsensitive) - { - char character_lower = nzToLower(character); - char character_upper = nzToUpper(character); - do - { - if (*str == character_lower || *str == character_upper) - count++; - } - while (*++str); - } - else - { - while ((str = std::strchr(str, character))) - { - count++; - str++; - } - } - - return count; -} - -unsigned int NzString::Count(const char* string, int start, nzUInt32 flags) const -{ - if (!string || !string[0] || m_sharedString->size == 0) - return 0; - - if (start < 0) - start = std::max(static_cast(m_sharedString->size + start), 0); - - unsigned int pos = static_cast(start); - if (pos >= m_sharedString->size) - return 0; - - char* str = &m_sharedString->string[pos]; - unsigned int count = 0; - if (flags & CaseInsensitive) - { - if (flags & HandleUtf8) - { - while (utf8::internal::is_trail(*str)) - str++; - - utf8::unchecked::iterator it(str); - - const char* t = string; - char32_t c = NzUnicode::GetLowercase(utf8::unchecked::next(t)); - do - { - if (NzUnicode::GetLowercase(*it) == c) - { - ++it; - - utf8::unchecked::iterator it2(t); - while (true) - { - if (*it2 == '\0') - { - count++; - break; - } - - if (*it == '\0') - return count; - - if (NzUnicode::GetLowercase(*it) != NzUnicode::GetLowercase(*it2)) - break; - - ++it; - ++it2; - } - } - } - while (*++it); - } - else - { - char c = nzToLower(string[0]); - do - { - if (nzToLower(*str) == c) - { - str++; - - const char* ptr = &string[1]; - while (true) - { - if (*ptr == '\0') - { - count++; - break; - } - - if (*str == '\0') - return count; - - if (nzToLower(*str) != nzToLower(*ptr)) - break; - - ptr++; - str++; - } - } - } - while (*++str); - } - } - else - { - while ((str = std::strstr(str, string))) - { - count++; - str++; - } - } - - return count; -} - -unsigned int NzString::Count(const NzString& string, int start, nzUInt32 flags) const -{ - if (string.m_sharedString->size == 0 || string.m_sharedString->size > m_sharedString->size) - return 0; - - if (start < 0) - start = std::max(static_cast(m_sharedString->size + start), 0); - - unsigned int pos = static_cast(start); - if (pos >= m_sharedString->size) - return 0; - - char* str = &m_sharedString->string[pos]; - unsigned int count = 0; - if (flags & CaseInsensitive) - { - if (flags & HandleUtf8) - { - while (utf8::internal::is_trail(*str)) - str++; - - utf8::unchecked::iterator it(str); - - const char* t = string.m_sharedString->string; - char32_t c = NzUnicode::GetLowercase(utf8::unchecked::next(t)); - do - { - if (NzUnicode::GetLowercase(*it) == c) - { - ++it; - - utf8::unchecked::iterator it2(t); - while (true) - { - if (*it2 == '\0') - { - count++; - break; - } - - if (*it == '\0') - return count; - - if (NzUnicode::GetLowercase(*it) != NzUnicode::GetLowercase(*it2)) - break; - - ++it; - ++it2; - } - } - } - while (*++it); - } - else - { - char c = nzToLower(string.m_sharedString->string[0]); - do - { - if (nzToLower(*str) == c) - { - str++; - - const char* ptr = &string.m_sharedString->string[1]; - while (true) - { - if (*ptr == '\0') - { - count++; - break; - } - - if (*str == '\0') - return count; - - if (nzToLower(*str) != nzToLower(*ptr)) - break; - - ptr++; - str++; - } - } - } - while (*++str); - } - } - else - { - while ((str = std::strstr(str, string.m_sharedString->string))) - { - count++; - str++; - } - } - - return count; -} - -unsigned int NzString::CountAny(const char* string, int start, nzUInt32 flags) const -{ - if (!string || !string[0] || m_sharedString->size == 0) - return 0; - - if (start < 0) - start = std::max(static_cast(m_sharedString->size + start), 0); - - unsigned int pos = static_cast(start); - if (pos >= m_sharedString->size) - return 0; - - char* str = &m_sharedString->string[pos]; - unsigned int count = 0; - if (flags & HandleUtf8) - { - while (utf8::internal::is_trail(*str)) - str++; - - utf8::unchecked::iterator it(str); - - if (flags & CaseInsensitive) - { - do - { - utf8::unchecked::iterator it2(string); - do - { - if (NzUnicode::GetLowercase(*it) == NzUnicode::GetLowercase(*it2)) - { - count++; - break; - } - } - while (*++it2); - } - while (*++str); - } - else - { - do - { - utf8::unchecked::iterator it2(string); - do - { - if (*it == *it2) - { - count++; - break; - } - } - while (*++it2); - } - while (*++str); - } - } - else - { - if (flags & CaseInsensitive) - { - do - { - const char* c = string; - do - { - if (nzToLower(*str) == nzToLower(*c)) - { - count++; - break; - } - } - while (*++c); - } - while (*++str); - } - else - { - while ((str = std::strpbrk(str, string))) - { - count++; - str++; - } - } - } - - return count; -} - -unsigned int NzString::CountAny(const NzString& string, int start, nzUInt32 flags) const -{ - if (string.m_sharedString->size == 0 || m_sharedString->size == 0) - return 0; - - if (start < 0) - start = std::max(static_cast(m_sharedString->size + start), 0); - - unsigned int pos = static_cast(start); - if (pos >= m_sharedString->size) - return 0; - - char* str = &m_sharedString->string[pos]; - unsigned int count = 0; - if (flags & HandleUtf8) - { - while (utf8::internal::is_trail(*str)) - str++; - - utf8::unchecked::iterator it(str); - - if (flags & CaseInsensitive) - { - do - { - utf8::unchecked::iterator it2(string.m_sharedString->string); - do - { - if (NzUnicode::GetLowercase(*it) == NzUnicode::GetLowercase(*it2)) - { - count++; - break; - } - } - while (*++it2); - } - while (*++str); - } - else - { - do - { - utf8::unchecked::iterator it2(string.m_sharedString->string); - do - { - if (*it == *it2) - { - count++; - break; - } - } - while (*++it2); - } - while (*++str); - } - } - else - { - if (flags & CaseInsensitive) - { - do - { - const char* c = string.m_sharedString->string; - do - { - if (nzToLower(*str) == nzToLower(*c)) - { - count++; - break; - } - } - while (*++c); - } - while (*++str); - } - else - { - while ((str = std::strpbrk(str, string.m_sharedString->string))) - { - count++; - str++; - } - } - } - - return count; -} - -bool NzString::EndsWith(char character, nzUInt32 flags) const -{ - if (m_sharedString->size == 0) - return 0; - - if (flags & CaseInsensitive) - return nzToLower(m_sharedString->string[m_sharedString->size-1]) == nzToLower(character); - else - return m_sharedString->string[m_sharedString->size-1] == character; // character == '\0' sera toujours faux -} - -bool NzString::EndsWith(const char* string, nzUInt32 flags) const -{ - if (!string || !string[0] || m_sharedString->size == 0) - return false; - - unsigned int len = std::strlen(string); - if (len > m_sharedString->size) - return false; - - if (flags & CaseInsensitive) - { - if (flags & HandleUtf8) - return nzUnicodecasecmp(&m_sharedString->string[m_sharedString->size - len], string) == 0; - else - return nzStrcasecmp(&m_sharedString->string[m_sharedString->size - len], string) == 0; - } - else - return std::strcmp(&m_sharedString->string[m_sharedString->size - len], string) == 0; -} - -bool NzString::EndsWith(const NzString& string, nzUInt32 flags) const -{ - if (string.m_sharedString->size == 0 || string.m_sharedString->size > m_sharedString->size) - return false; - - if (flags & CaseInsensitive) - { - if (flags & HandleUtf8) - return nzUnicodecasecmp(&m_sharedString->string[m_sharedString->size - string.m_sharedString->size], string.m_sharedString->string) == 0; - else - return nzStrcasecmp(&m_sharedString->string[m_sharedString->size - string.m_sharedString->size], string.m_sharedString->string) == 0; - } - else - return std::strcmp(&m_sharedString->string[m_sharedString->size - string.m_sharedString->size], string.m_sharedString->string) == 0; -} - -unsigned int NzString::Find(char character, int start, nzUInt32 flags) const -{ - if (character == '\0' || m_sharedString->size == 0) - return npos; - - if (start < 0) - start = std::max(static_cast(m_sharedString->size + start), 0); - - unsigned int pos = static_cast(start); - if (pos >= m_sharedString->size) - return npos; - - if (flags & CaseInsensitive) - { - char ch = nzToLower(character); - const char* str = m_sharedString->string; - do - { - if (nzToLower(*str) == ch) - return static_cast(str - m_sharedString->string); - } - while (*++str); - - return npos; - } - else - { - char* ch = std::strchr(&m_sharedString->string[pos], character); - if (ch) - return static_cast(ch - m_sharedString->string); - else - return npos; - } -} - -unsigned int NzString::Find(const char* string, int start, nzUInt32 flags) const -{ - if (!string || !string[0] || m_sharedString->size == 0) - return npos; - - if (start < 0) - start = std::max(static_cast(m_sharedString->size + start), 0); - - unsigned int pos = static_cast(start); - if (pos >= m_sharedString->size) - return npos; - - char* str = &m_sharedString->string[pos]; - if (flags & CaseInsensitive) - { - if (flags & HandleUtf8) - { - while (utf8::internal::is_trail(*str)) - str++; - - utf8::unchecked::iterator it(str); - - const char* t = string; - char32_t c = NzUnicode::GetLowercase(utf8::unchecked::next(t)); - do - { - if (NzUnicode::GetLowercase(*it) == c) - { - const char* ptrPos = it.base(); - ++it; - - utf8::unchecked::iterator it2(t); - while (true) - { - if (*it2 == '\0') - return static_cast(ptrPos - m_sharedString->string); - - if (*it == '\0') - return npos; - - if (NzUnicode::GetLowercase(*it) != NzUnicode::GetLowercase(*it2)) - break; - - ++it; - ++it2; - } - } - } - while (*++it); - } - else - { - char c = nzToLower(string[0]); - do - { - if (nzToLower(*str) == c) - { - char* ptrPos = str; - str++; - - const char* ptr = &string[1]; - while (true) - { - if (*ptr == '\0') - return static_cast(ptrPos - m_sharedString->string); - - if (*str == '\0') - return npos; - - if (nzToLower(*str) != nzToLower(*ptr)) - break; - - ptr++; - str++; - } - } - } - while (*++str); - } - } - else - { - char* ch = std::strstr(&m_sharedString->string[pos], string); - if (ch) - return static_cast(ch - m_sharedString->string); - } - - return npos; -} - -unsigned int NzString::Find(const NzString& string, int start, nzUInt32 flags) const -{ - if (string.m_sharedString->size == 0 || string.m_sharedString->size > m_sharedString->size) - return npos; - - if (start < 0) - start = std::max(static_cast(m_sharedString->size + start), 0); - - unsigned int pos = static_cast(start); - if (pos >= m_sharedString->size) - return npos; - - char* str = &m_sharedString->string[pos]; - if (flags & CaseInsensitive) - { - if (flags & HandleUtf8) - { - while (utf8::internal::is_trail(*str)) - str++; - - utf8::unchecked::iterator it(str); - - const char* t = string.m_sharedString->string; - char32_t c = NzUnicode::GetLowercase(utf8::unchecked::next(t)); - do - { - if (NzUnicode::GetLowercase(*it) == c) - { - const char* ptrPos = it.base(); - ++it; - - utf8::unchecked::iterator it2(t); - while (true) - { - if (*it2 == '\0') - return static_cast(ptrPos - m_sharedString->string); - - if (*it == '\0') - return npos; - - if (NzUnicode::GetLowercase(*it) != NzUnicode::GetLowercase(*it2)) - break; - - ++it; - ++it2; - } - } - } - while (*++it); - } - else - { - char c = nzToLower(string.m_sharedString->string[0]); - do - { - if (nzToLower(*str) == c) - { - char* ptrPos = str; - str++; - - const char* ptr = &string.m_sharedString->string[1]; - while (true) - { - if (*ptr == '\0') - return static_cast(ptrPos - m_sharedString->string); - - if (*str == '\0') - return npos; - - if (nzToLower(*str) != nzToLower(*ptr)) - break; - - ptr++; - str++; - } - } - } - while (*++str); - } - } - else - { - char* ch = std::strstr(&m_sharedString->string[pos], string.m_sharedString->string); - if (ch) - return static_cast(ch - m_sharedString->string); - } - - return npos; -} - -unsigned int NzString::FindAny(const char* string, int start, nzUInt32 flags) const -{ - if (m_sharedString->size == 0 || !string || !string[0]) - return npos; - - if (start < 0) - start = std::max(m_sharedString->size+start, 0U); - - unsigned int pos = static_cast(start); - if (pos >= m_sharedString->size) - return npos; - - char* str = &m_sharedString->string[pos]; - if (flags & HandleUtf8) - { - while (utf8::internal::is_trail(*str)) - str++; - - utf8::unchecked::iterator it(str); - - if (flags & CaseInsensitive) - { - do - { - utf8::unchecked::iterator it2(string); - char32_t character = NzUnicode::GetLowercase(*it); - do - { - if (character == NzUnicode::GetLowercase(*it2)) - return it.base() - m_sharedString->string; - } - while (*++it2); - } - while (*++it); - } - else - { - do - { - utf8::unchecked::iterator it2(string); - do - { - if (*it == *it2) - return it.base() - m_sharedString->string; - } - while (*++it2); - } - while (*++it); - } - } - else - { - if (flags & CaseInsensitive) - { - do - { - const char* c = string; - char character = nzToLower(*str); - do - { - if (character == nzToLower(*c)) - return str - m_sharedString->string; - } - while (*++c); - } - while (*++str); - } - else - { - str = std::strpbrk(str, string); - if (str) - return str - m_sharedString->string; - } - } - - return npos; -} - -unsigned int NzString::FindAny(const NzString& string, int start, nzUInt32 flags) const -{ - if (m_sharedString->size == 0 || string.m_sharedString->size == 0) - return npos; - - if (string.m_sharedString->size > m_sharedString->size) - return npos; - - if (start < 0) - start = std::max(m_sharedString->size+start, 0U); - - unsigned int pos = static_cast(start); - if (pos >= m_sharedString->size) - return npos; - - char* str = &m_sharedString->string[pos]; - if (flags & HandleUtf8) - { - while (utf8::internal::is_trail(*str)) - str++; - - utf8::unchecked::iterator it(str); - - if (flags & CaseInsensitive) - { - do - { - utf8::unchecked::iterator it2(string.m_sharedString->string); - char32_t character = NzUnicode::GetLowercase(*it); - do - { - if (character == NzUnicode::GetLowercase(*it2)) - return it.base() - m_sharedString->string; - } - while (*++it2); - } - while (*++it); - } - else - { - do - { - utf8::unchecked::iterator it2(string.m_sharedString->string); - do - { - if (*it == *it2) - return it.base() - m_sharedString->string; - } - while (*++it2); - } - while (*++it); - } - } - else - { - if (flags & CaseInsensitive) - { - do - { - const char* c = string.m_sharedString->string; - char character = nzToLower(*str); - do - { - if (character == nzToLower(*c)) - return str - m_sharedString->string; - } - while (*++c); - } - while (*++str); - } - else - { - str = std::strpbrk(str, string.m_sharedString->string); - if (str) - return str - m_sharedString->string; - } - } - - return npos; -} - -unsigned int NzString::FindLast(char character, int start, nzUInt32 flags) const -{ - if (character == '\0' || m_sharedString->size == 0) - return npos; - - if (start < 0) - start = std::max(static_cast(m_sharedString->size + start), 0); - - unsigned int pos = static_cast(start); - if (pos >= m_sharedString->size) - return npos; - - char* ptr = &m_sharedString->string[m_sharedString->size-1]; - - if (flags & CaseInsensitive) - { - character = nzToLower(character); - do - { - if (nzToLower(*ptr) == character) - return static_cast(ptr - m_sharedString->string); - } - while (ptr-- != m_sharedString->string); - } - else - { - // strrchr ? - do - { - if (*ptr == character) - return static_cast(ptr - m_sharedString->string); - } - while (ptr-- != m_sharedString->string); - } - - return npos; -} - -unsigned int NzString::FindLast(const char* string, int start, nzUInt32 flags) const -{ - if (!string || !string[0] ||m_sharedString->size == 0) - return npos; - - if (start < 0) - start = std::max(static_cast(m_sharedString->size + start), 0); - - unsigned int pos = static_cast(start); - if (pos >= m_sharedString->size) - return npos; - - ///Algo 1.FindLast#3 (Taille du pattern inconnue) - const char* ptr = &m_sharedString->string[pos]; - if (flags & CaseInsensitive) - { - if (flags & HandleUtf8) - { - if (utf8::internal::is_trail(*ptr)) - utf8::unchecked::prior(ptr); // On s'assure d'avoir un pointeur vers le début d'un caractère - - utf8::unchecked::iterator it(ptr); - const char* t = string; - char32_t c = NzUnicode::GetLowercase(utf8::unchecked::next(t)); - do - { - if (NzUnicode::GetLowercase(*it) == c) - { - utf8::unchecked::iterator it2(t); - utf8::unchecked::iterator tIt(it); - ++tIt; - - while (true) - { - if (*it2 == '\0') - return it.base() - m_sharedString->string; - - if (tIt.base() > &m_sharedString->string[pos]) - break; - - if (NzUnicode::GetLowercase(*tIt) != NzUnicode::GetLowercase(*it2)) - break; - - ++it2; - ++tIt; - } - } - } - while (it--.base() != m_sharedString->string); - } - else - { - char c = nzToLower(string[0]); - do - { - if (nzToLower(*ptr) == c) - { - const char* p = &string[1]; - const char* tPtr = ptr+1; - while (true) - { - if (*p == '\0') - return ptr - m_sharedString->string; - - if (tPtr > &m_sharedString->string[pos]) - break; - - if (nzToLower(*tPtr) != nzToLower(*p)) - break; - - p++; - tPtr++; - } - } - } - while (ptr-- != m_sharedString->string); - } - } - else - { - do - { - if (*ptr == string[0]) - { - const char* p = &string[1]; - const char* tPtr = ptr+1; - while (true) - { - if (*p == '\0') - return ptr - m_sharedString->string; - - if (tPtr > &m_sharedString->string[pos]) - break; - - if (*tPtr != *p) - break; - - p++; - tPtr++; - } - } - } - while (ptr-- != m_sharedString->string); - } - - return npos; -} - -unsigned int NzString::FindLast(const NzString& string, int start, nzUInt32 flags) const -{ - if (string.m_sharedString->size == 0 || string.m_sharedString->size > m_sharedString->size) - return npos; - - if (start < 0) - start = std::max(static_cast(m_sharedString->size + start), 0); - - unsigned int pos = static_cast(start); - if (pos >= m_sharedString->size || string.m_sharedString->size > m_sharedString->size) - return npos; - - const char* ptr = &m_sharedString->string[pos]; - const char* limit = &m_sharedString->string[string.m_sharedString->size-1]; - - if (flags & CaseInsensitive) - { - if (flags & HandleUtf8) - { - ///Algo 1.FindLast#3 (Itérateur non-adapté) - if (utf8::internal::is_trail(*ptr)) - utf8::unchecked::prior(ptr); // On s'assure d'avoir un pointeur vers le début d'un caractère - - utf8::unchecked::iterator it(ptr); - const char* t = string.m_sharedString->string; - char32_t c = NzUnicode::GetLowercase(utf8::unchecked::next(t)); - do - { - if (NzUnicode::GetLowercase(*it) == c) - { - utf8::unchecked::iterator it2(t); - utf8::unchecked::iterator tIt(it); - ++tIt; - - while (true) - { - if (*it2 == '\0') - return it.base() - m_sharedString->string; - - if (tIt.base() > &m_sharedString->string[pos]) - break; - - if (NzUnicode::GetLowercase(*tIt) != NzUnicode::GetLowercase(*it2)) - break; - - ++it2; - ++tIt; - } - } - } - while (it--.base() != limit); - } - else - { - ///Algo 1.FindLast#4 (Taille du pattern connue) - char c = nzToLower(string.m_sharedString->string[string.m_sharedString->size-1]); - while (true) - { - if (nzToLower(*ptr) == c) - { - const char* p = &string.m_sharedString->string[string.m_sharedString->size-1]; - for (; p >= &string.m_sharedString->string[0]; --p, --ptr) - { - if (nzToLower(*ptr) != nzToLower(*p)) - break; - - if (p == &string.m_sharedString->string[0]) - return ptr-m_sharedString->string; - - if (ptr == m_sharedString->string) - return npos; - } - } - else if (ptr-- <= limit) - break; - } - } - } - else - { - ///Algo 1.FindLast#4 (Taille du pattern connue) - while (true) - { - if (*ptr == string.m_sharedString->string[string.m_sharedString->size-1]) - { - const char* p = &string.m_sharedString->string[string.m_sharedString->size-1]; - for (; p >= &string.m_sharedString->string[0]; --p, --ptr) - { - if (*ptr != *p) - break; - - if (p == &string.m_sharedString->string[0]) - return ptr-m_sharedString->string; - - if (ptr == m_sharedString->string) - return npos; - } - } - else if (ptr-- <= limit) - break; - } - } - - return npos; -} - -unsigned int NzString::FindLastAny(const char* string, int start, nzUInt32 flags) const -{ - if (m_sharedString->size == 0 || !string || !string[0]) - return npos; - - if (start < 0) - start = std::max(m_sharedString->size+start, 0U); - - unsigned int pos = static_cast(start); - if (pos >= m_sharedString->size) - return npos; - - char* str = &m_sharedString->string[pos]; - if (flags & HandleUtf8) - { - while (utf8::internal::is_trail(*str)) - str++; - - utf8::unchecked::iterator it(str); - - if (flags & CaseInsensitive) - { - do - { - utf8::unchecked::iterator it2(string); - char32_t character = NzUnicode::GetLowercase(*it); - do - { - if (character == NzUnicode::GetLowercase(*it2)) - return it.base() - m_sharedString->string; - } - while (*++it2); - } - while (it--.base() != m_sharedString->string); - } - else - { - do - { - utf8::unchecked::iterator it2(string); - do - { - if (*it == *it2) - return it.base() - m_sharedString->string; - } - while (*++it2); - } - while (it--.base() != m_sharedString->string); - } - } - else - { - if (flags & CaseInsensitive) - { - do - { - const char* c = string; - char character = nzToLower(*str); - do - { - if (character == nzToLower(*c)) - return str - m_sharedString->string; - } - while (*++c); - } - while (str-- != m_sharedString->string); - } - else - { - do - { - const char* c = string; - do - { - if (*str == *c) - return str - m_sharedString->string; - } - while (*++c); - } - while (str-- != m_sharedString->string); - } - } - - return npos; -} - -unsigned int NzString::FindLastAny(const NzString& string, int start, nzUInt32 flags) const -{ - if (m_sharedString->size == 0 || string.m_sharedString->size == 0) - return npos; - - if (start < 0) - start = std::max(m_sharedString->size+start, 0U); - - unsigned int pos = static_cast(start); - if (pos >= m_sharedString->size) - return npos; - - char* str = &m_sharedString->string[pos]; - if (flags & HandleUtf8) - { - while (utf8::internal::is_trail(*str)) - str++; - - utf8::unchecked::iterator it(str); - - if (flags & CaseInsensitive) - { - do - { - utf8::unchecked::iterator it2(string.m_sharedString->string); - char32_t character = NzUnicode::GetLowercase(*it); - do - { - if (character == NzUnicode::GetLowercase(*it2)) - return it.base() - m_sharedString->string; - } - while (*++it2); - } - while (it--.base() != m_sharedString->string); - } - else - { - do - { - utf8::unchecked::iterator it2(string.m_sharedString->string); - do - { - if (*it == *it2) - return it.base() - m_sharedString->string; - } - while (*++it2); - } - while (it--.base() != m_sharedString->string); - } - } - else - { - if (flags & CaseInsensitive) - { - do - { - const char* c = string.m_sharedString->string; - char character = nzToLower(*str); - do - { - if (character == nzToLower(*c)) - return str - m_sharedString->string; - } - while (*++c); - } - while (str-- != m_sharedString->string); - } - else - { - do - { - const char* c = string.m_sharedString->string; - do - { - if (*str == *c) - return str - m_sharedString->string; - } - while (*++c); - } - while (str-- != m_sharedString->string); - } - } - - return npos; -} - -unsigned int NzString::FindLastWord(const char* string, int start, nzUInt32 flags) const -{ - if (!string || !string[0] || m_sharedString->size == 0) - return npos; - - if (start < 0) - start = std::max(static_cast(m_sharedString->size + start), 0); - - unsigned int pos = static_cast(start); - if (pos >= m_sharedString->size) - return npos; - - ///Algo 2.FindLastWord#1 (Taille du pattern inconnue) - const char* ptr = &m_sharedString->string[pos]; - - if (flags & HandleUtf8) - { - if (utf8::internal::is_trail(*ptr)) - utf8::unchecked::prior(ptr); // On s'assure d'avoir un pointeur vers le début d'un caractère - - utf8::unchecked::iterator it(ptr); - - if (flags & CaseInsensitive) - { - const char* t = string; // utf8(::unchecked)::next affecte l'itérateur en argument - nzUInt32 c = NzUnicode::GetLowercase(utf8::unchecked::next(t)); - do - { - if (NzUnicode::GetLowercase(*it) == c) - { - if (it.base() != m_sharedString->string) - { - it--; - if (!(NzUnicode::GetCategory(*it++) & NzUnicode::Category_Separator)) - continue; - } - - utf8::unchecked::iterator p(t); - utf8::unchecked::iterator tIt = it; - ++tIt; - - while (true) - { - if (*p == '\0') - { - if (*tIt == '\0' || NzUnicode::GetCategory(*tIt) & NzUnicode::Category_Separator) - return it.base() - m_sharedString->string; - else - break; - } - - if (tIt.base() > &m_sharedString->string[pos]) - break; - - if (NzUnicode::GetLowercase(*tIt) != NzUnicode::GetLowercase(*p)) - break; - - ++p; - ++tIt; - } - } - } - while (it--.base() != m_sharedString->string); - } - else - { - const char* t = string; // utf8(::unchecked)::next affecte l'itérateur en argument - nzUInt32 c = utf8::unchecked::next(t); - do - { - if (*it == c) - { - if (it.base() != m_sharedString->string) - { - it--; - if (!(NzUnicode::GetCategory(*it++) & NzUnicode::Category_Separator)) - continue; - } - - utf8::unchecked::iterator p(t); - utf8::unchecked::iterator tIt = it; - ++tIt; - - while (true) - { - if (*p == '\0') - { - if (*tIt == '\0' || NzUnicode::GetCategory(*tIt) & NzUnicode::Category_Separator) - return it.base() - m_sharedString->string; - else - break; - } - - if (tIt.base() > &m_sharedString->string[pos]) - break; - - if (*tIt != *p) - break; - - ++p; - ++tIt; - } - } - } - while (it--.base() != m_sharedString->string); - } - } - else - { - if (flags & CaseInsensitive) - { - char c = nzToLower(string[0]); - do - { - if (nzToLower(*ptr) == c) - { - if (ptr != m_sharedString->string && !std::isspace(*(ptr-1))) - continue; - - const char* p = &string[1]; - const char* tPtr = ptr+1; - while (true) - { - if (*p == '\0') - { - if (*tPtr == '\0' || std::isspace(*tPtr)) - return ptr-m_sharedString->string; - else - break; - } - - if (tPtr > &m_sharedString->string[pos]) - break; - - if (nzToLower(*tPtr) != nzToLower(*p)) - break; - - p++; - tPtr++; - } - } - } - while (ptr-- != m_sharedString->string); - } - else - { - do - { - if (*ptr == string[0]) - { - if (ptr != m_sharedString->string && !std::isspace(*(ptr-1))) - continue; - - const char* p = &string[1]; - const char* tPtr = ptr+1; - while (true) - { - if (*p == '\0') - { - if (*tPtr == '\0' || std::isspace(*tPtr)) - return ptr-m_sharedString->string; - else - break; - } - - if (tPtr > &m_sharedString->string[pos]) - break; - - if (*tPtr != *p) - break; - - p++; - tPtr++; - } - } - } - while (ptr-- != m_sharedString->string); - } - } - - return npos; -} - -unsigned int NzString::FindLastWord(const NzString& string, int start, nzUInt32 flags) const -{ - if (string.m_sharedString->size == 0 || string.m_sharedString->size > m_sharedString->size) - return npos; - - if (start < 0) - start = std::max(static_cast(m_sharedString->size + start), 0); - - unsigned int pos = static_cast(start); - if (pos >= m_sharedString->size) - return npos; - - const char* ptr = &m_sharedString->string[pos]; - const char* limit = &m_sharedString->string[string.m_sharedString->size-1]; - - if (flags & HandleUtf8) - { - if (utf8::internal::is_trail(*ptr)) - utf8::unchecked::prior(ptr); // On s'assure d'avoir un pointeur vers le début d'un caractère - - utf8::unchecked::iterator it(ptr); - - if (flags & CaseInsensitive) - { - const char* t = string.m_sharedString->string; // utf8(::unchecked)::next affecte l'itérateur en argument - nzUInt32 c = NzUnicode::GetLowercase(utf8::unchecked::next(t)); - do - { - if (NzUnicode::GetLowercase(*it) == c) - { - if (it.base() != m_sharedString->string) - { - it--; - if (!(NzUnicode::GetCategory(*it++) & NzUnicode::Category_Separator)) - continue; - } - - utf8::unchecked::iterator p(t); - utf8::unchecked::iterator tIt = it; - ++tIt; - - while (true) - { - if (*p == '\0') - { - if (*tIt == '\0' || NzUnicode::GetCategory(*tIt) & NzUnicode::Category_Separator) - return it.base() - m_sharedString->string; - else - break; - } - - if (tIt.base() > &m_sharedString->string[pos]) - break; - - if (NzUnicode::GetLowercase(*tIt) != NzUnicode::GetLowercase(*p)) - break; - - ++p; - ++tIt; - } - } - } - while (it--.base() != m_sharedString->string); - } - else - { - const char* t = string.m_sharedString->string; // utf8(::unchecked)::next affecte l'itérateur en argument - nzUInt32 c = utf8::unchecked::next(t); - do - { - if (*it == c) - { - if (it.base() != m_sharedString->string) - { - it--; - if (!(NzUnicode::GetCategory(*it++) & NzUnicode::Category_Separator)) - continue; - } - - utf8::unchecked::iterator p(t); - utf8::unchecked::iterator tIt = it; - ++tIt; - - while (true) - { - if (*p == '\0') - { - if (*tIt == '\0' || NzUnicode::GetCategory(*tIt) & NzUnicode::Category_Separator) - return it.base() - m_sharedString->string; - else - break; - } - - if (tIt.base() > &m_sharedString->string[pos]) - break; - - if (*tIt != *p) - break; - - ++p; - ++tIt; - } - } - } - while (it--.base() != m_sharedString->string); - } - } - else - { - ///Algo 2.FindLastWord#2 (Taille du pattern connue) - if (flags & CaseInsensitive) - { - char c = nzToLower(string.m_sharedString->string[string.m_sharedString->size-1]); - do - { - if (nzToLower(*ptr) == c) - { - if (*(ptr+1) != '\0' && !std::isspace(*(ptr+1))) - continue; - - const char* p = &string.m_sharedString->string[string.m_sharedString->size-1]; - for (; p >= &string.m_sharedString->string[0]; --p, --ptr) - { - if (nzToLower(*ptr) != nzToLower(*p)) - break; - - if (p == &string.m_sharedString->string[0]) - { - if (ptr == m_sharedString->string || std::isspace(*(ptr-1))) - return ptr-m_sharedString->string; - else - break; - } - - if (ptr == m_sharedString->string) - return npos; - } - } - } - while (ptr-- > limit); - } - else - { - do - { - if (*ptr == string.m_sharedString->string[string.m_sharedString->size-1]) - { - if (*(ptr+1) != '\0' && !std::isspace(*(ptr+1))) - continue; - - const char* p = &string.m_sharedString->string[string.m_sharedString->size-1]; - for (; p >= &string.m_sharedString->string[0]; --p, --ptr) - { - if (*ptr != *p) - break; - - if (p == &string.m_sharedString->string[0]) - { - if (ptr == m_sharedString->string || std::isspace(*(ptr-1))) - return ptr-m_sharedString->string; - else - break; - } - - if (ptr == m_sharedString->string) - return npos; - } - } - } - while (ptr-- > limit); - } - } - - return npos; -} - -unsigned int NzString::FindWord(const char* string, int start, nzUInt32 flags) const -{ - if (!string || !string[0] || m_sharedString->size == 0) - return npos; - - if (start < 0) - start = std::max(static_cast(m_sharedString->size + start), 0); - - unsigned int pos = static_cast(start); - if (pos >= m_sharedString->size) - return npos; - - ///Algo 3.FindWord#3 (Taille du pattern inconnue) - const char* ptr = m_sharedString->string; - if (flags & HandleUtf8) - { - if (utf8::internal::is_trail(*ptr)) - utf8::unchecked::prior(ptr); // On s'assure d'avoir un pointeur vers le début d'un caractère - - utf8::unchecked::iterator it(ptr); - - if (flags & CaseInsensitive) - { - const char* t = string; // utf8(::unchecked)::next affecte l'itérateur en argument - nzUInt32 c = NzUnicode::GetLowercase(utf8::unchecked::next(t)); - - do - { - if (*it == c) - { - if (it.base() != m_sharedString->string) - { - it--; - if (!(NzUnicode::GetCategory(*it++) & NzUnicode::Category_Separator)) - continue; - } - - utf8::unchecked::iterator p(t); - utf8::unchecked::iterator tIt = it; - ++tIt; - - while (true) - { - if (*p == '\0') - { - if (*tIt == '\0' || NzUnicode::GetCategory(*it++) & NzUnicode::Category_Separator) - return it.base() - m_sharedString->string; - else - break; - } - - if (NzUnicode::GetLowercase(*tIt) != NzUnicode::GetLowercase(*p)) - break; - - p++; - tIt++; - } - } - } - while (*++ptr); - } - else - { - const char* t = string; // utf8(::unchecked)::next affecte l'itérateur en argument - nzUInt32 c = NzUnicode::GetLowercase(utf8::unchecked::next(t)); - - do - { - if (*it == c) - { - if (it.base() != m_sharedString->string) - { - it--; - if (!(NzUnicode::GetCategory(*it++) & NzUnicode::Category_Separator)) - continue; - } - - utf8::unchecked::iterator p(t); - utf8::unchecked::iterator tIt = it; - ++tIt; - - while (true) - { - if (*p == '\0') - { - if (*tIt == '\0' || NzUnicode::GetCategory(*it++) & NzUnicode::Category_Separator) - return it.base() - m_sharedString->string; - else - break; - } - - if (*tIt != *p) - break; - - p++; - tIt++; - } - } - } - while (*++ptr); - } - } - else - { - if (flags & CaseInsensitive) - { - char c = nzToLower(string[0]); - do - { - if (nzToLower(*ptr) == c) - { - if (ptr != m_sharedString->string && !std::isspace(*(ptr-1))) - continue; - - const char* p = &string[1]; - const char* tPtr = ptr+1; - while (true) - { - if (*p == '\0') - { - if (*tPtr == '\0' || std::isspace(*tPtr)) - return ptr - m_sharedString->string; - else - break; - } - - if (nzToLower(*tPtr) != nzToLower(*p)) - break; - - p++; - tPtr++; - } - } - } - while (*++ptr); - } - else - { - do - { - if (*ptr == string[0]) - { - if (ptr != m_sharedString->string && !std::isspace(*(ptr-1))) - continue; - - const char* p = &string[1]; - const char* tPtr = ptr+1; - while (true) - { - if (*p == '\0') - { - if (*tPtr == '\0' || std::isspace(*tPtr)) - return ptr - m_sharedString->string; - else - break; - } - - if (*tPtr != *p) - break; - - p++; - tPtr++; - } - } - } - while (*++ptr); - } - } - - return npos; -} - -unsigned int NzString::FindWord(const NzString& string, int start, nzUInt32 flags) const -{ - if (string.m_sharedString->size == 0 || string.m_sharedString->size > m_sharedString->size) - return npos; - - if (start < 0) - start = std::max(static_cast(m_sharedString->size + start), 0); - - unsigned int pos = static_cast(start); - if (pos >= m_sharedString->size) - return npos; - - char* ptr = m_sharedString->string; - if (flags & HandleUtf8) - { - ///Algo 3.FindWord#3 (Itérateur trop lent pour #2) - if (utf8::internal::is_trail(*ptr)) - utf8::unchecked::prior(ptr); // On s'assure d'avoir un pointeur vers le début d'un caractère - - utf8::unchecked::iterator it(ptr); - - if (flags & CaseInsensitive) - { - const char* t = string.m_sharedString->string; // utf8(::unchecked)::next affecte l'itérateur en argument - nzUInt32 c = NzUnicode::GetLowercase(utf8::unchecked::next(t)); - - do - { - if (*it == c) - { - if (it.base() != m_sharedString->string) - { - it--; - if (!(NzUnicode::GetCategory(*it++) & NzUnicode::Category_Separator)) - continue; - } - - utf8::unchecked::iterator p(t); - utf8::unchecked::iterator tIt = it; - ++tIt; - - while (true) - { - if (*p == '\0') - { - if (*tIt == '\0' || NzUnicode::GetCategory(*it++) & NzUnicode::Category_Separator) - return it.base() - m_sharedString->string; - else - break; - } - - if (NzUnicode::GetLowercase(*tIt) != NzUnicode::GetLowercase(*p)) - break; - - p++; - tIt++; - } - } - } - while (*++ptr); - } - else - { - const char* t = string.m_sharedString->string; // utf8(::unchecked)::next affecte l'itérateur en argument - nzUInt32 c = NzUnicode::GetLowercase(utf8::unchecked::next(t)); - - do - { - if (*it == c) - { - if (it.base() != m_sharedString->string) - { - it--; - if (!(NzUnicode::GetCategory(*it++) & NzUnicode::Category_Separator)) - continue; - } - - utf8::unchecked::iterator p(t); - utf8::unchecked::iterator tIt = it; - ++tIt; - - while (true) - { - if (*p == '\0') - { - if (*tIt == '\0' || NzUnicode::GetCategory(*it++) & NzUnicode::Category_Separator) - return it.base() - m_sharedString->string; - else - break; - } - - if (*tIt != *p) - break; - - p++; - tIt++; - } - } - } - while (*++ptr); - } - } - else - { - ///Algo 3.FindWord#2 (Taille du pattern connue) - if (flags & CaseInsensitive) - { - char c = nzToLower(string.m_sharedString->string[0]); - do - { - if (nzToLower(*ptr) == c) - { - if (ptr != m_sharedString->string && !std::isspace(*(ptr-1))) - continue; - - const char* p = &string.m_sharedString->string[1]; - const char* tPtr = ptr+1; - while (true) - { - if (*p == '\0') - { - if (*tPtr == '\0' || std::isspace(*tPtr)) - return ptr - m_sharedString->string; - else - break; - } - - if (nzToLower(*tPtr) != nzToLower(*p)) - break; - - p++; - tPtr++; - } - } - } - while (*++ptr); - } - else - { - while ((ptr = std::strstr(ptr, string.m_sharedString->string))) - { - // Si le mot est bien isolé - if ((ptr == m_sharedString->string || std::isspace(*(ptr-1))) && (*(ptr+m_sharedString->size) == '\0' || std::isspace(*(ptr+m_sharedString->size)))) - return ptr - m_sharedString->string; - - ptr++; - } - } - } - - return npos; -} - -char* NzString::GetBuffer() -{ - EnsureOwnership(); - - return m_sharedString->string; -} - -unsigned int NzString::GetCapacity() const -{ - return m_sharedString->capacity; -} - -const char* NzString::GetConstBuffer() const -{ - return m_sharedString->string; -} - -unsigned int NzString::GetLength() const -{ - #if NAZARA_CORE_SAFE - try - { - #endif - return utf8::distance(m_sharedString->string, &m_sharedString->string[m_sharedString->size]); - #if NAZARA_CORE_SAFE - } - catch (const utf8::exception& exception) - { - NazaraError("UTF-8 error : " + NzString(exception.what())); - return 0; - } - #endif -} - -unsigned int NzString::GetSize() const -{ - return m_sharedString->size; -} - -char* NzString::GetUtf8Buffer(unsigned int* size) const -{ - if (m_sharedString->size == 0) - return nullptr; - - char* buffer = new char[m_sharedString->size+1]; - std::strcpy(buffer, m_sharedString->string); - - if (size) - *size = m_sharedString->size; - - return buffer; -} - -char16_t* NzString::GetUtf16Buffer(unsigned int* size) const -{ - if (m_sharedString->size == 0) - return nullptr; - - std::vector utf16; - utf16.reserve(m_sharedString->size); - #if NAZARA_CORE_SAFE - try - { - #endif - utf8::utf8to16(m_sharedString->string, &m_sharedString->string[m_sharedString->size], std::back_inserter(utf16)); - #if NAZARA_CORE_SAFE - } - catch (const utf8::exception& exception) - { - NazaraError("UTF-8 error : " + NzString(exception.what())); - return nullptr; - } - #endif - - unsigned int bufferSize = utf16.size(); - if (bufferSize == 0) - return nullptr; - - char16_t* buffer = new char16_t[bufferSize+1]; - std::memcpy(buffer, &utf16[0], bufferSize*sizeof(char16_t)); - buffer[bufferSize] ='\0'; - - if (size) - *size = bufferSize; - - return buffer; -} - -char32_t* NzString::GetUtf32Buffer(unsigned int* size) const -{ - if (m_sharedString->size == 0) - return nullptr; - - #if NAZARA_CORE_SAFE - try - { - #endif - unsigned int bufferSize = utf8::distance(m_sharedString->string, &m_sharedString->string[m_sharedString->size]); - if (bufferSize == 0) - return nullptr; - - char32_t* buffer = new char32_t[bufferSize+1]; - utf8::utf8to32(m_sharedString->string, &m_sharedString->string[m_sharedString->size], buffer); - buffer[bufferSize] ='\0'; - - if (size) - *size = bufferSize; - - return buffer; - #if NAZARA_CORE_SAFE - } - catch (const utf8::exception& exception) - { - NazaraError("UTF-8 error : " + NzString(exception.what())); - return nullptr; - } - #endif -} - -wchar_t* NzString::GetWideBuffer(unsigned int* size) const -{ - static_assert(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4, "wchar_t size is not supported"); - if (m_sharedString->size == 0) - return nullptr; - - #if NAZARA_CORE_SAFE - try - { - #endif - unsigned int bufferSize = utf8::distance(m_sharedString->string, &m_sharedString->string[m_sharedString->size]); - if (bufferSize == 0) - return nullptr; - - wchar_t* buffer = new wchar_t[bufferSize+1]; - if (sizeof(wchar_t) == 4) - utf8::utf8to32(m_sharedString->string, &m_sharedString->string[m_sharedString->size], buffer); - else - { - wchar_t* ptr = buffer; - - utf8::unchecked::iterator it(m_sharedString->string); - do - { - char32_t cp = *it; - if (cp <= 0xFFFF && (cp < 0xD800 || cp > 0xDFFF)) // @Laurent Gomila - *ptr++ = static_cast(cp); - else - *ptr++ = static_cast('?'); - } - while (*it++); - } - - if (size) - *size = bufferSize; - - return buffer; - #if NAZARA_CORE_SAFE - } - catch (const utf8::exception& exception) - { - NazaraError("UTF-8 error : " + NzString(exception.what())); - return nullptr; - } - #endif -} - -NzString NzString::GetWord(unsigned int index, nzUInt32 flags) const -{ - if (m_sharedString->size == 0) - return NzString(); - - NzString temp = Simplified(flags); // Évitons les mauvaises surprises - if (temp.IsEmpty()) - return NzString(); - - unsigned int foundPos = temp.Find(' '); // Simplified nous assure que nous n'avons plus que des espaces comme séparation - unsigned int lastPos = 0; - for (; index > 0; --index) - { - if (foundPos == npos) - return NzString(); - - lastPos = foundPos; - foundPos = temp.Find(' ', foundPos+1); - } - return temp.Substr((lastPos == 0) ? 0 : lastPos+1, (foundPos == npos) ? -1 : static_cast(foundPos-1)); -} - -unsigned int NzString::GetWordPosition(unsigned int index, nzUInt32 flags) const -{ - if (m_sharedString->size == 0) - return npos; - - unsigned int currentWord = 0; - bool inWord = false; - - const char* ptr = m_sharedString->string; - if (flags & HandleUtf8) - { - utf8::unchecked::iterator it(ptr); - do - { - if (NzUnicode::GetCategory(*it) & NzUnicode::Category_Separator) - inWord = false; - else - { - if (!inWord) - { - inWord = true; - if (++currentWord > index) - return static_cast(it.base() - m_sharedString->string); - } - } - } - while (*++it); - } - else - { - while (ptr != &m_sharedString->string[m_sharedString->size]) - { - if (!std::isspace(*ptr++)) - { - if (!inWord) - { - inWord = true; - if (++currentWord > index) - return ptr-m_sharedString->string; - } - } - else - inWord = false; - } - } - - return npos; -} - -NzString& NzString::Insert(int pos, char character) -{ - if (character == '\0') - return *this; - - if (m_sharedString->size == 0 && m_sharedString->capacity == 0) - return operator=(character); - - if (pos < 0) - pos = std::max(static_cast(m_sharedString->size + pos), 0); - - unsigned int start = std::min(static_cast(pos), m_sharedString->size); - - // Si le buffer est déjà suffisamment grand - if (m_sharedString->capacity >= m_sharedString->size+1) - { - EnsureOwnership(); - - std::memmove(&m_sharedString->string[start+1], &m_sharedString->string[start], m_sharedString->size*sizeof(char)); - m_sharedString->string[start] = character; - - m_sharedString->size += 1; - } - else - { - unsigned int newSize = m_sharedString->size+1; - char* newString = new char[newSize+1]; - - char* ptr = newString; - const char* s = m_sharedString->string; - while (ptr != &newString[start]) - *ptr++ = *s++; - - *ptr++ = character; - - std::strcpy(ptr, s); - - ReleaseString(); - - m_sharedString = new SharedString; - m_sharedString->capacity = newSize; - m_sharedString->size = newSize; - m_sharedString->string = newString; - } - - return *this; -} - -NzString& NzString::Insert(int pos, const char* string) -{ - if (!string || !string[0]) - return *this; - - if (pos < 0) - pos = std::max(static_cast(m_sharedString->size + pos), 0); - - unsigned int start = std::min(static_cast(pos), m_sharedString->size); - - // Si le buffer est déjà suffisamment grand - unsigned int len = std::strlen(string); - if (m_sharedString->capacity >= m_sharedString->size+len) - { - EnsureOwnership(); - - std::memmove(&m_sharedString->string[start+len], &m_sharedString->string[start], m_sharedString->size*sizeof(char)); - std::memcpy(&m_sharedString->string[start], string, len*sizeof(char)); - - m_sharedString->size += len; - } - else - { - unsigned int newSize = m_sharedString->size+len; - char* newString = new char[newSize+1]; - - char* ptr = newString; - const char* s = m_sharedString->string; - - while (ptr != &newString[start]) - *ptr++ = *s++; - - const char* p = string; - while (ptr != &newString[start+len]) - *ptr++ = *p++; - - std::strcpy(ptr, s); - - ReleaseString(); - m_sharedString = new SharedString; - m_sharedString->capacity = newSize; - m_sharedString->size = newSize; - m_sharedString->string = newString; - } - - return *this; -} - -NzString& NzString::Insert(int pos, const NzString& string) -{ - if (string.m_sharedString->size == 0) - return *this; - - if (m_sharedString->size == 0 && m_sharedString->capacity < string.m_sharedString->size) - return operator=(string); - - if (pos < 0) - pos = std::max(static_cast(m_sharedString->size + pos), 0); - - unsigned int start = std::min(static_cast(pos), m_sharedString->size); - - // Si le buffer est déjà suffisamment grand - if (m_sharedString->capacity >= m_sharedString->size + string.m_sharedString->size) - { - EnsureOwnership(); - - std::memmove(&m_sharedString->string[start+string.m_sharedString->size], &m_sharedString->string[start], m_sharedString->size*sizeof(char)); - std::memcpy(&m_sharedString->string[start], string.m_sharedString->string, string.m_sharedString->size*sizeof(char)); - - m_sharedString->size += string.m_sharedString->size; - } - else - { - unsigned int newSize = m_sharedString->size+string.m_sharedString->size; - char* newString = new char[newSize+1]; - - char* ptr = newString; - const char* s = m_sharedString->string; - - while (ptr != &newString[start]) - *ptr++ = *s++; - - const char* p = string.m_sharedString->string; - while (ptr != &newString[start+string.m_sharedString->size]) - *ptr++ = *p++; - - std::strcpy(ptr, s); - - ReleaseString(); - m_sharedString = new SharedString; - m_sharedString->capacity = newSize; - m_sharedString->size = newSize; - m_sharedString->string = newString; - } - - return *this; -} - -bool NzString::IsEmpty() const -{ - return m_sharedString->size == 0; -} - -bool NzString::IsNull() const -{ - return m_sharedString == &emptyString; -} - -bool NzString::IsNumber(nzUInt8 base, nzUInt32 flags) const -{ - #if NAZARA_CORE_SAFE - if (base < 2 || base > 36) - { - NazaraError("Base must be between 2 and 36"); - return false; - } - #endif - - if (m_sharedString->size == 0) - return false; - - NzString check = Simplified(); - if (check.m_sharedString->size) - return false; - - char* ptr = (check.m_sharedString->string[0] == '-') ? &check.m_sharedString->string[1] : check.m_sharedString->string; - - if (base > 10) - { - if (flags & CaseInsensitive) - { - do - { - char c = *ptr; - if (c != ' ' && (c < '0' || (c > '9' && c < 'A') || (c > 'A'+base-1 && c < 'a') || c > 'a'+base-1)) - return false; - } - while (*++ptr); - } - else - { - do - { - char c = *ptr; - if (c != ' ' && (c < '0' || (c > '9' && c < 'a') || c > 'a'+base-1)) - return false; - } - while (*++ptr); - } - } - else - { - do - { - char c = *ptr; - if (c != ' ' && (c < '0' || c > '0'+base-1)) - return false; - } - while (*++ptr); - } - - return true; -} - -bool NzString::Match(const char* pattern) const -{ - if (m_sharedString->size == 0 || !pattern) - return false; - - // Par Jack Handy - akkhandy@hotmail.com - // From : http://www.codeproject.com/Articles/1088/Wildcard-string-compare-globbing - const char* str = m_sharedString->string; - while (*str && *pattern != '*') - { - if (*pattern != *str && *pattern != '?') - return false; - - pattern++; - str++; - } - - const char* cp = nullptr; - const char* mp = nullptr; - while (*str) - { - if (*pattern == '*') - { - if (!*++pattern) - return true; - - mp = pattern; - cp = str+1; - } - else if (*pattern == *str || *pattern == '?') - { - pattern++; - str++; - } - else - { - pattern = mp; - str = cp++; - } - } - - while (*pattern == '*') - pattern++; - - return !*pattern; -} - -bool NzString::Match(const NzString& pattern) const -{ - return Match(pattern.m_sharedString->string); -} - -NzString& NzString::Prepend(char character) -{ - return Insert(0, character); -} - -NzString& NzString::Prepend(const char* string) -{ - return Insert(0, string); -} - -NzString& NzString::Prepend(const NzString& string) -{ - return Insert(0, string); -} - -unsigned int NzString::Replace(char oldCharacter, char newCharacter, int start, nzUInt32 flags) -{ - if (oldCharacter == '\0' || oldCharacter == newCharacter) - return 0; - - if (newCharacter == '\0') // Dans ce cas, il faut passer par un algorithme plus complexe - return Replace(NzString(oldCharacter), NzString(), start); - - if (start < 0) - start = std::max(m_sharedString->size + start, 0U); - - unsigned int pos = static_cast(start); - if (pos >= m_sharedString->size) - return npos; - - unsigned int count = 0; - char* ptr = &m_sharedString->string[pos]; - bool found = false; - if (flags & CaseInsensitive) - { - char character_lower = nzToLower(oldCharacter); - char character_upper = nzToUpper(oldCharacter); - do - { - if (*ptr == character_lower || *ptr == character_upper) - { - if (!found) - { - unsigned int offset = ptr-m_sharedString->string; - - EnsureOwnership(); - - ptr = &m_sharedString->string[offset]; - found = true; - } - - *ptr = newCharacter; - ++count; - } - } - while (*++ptr); - } - else - { - while ((ptr = std::strchr(ptr, oldCharacter))) - { - if (!found) - { - unsigned int offset = ptr-m_sharedString->string; - - EnsureOwnership(); - - ptr = &m_sharedString->string[offset]; - found = true; - } - - *ptr = newCharacter; - ++count; - } - } - - return count; -} - -unsigned int NzString::Replace(const char* oldString, const char* replaceString, int start, nzUInt32 flags) -{ - if (!oldString || !oldString[0]) - return 0; - - if (start < 0) - start = std::max(m_sharedString->size + start, 0U); - - unsigned int pos = static_cast(start); - if (pos >= m_sharedString->size) - return 0; - - unsigned int oSize = std::strlen(oldString); - if (oSize == 0) - return 0; - - unsigned int rSize = (replaceString) ? std::strlen(replaceString) : 0; - - unsigned int count = 0; - if (oSize == rSize) - { - bool found = false; - - // Si aucun changement de taille n'est nécessaire, nous pouvons alors utiliser un algorithme bien plus rapide - while ((pos = Find(oldString, pos, flags)) != npos) - { - if (!found) - { - EnsureOwnership(); - found = true; - } - - std::memcpy(&m_sharedString->string[pos], replaceString, oSize*sizeof(char)); - pos += oSize; - - ++count; - } - } - else ///TODO: Algorithme de remplacement sans changement de buffer (si rSize < oSize) - { - unsigned int newSize = m_sharedString->size + Count(oldString)*(rSize - oSize); - if (newSize == m_sharedString->size) // Alors c'est que Count(oldString) == 0 - return 0; - - char* newString = new char[newSize+1]; - - ///Algo 4.Replace#2 - char* ptr = newString; - const char* p = m_sharedString->string; - - while ((pos = Find(oldString, pos, flags)) != npos) - { - const char* r = &m_sharedString->string[pos]; - - std::memcpy(ptr, p, (r-p)*sizeof(char)); - ptr += r-p; - std::memcpy(ptr, replaceString, rSize*sizeof(char)); - ptr += rSize; - p = r+oSize; - pos += oSize; - - count++; - } - - std::strcpy(ptr, p); // Ajoute le caractère de fin par la même occasion - - ReleaseString(); - m_sharedString = new SharedString; - m_sharedString->capacity = newSize; - m_sharedString->size = newSize; - m_sharedString->string = newString; - } - - return count; -} - -unsigned int NzString::Replace(const NzString& oldString, const NzString& replaceString, int start, nzUInt32 flags) -{ - if (oldString.m_sharedString->size == 0) - return 0; - - if (start < 0) - start = std::max(m_sharedString->size + start, 0U); - - unsigned int pos = static_cast(start); - if (pos >= m_sharedString->size || oldString.m_sharedString->size == 0) - return 0; - - unsigned int count = 0; - if (oldString.m_sharedString->size == replaceString.m_sharedString->size) - { - bool found = false; - - // Si aucun changement de taille n'est nécessaire, nous pouvons alors utiliser un algorithme bien plus rapide - while ((pos = Find(oldString, pos, flags)) != npos) - { - if (!found) - { - EnsureOwnership(); - found = true; - } - - std::memcpy(&m_sharedString->string[pos], replaceString.m_sharedString->string, oldString.m_sharedString->size*sizeof(char)); - pos += oldString.m_sharedString->size; - - ++count; - } - } - else - { - unsigned int newSize = m_sharedString->size + Count(oldString)*(replaceString.m_sharedString->size - oldString.m_sharedString->size); - if (newSize == m_sharedString->size) // Alors c'est que Count(oldString) == 0 - return 0; - - char* newString = new char[newSize+1]; - - ///Algo 4.Replace#2 - char* ptr = newString; - const char* p = m_sharedString->string; - - while ((pos = Find(oldString, pos, flags)) != npos) - { - const char* r = &m_sharedString->string[pos]; - - std::memcpy(ptr, p, (r-p)*sizeof(char)); - ptr += r-p; - std::memcpy(ptr, replaceString.m_sharedString->string, replaceString.m_sharedString->size*sizeof(char)); - ptr += replaceString.m_sharedString->size; - p = r+oldString.m_sharedString->size; - pos += oldString.m_sharedString->size; - - count++; - } - - std::strcpy(ptr, p); // Ajoute le caractère de fin par la même occasion - - ReleaseString(); - m_sharedString = new SharedString; - m_sharedString->capacity = newSize; - m_sharedString->size = newSize; - m_sharedString->string = newString; - } - - return count; -} - -unsigned int NzString::ReplaceAny(const char* oldCharacters, char replaceCharacter, int start, nzUInt32 flags) -{ - ///FIXME: Ne gère pas l'UTF-8 - if (!oldCharacters || !oldCharacters[0]) - return 0; - - /*if (replaceCharacter == '\0') // Dans ce cas, il faut passer par un algorithme plus complexe - return ReplaceAny(NzString(oldCharacters), NzString(), start);*/ - - if (start < 0) - start = std::max(m_sharedString->size + start, 0U); - - unsigned int pos = static_cast(start); - if (pos >= m_sharedString->size) - return npos; - - unsigned int count = 0; - char* ptr = &m_sharedString->string[pos]; - if (flags & CaseInsensitive) - { - do - { - const char* c = oldCharacters; - char character = nzToLower(*ptr); - bool found = false; - do - { - if (character == nzToLower(*c)) - { - if (!found) - { - unsigned int offset = ptr-m_sharedString->string; - - EnsureOwnership(); - - ptr = &m_sharedString->string[offset]; - found = true; - } - - *ptr = replaceCharacter; - ++count; - break; - } - } - while (*++c); - } - while (*++ptr); - } - else - { - bool found = false; - while ((ptr = std::strpbrk(ptr, oldCharacters))) - { - if (!found) - { - unsigned int offset = ptr-m_sharedString->string; - - EnsureOwnership(); - - ptr = &m_sharedString->string[offset]; - found = true; - } - - *ptr++ = replaceCharacter; - ++count; - } - } - - return count; -} -/* - unsigned int NzString::ReplaceAny(const char* oldCharacters, const char* replaceString, int start, nzUInt32 flags) - { - if (start < 0) - { - start = m_sharedString->size+start; - if (start < 0) - start = 0; - } - - unsigned int pos = static_cast(start); - unsigned int oSize = (oldCharacters) ? std::strlen(oldCharacters) : 0; - unsigned int rSize = (replaceString) ? std::strlen(replaceString) : 0; - - if (pos >= m_sharedString->size || m_sharedString->size == 0 || oSize == 0) - return 0; - - unsigned int count = 0; - - if (rSize == 1) // On utilise un algorithme optimisé - { - EnsureOwnership(); - - f or (; pos < m_sharedString->size; ++pos) - { - for (unsigned int i = 0; i < oSize; ++i) - { - if (m_sharedString->string[pos] == oldCharacters[i]) - { - m_sharedString->string[pos] = replaceString[0]; - ++count; - - break; - } - } - } - } - else - { - unsigned int newSize; - { - unsigned int count = CountAny(oldCharacters); - newSize = m_sharedString->size - count + count*rSize; - } - char* newString = new char[newSize+1]; - - unsigned int j = 0; - for (unsigned int i = 0; i < m_sharedString->size; ++i) - { - if (i < pos) // Avant la position où on est censé commencer à remplacer, on ne fait que recopier - newString[j++] = m_sharedString->string[i]; - else - { - bool found = false; - for (unsigned int l = 0; l < oSize; ++l) - { - if (m_sharedString->string[i] == oldCharacters[l]) - { - for (unsigned int k = 0; k < rSize; ++k) - newString[j++] = replaceString[k]; - - ++count; - found = true; - break; // Simple façon d'éviter la ligne après la boucle - } - } - - if (!found) - newString[j++] = m_sharedString->string[i]; - } - } - newString[newSize] = '\0'; - - ReleaseString(); - - m_sharedString->size = newSize; - m_sharedString->string = newString; - } - - return count; - } - - unsigned int NzString::ReplaceAny(const NzString& oldCharacters, const NzString& replaceString, int start, nzUInt32 flags) - { - if (start < 0) - { - start = m_sharedString->size+start; - if (start < 0) - start = 0; - } - - unsigned int pos = static_cast(start); - - if (pos >= m_sharedString->size || m_sharedString->size == 0 || oldCharacters.m_sharedString->size == 0) - return 0; - - unsigned int count = 0; - - if (replaceString.m_sharedString->size == 1) // On utilise un algorithme optimisé - { - EnsureOwnership(); - - char character = replaceString[0]; - for (; pos < m_sharedString->size; ++pos) - { - for (unsigned int i = 0; i < oldCharacters.m_sharedString->size; ++i) - { - if (m_sharedString->string[pos] == oldCharacters[i]) - { - m_sharedString->string[pos] = character; - ++count; - break; - } - } - } - } - else - { - unsigned int newSize; - { - unsigned int count = CountAny(oldCharacters); - newSize = m_sharedString->size - count + count*replaceString.m_sharedString->size; - } - char* newString = new char[newSize+1]; - - unsigned int j = 0; - for (unsigned int i = 0; i < m_sharedString->size; ++i) - { - if (i < pos) // Avant la position où on est censé commencer à remplacer, on ne fait que recopier - newString[j++] = m_sharedString->string[i]; - else - { - bool found = false; - for (unsigned int l = 0; l < oldCharacters.m_sharedString->size; ++l) - { - if (m_sharedString->string[i] == oldCharacters[l]) - { - for (unsigned int k = 0; k < replaceString.m_sharedString->size; ++k) - newString[j++] = replaceString[k]; - - ++count; - found = true; - break; // Simple façon d'éviter la ligne après la boucle - } - } - - if (!found) - newString[j++] = m_sharedString->string[i]; - } - } - newString[newSize] = '\0'; - - ReleaseString(); - - m_sharedString->size = newSize; - m_sharedString->string = newString; - } - - return count; - } -*/ -void NzString::Reserve(unsigned int bufferSize) -{ - if (m_sharedString->capacity >= bufferSize) - return; - - char* ptr = new char[bufferSize+1]; - if (m_sharedString->size > 0) - std::strcpy(ptr, m_sharedString->string); - - unsigned int size = m_sharedString->size; - - ReleaseString(); - m_sharedString = new SharedString; - m_sharedString->capacity = bufferSize; - m_sharedString->size = size; - m_sharedString->string = ptr; -} - -NzString& NzString::Resize(int size, char character) -{ - if (size == 0) - { - Clear(true); - return *this; - } - - if (size < 0) - size = std::max(static_cast(m_sharedString->size + size), 0); - - unsigned int newSize = static_cast(size); - - if (m_sharedString->capacity >= newSize) - { - EnsureOwnership(); - - // Nous avons déjà la place requise, contentons-nous de remplir le buffer - if (character != '\0' && newSize > m_sharedString->size) - { - char* ptr = &m_sharedString->string[m_sharedString->size]; - char* limit = &m_sharedString->string[newSize]; - while (ptr != limit) - *ptr++ = character; - } - - m_sharedString->size = newSize; - m_sharedString->string[newSize] = '\0'; - } - else // On veut forcément agrandir la chaine - { - char* newString = new char[newSize+1]; - if (m_sharedString->size != 0) - std::memcpy(newString, m_sharedString->string, newSize*sizeof(char)); - - char* ptr = &newString[m_sharedString->size]; - char* limit = &newString[newSize]; - while (ptr != limit) - *ptr++ = character; - - ReleaseString(); - m_sharedString = new SharedString; - m_sharedString->capacity = newSize; - m_sharedString->size = newSize; - m_sharedString->string = newString; - } - - return *this; -} - -NzString NzString::Resized(int size, char character) const -{ - if (size < 0) - size = m_sharedString->size + size; - - if (size <= 0) - return NzString(); - - unsigned int newSize = static_cast(size); - if (newSize == m_sharedString->size) - return *this; - - char* str = new char[newSize+1]; - if (newSize > m_sharedString->size) - { - std::memcpy(str, m_sharedString->string, m_sharedString->size); - if (character != '\0') - { - char* ptr = &str[m_sharedString->size]; - char* limit = &str[newSize]; - while (ptr != limit) - *ptr++ = character; - } - } - else - std::memcpy(str, m_sharedString->string, newSize); - - str[newSize] = '\0'; - - return NzString(new SharedString(1, newSize, newSize, str)); -} - -NzString& NzString::Reverse() -{ - if (m_sharedString->size != 0) - { - unsigned int i = 0; - unsigned int j = m_sharedString->size-1; - - while (i < j) - std::swap(m_sharedString->string[i++], m_sharedString->string[j--]); - } - - return *this; -} - -NzString NzString::Reversed() const -{ - if (m_sharedString->size == 0) - return NzString(); - - char* str = new char[m_sharedString->size+1]; - - char* ptr = &str[m_sharedString->size-1]; - char* p = m_sharedString->string; - - do - *ptr-- = *p; - while (*(++p)); - - str[m_sharedString->size] = '\0'; - - return NzString(new SharedString(1, m_sharedString->size, m_sharedString->size, str)); -} - -NzString NzString::Simplified(nzUInt32 flags) const -{ - char* str = new char[m_sharedString->size+1]; - char* p = str; - - const char* ptr = m_sharedString->string; - bool inword = false; - if (flags & HandleUtf8) - { - utf8::unchecked::iterator it(ptr); - do - { - if (NzUnicode::GetCategory(*it) & NzUnicode::Category_Separator) - { - if (inword) - { - *p++ = ' '; - inword = false; - } - } - else - { - p = utf8::append(*it, p); - inword = true; - } - } - while (*++it); - } - else - { - const char* limit = &m_sharedString->string[m_sharedString->size]; - do - { - if (std::isspace(*ptr)) - { - if (inword) - { - *p++ = ' '; - inword = false; - } - } - else - { - *p++ = *ptr; - inword = true; - } - } - while (++ptr != limit); - } - - if (!inword) - p--; - - *p = '\0'; - - return NzString(new SharedString(1, m_sharedString->size, p-str, str)); -} - -NzString& NzString::Simplify(nzUInt32 flags) -{ - return operator=(Simplified(flags)); -} - -unsigned int NzString::Split(std::vector& result, char separation, int start, nzUInt32 flags) const -{ - if (separation == '\0' || m_sharedString->size == 0) - return 0; - - unsigned int lastSep = Find(separation, start, flags); - if (lastSep == npos) - { - result.push_back(*this); - return 1; - } - else if (lastSep != 0) - result.push_back(Substr(0, lastSep-1)); - - while (true) - { - unsigned int sep = Find(separation, lastSep+1, flags); - if (sep == npos) - break; - - if (sep-lastSep > 1) - result.push_back(Substr(lastSep+1, sep-1)); - - lastSep = sep; - } - - if (lastSep != m_sharedString->size-1) - result.push_back(Substr(lastSep+1)); - - return result.size(); -} - -unsigned int NzString::Split(std::vector& result, const char* separation, int start, nzUInt32 flags) const -{ - unsigned int size = (separation) ? std::strlen(separation) : 0; - if (m_sharedString->size == 0) - return 0; - else if (size == 0) - { - result.reserve(m_sharedString->size); - for (unsigned int i = 0; i < m_sharedString->size; ++i) - result.push_back(NzString(m_sharedString->string[i])); - - return m_sharedString->size; - } - else if (size > m_sharedString->size) - { - result.push_back(*this); - return 1; - } - - unsigned int lastSep = Find(separation, start, flags); - unsigned int oldSize = result.size(); - if (lastSep == npos) - { - result.push_back(*this); - return 1; - } - else if (lastSep != 0) - result.push_back(Substr(0, lastSep-1)); - - unsigned int sep; - while ((sep = Find(separation, lastSep+size, flags)) != npos) - { - if (sep-lastSep > size) - result.push_back(Substr(lastSep+size, sep-1)); - - lastSep = sep; - } - - if (lastSep != m_sharedString->size-size) - result.push_back(Substr(lastSep+size)); - - return result.size()-oldSize; -} - -unsigned int NzString::Split(std::vector& result, const NzString& separation, int start, nzUInt32 flags) const -{ - if (m_sharedString->size == 0) - return 0; - else if (separation.m_sharedString->size == 0) - { - result.reserve(m_sharedString->size); - for (unsigned int i = 0; i < m_sharedString->size; ++i) - result.push_back(NzString(m_sharedString->string[i])); - - return m_sharedString->size; - } - else if (separation.m_sharedString->size > m_sharedString->size) - { - result.push_back(*this); - return 1; - } - - unsigned int lastSep = Find(separation, start, flags); - unsigned int oldSize = result.size(); - if (lastSep == npos) - { - result.push_back(*this); - return 1; - } - else if (lastSep != 0) - result.push_back(Substr(0, lastSep-1)); - - unsigned int sep; - while ((sep = Find(separation, lastSep+separation.m_sharedString->size, flags)) != npos) - { - if (sep-lastSep > separation.m_sharedString->size) - result.push_back(Substr(lastSep+separation.m_sharedString->size, sep-1)); - - lastSep = sep; - } - - if (lastSep != m_sharedString->size-separation.m_sharedString->size) - result.push_back(Substr(lastSep+separation.m_sharedString->size)); - - return result.size()-oldSize; -} - -unsigned int NzString::SplitAny(std::vector& result, const char* separations, int start, nzUInt32 flags) const -{ - if (m_sharedString->size == 0) - return 0; - - unsigned int oldSize = result.size(); - - unsigned int lastSep = FindAny(separations, start, flags); - if (lastSep == npos) - { - result.push_back(*this); - return 1; - } - else if (lastSep != 0) - result.push_back(Substr(0, lastSep-1)); - - unsigned int sep; - while ((sep = FindAny(separations, lastSep+1, flags)) != npos) - { - if (sep-lastSep > 1) - result.push_back(Substr(lastSep+1, sep-1)); - - lastSep = sep; - } - - if (lastSep != m_sharedString->size-1) - result.push_back(Substr(lastSep+1)); - - return result.size()-oldSize; -} - -unsigned int NzString::SplitAny(std::vector& result, const NzString& separations, int start, nzUInt32 flags) const -{ - if (m_sharedString->size == 0) - return 0; - - unsigned int lastSep = FindAny(separations, start, flags); - unsigned int oldSize = result.size(); - if (lastSep == npos) - { - result.push_back(*this); - return 1; - } - else if (lastSep != 0) - result.push_back(Substr(0, lastSep-1)); - - unsigned int sep; - while ((sep = FindAny(separations, lastSep+1, flags)) != npos) - { - if (sep-lastSep > 1) - result.push_back(Substr(lastSep+1, sep-1)); - - lastSep = sep; - } - - if (lastSep != m_sharedString->size-1) - result.push_back(Substr(lastSep+1)); - - return result.size()-oldSize; -} - -bool NzString::StartsWith(char character, nzUInt32 flags) const -{ - if (character == '\0' || m_sharedString->size == 0) - return false; - - if (flags & CaseInsensitive) - return nzToLower(m_sharedString->string[0]) == nzToLower(character); - else - return m_sharedString->string[0] == character; -} - -bool NzString::StartsWith(const char* string, nzUInt32 flags) const -{ - if (!string || !string[0] || m_sharedString->size == 0) - return false; - - if (flags & CaseInsensitive) - { - if (flags & HandleUtf8) - { - utf8::unchecked::iterator it(m_sharedString->string); - utf8::unchecked::iterator it2(string); - do - { - if (*it2 == '\0') - return true; - - if (NzUnicode::GetLowercase(*it) != NzUnicode::GetLowercase(*it2)) - return false; - - ++it2; - } - while (*it++); - } - else - { - char* ptr = m_sharedString->string; - const char* s = string; - do - { - if (*s == '\0') - return true; - - if (nzToLower(*ptr) != nzToLower(*s)) - return false; - - s++; - } - while (*ptr++); - } - } - else - { - char* ptr = m_sharedString->string; - const char* s = string; - do - { - if (*s == '\0') - return true; - - if (*ptr != *s) - return false; - - s++; - } - while (*ptr++); - } - - return false; -} - -bool NzString::StartsWith(const NzString& string, nzUInt32 flags) const -{ - if (string.m_sharedString->size == 0) - return false; - - if (m_sharedString->size < string.m_sharedString->size) - return false; - - if (flags & CaseInsensitive) - { - if (flags & HandleUtf8) - { - utf8::unchecked::iterator it(m_sharedString->string); - utf8::unchecked::iterator it2(string.m_sharedString->string); - do - { - if (*it2 == '\0') - return true; - - if (NzUnicode::GetLowercase(*it) != NzUnicode::GetLowercase(*it2)) - return false; - - ++it2; - } - while (*it++); - } - else - { - char* ptr = m_sharedString->string; - const char* s = string.m_sharedString->string; - do - { - if (*s == '\0') - return true; - - if (nzToLower(*ptr) != nzToLower(*s)) - return false; - - s++; - } - while (*ptr++); - } - } - else - return std::memcmp(m_sharedString->string, string.m_sharedString->string, string.m_sharedString->size*sizeof(char)) == 0; - - return false; -} - -NzString NzString::Substr(int startPos, int endPos) const -{ - if (startPos < 0) - startPos = std::max(m_sharedString->size+startPos, 0U); - - unsigned int start = static_cast(startPos); - - if (endPos < 0) - { - endPos = m_sharedString->size+endPos; - if (endPos < 0) - return NzString(); - } - - unsigned int minEnd = std::min(static_cast(endPos), m_sharedString->size-1); - - if (start > minEnd || start >= m_sharedString->size) - return NzString(); - - unsigned int size = minEnd-start+1; - char* str = new char[size+1]; - std::memcpy(str, &m_sharedString->string[start], size*sizeof(char)); - str[size] = '\0'; - - return NzString(new SharedString(1, size, size, str)); -} - -NzString NzString::SubstrFrom(char character, int startPos, bool fromLast, bool include, nzUInt32 flags) const -{ - if (character == '\0') - return *this; - - unsigned int pos; - if (fromLast) - pos = FindLast(character, startPos, flags); - else - pos = Find(character, startPos, flags); - - if (pos == 0 and include) - return *this; - else if (pos == npos) - return NzString(); - - return Substr(pos+((include) ? 0 : 1)); -} - -NzString NzString::SubstrFrom(const char* string, int startPos, bool fromLast, bool include, nzUInt32 flags) const -{ - unsigned int pos; - if (fromLast) - pos = FindLast(string, startPos, flags); - else - pos = Find(string, startPos, flags); - - if (pos == 0 && include) - return *this; - else if (pos == npos) - return NzString(); - - return Substr(pos+((include) ? 0 : std::strlen(string))); -} - -NzString NzString::SubstrFrom(const NzString& string, int startPos, bool fromLast, bool include, nzUInt32 flags) const -{ - unsigned int pos; - if (fromLast) - pos = FindLast(string, startPos, flags); - else - pos = Find(string, startPos, flags); - - if (pos == 0 && include) - return *this; - else if (pos == npos) - return NzString(); - - return Substr(pos+((include) ? 0 : string.m_sharedString->size)); -} - -NzString NzString::SubstrTo(char character, int startPos, bool toLast, bool include, nzUInt32 flags) const -{ - if (character == '\0') - return *this; - - unsigned int pos; - if (toLast) - pos = FindLast(character, startPos); - else - pos = Find(character, startPos, flags); - - if (pos == 0) - return (include) ? character : NzString(); - else if (pos == npos) - return *this; - - return Substr(0, pos+((include) ? 1 : 0)-1); -} - -NzString NzString::SubstrTo(const char* string, int startPos, bool toLast, bool include, nzUInt32 flags) const -{ - unsigned int pos; - if (toLast) - pos = FindLast(string, startPos, flags); - else - pos = Find(string, startPos, flags); - - if (pos == 0) - return (include) ? string : NzString(); - else if (pos == npos) - return *this; - - return Substr(0, pos+((include) ? std::strlen(string) : 0)-1); -} - -NzString NzString::SubstrTo(const NzString& string, int startPos, bool toLast, bool include, nzUInt32 flags) const -{ - unsigned int pos; - if (toLast) - pos = FindLast(string, startPos, flags); - else - pos = Find(string, startPos, flags); - - if (pos == 0) - return (include) ? string : NzString(); - else if (pos == npos) - return *this; - - return Substr(0, pos+((include) ? string.m_sharedString->size : 0)-1); -} - -void NzString::Swap(NzString& str) -{ - std::swap(m_sharedString, str.m_sharedString); -} - -bool NzString::ToBool(bool* value, nzUInt32 flags) const -{ - if (m_sharedString->size == 0) - return false; - - NzString word = GetWord(0); - - if (word[0] == '1') - { - if (value) - *value = true; - } - else if (word[0] == '0') - { - if (value) - *value = false; - } - else - { - if (flags & CaseInsensitive) - word = word.ToLower(); // Les mots identifiés sont en ASCII, inutile de passer le flag unicode - - if (word == "true") - { - if (value) - *value = true; - } - else if (word == "false") - { - if (value) - *value = false; - } - else - return false; - } - - return true; -} - -bool NzString::ToDouble(double* value) const -{ - if (m_sharedString->size == 0) - return false; - - if (value) - *value = std::atof(m_sharedString->string); - - return true; -} - -bool NzString::ToInteger(long long* value, nzUInt8 base) const -{ - if (value) - { - bool ok; - *value = NzStringToNumber(*this, base, &ok); - - return ok; - } - else - return IsNumber(base); -} - -NzString NzString::ToLower(nzUInt32 flags) const -{ - if (m_sharedString->size == 0) - return *this; - - if (flags & HandleUtf8) - { - NzString lower; - lower.Reserve(m_sharedString->size); - utf8::unchecked::iterator it(m_sharedString->string); - do - utf8::append(NzUnicode::GetLowercase(*it), std::back_inserter(lower)); - while (*++it); - - return lower; - } - else - { - char* str = new char[m_sharedString->size+1]; - - char* ptr = m_sharedString->string; - char* s = str; - do - *s++ = nzToLower(*ptr); - while (*++ptr); - - *s = '\0'; - - return NzString(new SharedString(1, m_sharedString->size, m_sharedString->size, str)); - } -} - -NzString NzString::ToUpper(nzUInt32 flags) const -{ - if (m_sharedString->size == 0) - return *this; - - if (flags & HandleUtf8) - { - NzString upper; - upper.Reserve(m_sharedString->size); - utf8::unchecked::iterator it(m_sharedString->string); - do - utf8::append(NzUnicode::GetUppercase(*it), std::back_inserter(upper)); - while (*++it); - - return upper; - } - else - { - char* str = new char[m_sharedString->size+1]; - - char* ptr = m_sharedString->string; - char* s = str; - do - *s++ = nzToUpper(*ptr); - while (*++ptr); - - *s = '\0'; - - return NzString(new SharedString(1, m_sharedString->size, m_sharedString->size, str)); - } -} - -NzString& NzString::Trim(nzUInt32 flags) -{ - return operator=(Trimmed(flags)); -} - -NzString& NzString::Trim(char character, nzUInt32 flags) -{ - return operator=(Trimmed(character, flags)); -} - -NzString NzString::Trimmed(nzUInt32 flags) const -{ - if (m_sharedString->size == 0) - return *this; - - unsigned int startPos; - unsigned int endPos; - if (flags & HandleUtf8) - { - if ((flags & TrimOnlyRight) == 0) - { - utf8::unchecked::iterator it(m_sharedString->string); - do - { - if (NzUnicode::GetCategory(*it) & NzUnicode::Category_Separator) - break; - } - while (*++it); - - startPos = it.base() - m_sharedString->string; - } - else - startPos = 0; - - if ((flags & TrimOnlyLeft) == 0) - { - utf8::unchecked::iterator it(&m_sharedString->string[m_sharedString->size]); - while ((it--).base() != m_sharedString->string) - { - if (NzUnicode::GetCategory(*it) & NzUnicode::Category_Separator) - break; - } - - endPos = it.base() - m_sharedString->string; - } - else - endPos = m_sharedString->size-1; - } - else - { - startPos = 0; - if ((flags & TrimOnlyRight) == 0) - { - for (; startPos < m_sharedString->size; ++startPos) - { - if (!std::isspace(m_sharedString->string[startPos])) - break; - } - } - - endPos = m_sharedString->size-1; - if ((flags & TrimOnlyLeft) == 0) - { - for (; endPos > 0; --endPos) - { - if (!std::isspace(m_sharedString->string[endPos])) - break; - } - } - } - - return Substr(startPos, endPos); -} - -NzString NzString::Trimmed(char character, nzUInt32 flags) const -{ - if (m_sharedString->size == 0) - return *this; - - unsigned int startPos = 0; - unsigned int endPos = m_sharedString->size-1; - if (flags & CaseInsensitive) - { - char ch = nzToLower(character); - if ((flags & TrimOnlyRight) == 0) - { - for (; startPos < m_sharedString->size; ++startPos) - { - if (nzToLower(m_sharedString->string[startPos]) != ch) - break; - } - } - - if ((flags & TrimOnlyLeft) == 0) - { - for (; endPos > 0; --endPos) - { - if (nzToLower(m_sharedString->string[startPos]) != ch) - break; - } - } - } - else - { - if ((flags & TrimOnlyRight) == 0) - { - for (; startPos < m_sharedString->size; ++startPos) - { - if (m_sharedString->string[startPos] != character) - break; - } - } - - if ((flags & TrimOnlyLeft) == 0) - { - for (; endPos > 0; --endPos) - { - if (m_sharedString->string[startPos] != character) - break; - } - } - } - - return Substr(startPos, endPos); -} - -char* NzString::begin() -{ - return m_sharedString->string; -} - -const char* NzString::begin() const -{ - return m_sharedString->string; -} - -char* NzString::end() -{ - return &m_sharedString->string[m_sharedString->size]; -} - -const char* NzString::end() const -{ - return &m_sharedString->string[m_sharedString->size]; -} - -void NzString::push_front(char c) -{ - operator=(c + *this); -} - -void NzString::push_back(char c) -{ - operator+=(c); -} -/* -char* NzString::rbegin() -{ - return &m_sharedString->string[m_sharedString->size-1]; -} - -const char* NzString::rbegin() const -{ - return &m_sharedString->string[m_sharedString->size-1]; -} - -char* NzString::rend() -{ - return &m_sharedString->string[-1]; -} - -const char* NzString::rend() const -{ - return &m_sharedString->string[-1]; -} -*/ - -NzString::operator std::string() const -{ - return std::string(m_sharedString->string, m_sharedString->size); -} - -char& NzString::operator[](unsigned int pos) -{ - EnsureOwnership(); - - if (pos >= m_sharedString->size) - Resize(pos+1); - - return m_sharedString->string[pos]; -} - -char NzString::operator[](unsigned int pos) const -{ - #if NAZARA_CORE_SAFE - if (pos >= m_sharedString->size) - { - NazaraError("Index out of range (" + Number(pos) + " >= " + Number(m_sharedString->size) + ')'); - return 0; - } - #endif - - return m_sharedString->string[pos]; -} - -NzString& NzString::operator=(char character) -{ - if (character != '\0') - { - if (m_sharedString->capacity >= 1) - EnsureOwnership(); - else - { - ReleaseString(); - m_sharedString = new SharedString; - m_sharedString->capacity = 1; - m_sharedString->string = new char[2]; - } - - m_sharedString->size = 1; - m_sharedString->string[0] = character; - m_sharedString->string[1] = '\0'; - } - else - ReleaseString(); - - return *this; -} - -NzString& NzString::operator=(const char* string) -{ - if (string && string[0] != '\0') - { - unsigned int size = std::strlen(string); - if (m_sharedString->capacity >= size) - EnsureOwnership(); - else - { - ReleaseString(); - - m_sharedString = new SharedString; - m_sharedString->capacity = size; - m_sharedString->string = new char[size+1]; - } - - m_sharedString->size = size; - std::strcpy(m_sharedString->string, string); - } - else - ReleaseString(); - - return *this; -} - -NzString& NzString::operator=(const std::string& string) -{ - if (string.size() > 0) - { - if (m_sharedString->capacity >= string.size()) - EnsureOwnership(); - else - { - ReleaseString(); - - m_sharedString = new SharedString; - m_sharedString->capacity = string.size(); - m_sharedString->string = new char[string.size()+1]; - } - - m_sharedString->size = string.size(); - std::strcpy(m_sharedString->string, string.c_str()); - } - else - ReleaseString(); - - return *this; -} - -NzString& NzString::operator=(const NzString& string) -{ - ReleaseString(); - - m_sharedString = string.m_sharedString; - if (m_sharedString != &emptyString) - { - NazaraMutexLock(m_sharedString->mutex); - m_sharedString->refCount++; - NazaraMutexUnlock(m_sharedString->mutex); - } - - return *this; -} - -NzString& NzString::operator=(NzString&& string) noexcept -{ - std::swap(m_sharedString, string.m_sharedString); - - return *this; -} - -NzString NzString::operator+(char character) const -{ - if (character == '\0') - return *this; - - unsigned int totalSize = m_sharedString->size+1; - char* str = new char[totalSize+1]; - std::memcpy(str, m_sharedString->string, m_sharedString->size*sizeof(char)); - - str[m_sharedString->size] = character; - str[totalSize] = '\0'; - - return NzString(new SharedString(1, totalSize, totalSize, str)); -} - -NzString NzString::operator+(const char* string) const -{ - if (!string || !string[0]) - return *this; - - if (m_sharedString->size == 0) - return string; - - unsigned int length = std::strlen(string); - if (length == 0) - return *this; - - unsigned int totalSize = m_sharedString->size + length; - char* str = new char[totalSize+1]; - std::memcpy(str, m_sharedString->string, m_sharedString->size*sizeof(char)); - std::strcpy(&str[m_sharedString->size], string); - - return NzString(new SharedString(1, totalSize, totalSize, str)); -} - -NzString NzString::operator+(const std::string& string) const -{ - if (string.empty()) - return *this; - - if (m_sharedString->size == 0) - return string; - - unsigned int totalSize = m_sharedString->size + string.size(); - char* str = new char[totalSize+1]; - std::memcpy(str, m_sharedString->string, m_sharedString->size*sizeof(char)); - std::strcpy(&str[m_sharedString->size], string.c_str()); - - return NzString(new SharedString(1, totalSize, totalSize, str)); -} - -NzString NzString::operator+(const NzString& string) const -{ - if (string.m_sharedString->size == 0) - return *this; - - if (m_sharedString->size == 0) - return string; - - unsigned int totalSize = m_sharedString->size + string.m_sharedString->size; - char* str = new char[totalSize+1]; - std::memcpy(str, m_sharedString->string, m_sharedString->size*sizeof(char)); - std::strcpy(&str[m_sharedString->size], string.m_sharedString->string); - - return NzString(new SharedString(1, totalSize, totalSize, str)); -} - -NzString& NzString::operator+=(char character) -{ - if (character == '\0') - return *this; - - if (m_sharedString->size == 0) - return operator=(character); - - if (m_sharedString->capacity > m_sharedString->size) - { - EnsureOwnership(); - - m_sharedString->string[m_sharedString->size] = character; - m_sharedString->string[m_sharedString->size+1] = '\0'; - m_sharedString->size++; - } - else - { - unsigned int newSize = m_sharedString->size+1; - unsigned int bufferSize = nzGetNewSize(newSize); - - char* str = new char[bufferSize+1]; - std::memcpy(str, m_sharedString->string, m_sharedString->size*sizeof(char)); - str[m_sharedString->size] = character; - str[newSize] = '\0'; - - ReleaseString(); - m_sharedString = new SharedString; - m_sharedString->capacity = bufferSize; - m_sharedString->size = newSize; - m_sharedString->string = str; - } - - return *this; -} - -NzString& NzString::operator+=(const char* string) -{ - if (!string || !string[0]) - return *this; - - if (m_sharedString->size == 0) - return operator=(string); - - unsigned int length = std::strlen(string); - if (length == 0) - return *this; - - if (m_sharedString->capacity >= m_sharedString->size + length) - { - EnsureOwnership(); - - std::strcpy(&m_sharedString->string[m_sharedString->size], string); - m_sharedString->size += length; - } - else - { - unsigned int newSize = m_sharedString->size + length; - unsigned int bufferSize = nzGetNewSize(newSize); - - char* str = new char[bufferSize+1]; - std::memcpy(str, m_sharedString->string, m_sharedString->size*sizeof(char)); - std::strcpy(&str[m_sharedString->size], string); - - ReleaseString(); - m_sharedString = new SharedString; - m_sharedString->capacity = bufferSize; - m_sharedString->size = newSize; - m_sharedString->string = str; - } - - return *this; -} - -NzString& NzString::operator+=(const std::string& string) -{ - if (string.empty()) - return *this; - - if (m_sharedString->size == 0) - return operator=(string); - - if (m_sharedString->capacity >= m_sharedString->size + string.size()) - { - EnsureOwnership(); - - std::strcpy(&m_sharedString->string[m_sharedString->size], string.c_str()); - m_sharedString->size += string.size(); - } - else - { - unsigned int newSize = m_sharedString->size + string.size(); - unsigned int bufferSize = nzGetNewSize(newSize); - - char* str = new char[bufferSize+1]; - std::memcpy(str, m_sharedString->string, m_sharedString->size*sizeof(char)); - std::strcpy(&str[m_sharedString->size], string.c_str()); - - ReleaseString(); - m_sharedString = new SharedString; - m_sharedString->capacity = bufferSize; - m_sharedString->size = newSize; - m_sharedString->string = str; - } - - return *this; -} - -NzString& NzString::operator+=(const NzString& string) -{ - if (string.m_sharedString->size == 0) - return *this; - - if (m_sharedString->size == 0) - return operator=(string); - - if (m_sharedString->capacity >= m_sharedString->size + string.m_sharedString->size) - { - EnsureOwnership(); - - std::strcpy(&m_sharedString->string[m_sharedString->size], string.m_sharedString->string); - m_sharedString->size += string.m_sharedString->size; - } - else - { - unsigned int newSize = m_sharedString->size + string.m_sharedString->size; - unsigned int bufferSize = nzGetNewSize(newSize); - - char* str = new char[bufferSize+1]; - std::memcpy(str, m_sharedString->string, m_sharedString->size*sizeof(char)); - std::strcpy(&str[m_sharedString->size], string.m_sharedString->string); - - ReleaseString(); - m_sharedString = new SharedString; - m_sharedString->capacity = bufferSize; - m_sharedString->size = newSize; - m_sharedString->string = str; - } - - return *this; -} - -bool NzString::operator==(char character) const -{ - if (m_sharedString->size == 0) - return character == '\0'; - - if (m_sharedString->size > 1) - return false; - - return m_sharedString->string[0] == character; -} - -bool NzString::operator==(const char* string) const -{ - if (m_sharedString->size == 0) - return !string || !string[0]; - - if (!string || !string[0]) - return false; - - return std::strcmp(m_sharedString->string, string) == 0; -} - -bool NzString::operator==(const std::string& string) const -{ - if (m_sharedString->size == 0 || string.empty()) - return m_sharedString->size == string.size(); - - if (m_sharedString->size != string.size()) - return false; - - return std::strcmp(m_sharedString->string, string.c_str()) == 0; -} - -bool NzString::operator!=(char character) const -{ - if (m_sharedString->size == 0) - return character != '\0'; - - if (character == '\0' || m_sharedString->size != 1) - return true; - - if (m_sharedString->size != 1) - return true; - - return m_sharedString->string[0] != character; -} - -bool NzString::operator!=(const char* string) const -{ - if (m_sharedString->size == 0) - return string && string[0]; - - if (!string || !string[0]) - return true; - - return std::strcmp(m_sharedString->string, string) != 0; -} - -bool NzString::operator!=(const std::string& string) const -{ - if (m_sharedString->size == 0 || string.empty()) - return m_sharedString->size == string.size(); - - if (m_sharedString->size != string.size()) - return false; - - return std::strcmp(m_sharedString->string, string.c_str()) != 0; -} - -bool NzString::operator<(char character) const -{ - if (character == '\0') - return false; - - if (m_sharedString->size == 0) - return true; - - return m_sharedString->string[0] < character; -} - -bool NzString::operator<(const char* string) const -{ - if (!string || !string[0]) - return false; - - if (m_sharedString->size == 0) - return true; - - return std::strcmp(m_sharedString->string, string) < 0; -} - -bool NzString::operator<(const std::string& string) const -{ - if (string.empty()) - return false; - - if (m_sharedString->size == 0) - return true; - - return std::strcmp(m_sharedString->string, string.c_str()) < 0; -} - -bool NzString::operator<=(char character) const -{ - if (m_sharedString->size == 0) - return true; - - if (character == '\0') - return false; - - return m_sharedString->string[0] < character || (m_sharedString->string[0] == character && m_sharedString->size == 1); -} - -bool NzString::operator<=(const char* string) const -{ - if (m_sharedString->size == 0) - return true; - - if (!string || !string[0]) - return false; - - return std::strcmp(m_sharedString->string, string) <= 0; -} - -bool NzString::operator<=(const std::string& string) const -{ - if (m_sharedString->size == 0) - return true; - - if (string.empty()) - return false; - - return std::strcmp(m_sharedString->string, string.c_str()) <= 0; -} - -bool NzString::operator>(char character) const -{ - if (m_sharedString->size == 0) - return false; - - if (character == '\0') - return true; - - return m_sharedString->string[0] > character; -} - -bool NzString::operator>(const char* string) const -{ - if (m_sharedString->size == 0) - return false; - - if (!string || !string[0]) - return true; - - return std::strcmp(m_sharedString->string, string) > 0; -} - -bool NzString::operator>(const std::string& string) const -{ - if (m_sharedString->size == 0) - return false; - - if (string.empty()) - return true; - - return std::strcmp(m_sharedString->string, string.c_str()) > 0; -} - -bool NzString::operator>=(char character) const -{ - if (character == '\0') - return true; - - if (m_sharedString->size == 0) - return false; - - return m_sharedString->string[0] > character || (m_sharedString->string[0] == character && m_sharedString->size == 1); -} - -bool NzString::operator>=(const char* string) const -{ - if (!string || !string[0]) - return true; - - if (m_sharedString->size == 0) - return false; - - return std::strcmp(m_sharedString->string, string) >= 0; -} - -bool NzString::operator>=(const std::string& string) const -{ - if (string.empty()) - return true; - - if (m_sharedString->size == 0) - return false; - - return std::strcmp(m_sharedString->string, string.c_str()) >= 0; -} - -NzString NzString::Boolean(bool boolean) -{ - unsigned int size = (boolean) ? 4 : 5; - char* str = new char[size+1]; - std::strcpy(str, (boolean) ? "true" : "false"); - - return NzString(new SharedString(1, size, size, str)); -} - -int NzString::Compare(const NzString& first, const NzString& second) -{ - if (first.m_sharedString->size == 0) - return (second.m_sharedString->size == 0) ? 0 : -1; - - if (second.m_sharedString->size == 0) - return 1; - - return std::strcmp(first.m_sharedString->string, second.m_sharedString->string); -} - -NzString NzString::Number(float number) -{ - std::ostringstream oss; - oss.precision(NAZARA_CORE_REAL_PRECISION); - oss << number; - - return NzString(oss.str()); -} - -NzString NzString::Number(double number) -{ - std::ostringstream oss; - oss.precision(NAZARA_CORE_REAL_PRECISION); - oss << number; - - return NzString(oss.str()); -} - -NzString NzString::Number(long double number) -{ - std::ostringstream oss; - oss.precision(NAZARA_CORE_REAL_PRECISION); - oss << number; - - return NzString(oss.str()); -} - -NzString NzString::Number(signed char number, nzUInt8 radix) -{ - return NzNumberToString(number, radix); -} - -NzString NzString::Number(unsigned char number, nzUInt8 radix) -{ - return NzNumberToString(number, radix); -} - -NzString NzString::Number(short number, nzUInt8 radix) -{ - return NzNumberToString(number, radix); -} - -NzString NzString::Number(unsigned short number, nzUInt8 radix) -{ - return NzNumberToString(number, radix); -} - -NzString NzString::Number(int number, nzUInt8 radix) -{ - return NzNumberToString(number, radix); -} - -NzString NzString::Number(unsigned int number, nzUInt8 radix) -{ - return NzNumberToString(number, radix); -} - -NzString NzString::Number(long number, nzUInt8 radix) -{ - return NzNumberToString(number, radix); -} - -NzString NzString::Number(unsigned long number, nzUInt8 radix) -{ - return NzNumberToString(number, radix); -} - -NzString NzString::Number(long long number, nzUInt8 radix) -{ - return NzNumberToString(number, radix); -} - -NzString NzString::Number(unsigned long long number, nzUInt8 radix) -{ - return NzNumberToString(number, radix); -} - -NzString NzString::Pointer(const void* ptr) -{ - unsigned int size = sizeof(ptr)*2+2; - char* str = new char[size+1]; - std::sprintf(str, "0x%p", ptr); - - return NzString(new SharedString(1, size, size, str)); -} - -NzString NzString::Unicode(char32_t character) -{ - if (character == '\0') - return NzString(); - - unsigned int count = 0; - if (character < 0x80) - count = 1; - else if (character < 0x800) - count = 2; - else if (character < 0x10000) - count = 3; - else - count = 4; - - char* str = new char[count+1]; - utf8::append(character, str); - str[count] = '\0'; - - return NzString(new SharedString(1, count, count, str)); -} - -NzString NzString::Unicode(const char* u8String) -{ - return NzString(u8String); -} - -NzString NzString::Unicode(const char16_t* u16String) -{ - if (!u16String || !u16String[0]) - return NzString(); - - const char16_t* ptr = u16String; - unsigned int count = 0; - do - count++; - while (*++ptr); - - count *= 2; // On s'assure d'avoir la place suffisante - - char* str = new char[count+1]; - char* r = utf8::utf16to8(u16String, ptr, str); - *r = '\0'; - - return NzString(new SharedString(1, count, r-str, str)); -} - -NzString NzString::Unicode(const char32_t* u32String) -{ - if (!u32String || !u32String[0]) - return NzString(); - - const char32_t* ptr = u32String; - unsigned int count = 0; - do - { - char32_t cp = *ptr; - if (cp < 0x80) - count += 1; - else if (cp < 0x800) - count += 2; - else if (cp < 0x10000) - count += 3; - else - count += 4; - } - while (*++ptr); - - char* str = new char[count+1]; - char* r = utf8::utf32to8(u32String, ptr, str); - *r = '\0'; - - return NzString(new SharedString(1, count, count, str)); -} - -NzString NzString::Unicode(const wchar_t* wString) -{ - if (!wString || !wString[0]) - return NzString(); - - const wchar_t* ptr = wString; - unsigned int count = 0; - do - { - char32_t cp = *ptr; - if (cp < 0x80) - count += 1; - else if (cp < 0x800) - count += 2; - else if (cp < 0x10000) - count += 3; - else - count += 4; - } - while (*++ptr); - - char* str = new char[count+1]; - char* r = utf8::utf32to8(wString, ptr, str); - *r = '\0'; - - return NzString(new SharedString(1, count, count, str)); -} - -std::istream& operator>>(std::istream& is, NzString& str) -{ - str.Clear(); - - char c; - do - { - is.get(c); - if (c == '\0') - break; - else if (std::isspace(c)) - { - if (str.IsNull()) - continue; - else - break; - } - else - str += c; - } - while (true); - - return is; -} - -std::ostream& operator<<(std::ostream& os, const NzString& str) -{ - if (str.IsEmpty()) - return os; - - return operator<<(os, str.m_sharedString->string); -} - -NzString operator+(char character, const NzString& string) -{ - if (character == '\0') - return string; - - if (string.IsEmpty()) - return character; - - unsigned int totalSize = string.m_sharedString->size+1; - char* str = new char[totalSize+1]; - str[0] = character; - std::strcpy(str, string.m_sharedString->string); - - return NzString(new NzString::SharedString(1, totalSize, totalSize, str)); -} - -NzString operator+(const char* string, const NzString& nstring) -{ - if (!string || !string[0]) - return nstring; - - if (nstring.IsEmpty()) - return string; - - unsigned int size = std::strlen(string); - unsigned int totalSize = size + nstring.m_sharedString->size; - char* str = new char[totalSize+1]; - std::memcpy(str, string, size*sizeof(char)); - std::strcpy(&str[size], nstring.m_sharedString->string); - - return NzString(new NzString::SharedString(1, totalSize, totalSize, str)); -} - -NzString operator+(const std::string& string, const NzString& nstring) -{ - if (string.empty()) - return nstring; - - if (nstring.m_sharedString->size == 0) - return string; - - unsigned int totalSize = string.size() + nstring.m_sharedString->size; - char* str = new char[totalSize+1]; - std::memcpy(str, string.c_str(), string.size()*sizeof(char)); - std::strcpy(&str[string.size()], nstring.m_sharedString->string); - - return NzString(new NzString::SharedString(1, totalSize, totalSize, str)); -} - -bool operator==(const NzString& first, const NzString& second) -{ - if (first.m_sharedString->size == 0 || second.m_sharedString->size == 0) - return first.m_sharedString->size == second.m_sharedString->size; - - if (first.m_sharedString->size != second.m_sharedString->size) - return false; - - return std::strcmp(first.m_sharedString->string, second.m_sharedString->string) == 0; -} - -bool operator!=(const NzString& first, const NzString& second) -{ - return !operator==(first, second); -} - -bool operator<(const NzString& first, const NzString& second) -{ - if (second.m_sharedString->size == 0) - return false; - - if (first.m_sharedString->size == 0) - return true; - - return std::strcmp(first.m_sharedString->string, second.m_sharedString->string) < 0; -} - -bool operator<=(const NzString& first, const NzString& second) -{ - return !operator<(second, first); -} - -bool operator>(const NzString& first, const NzString& second) -{ - return second < first; -} - -bool operator>=(const NzString& first, const NzString& second) -{ - return !operator<(first, second); -} - -bool operator==(char character, const NzString& nstring) -{ - return nstring == character; -} - -bool operator==(const char* string, const NzString& nstring) -{ - return nstring == string; -} - -bool operator==(const std::string& string, const NzString& nstring) -{ - return nstring == string; -} - -bool operator!=(char character, const NzString& nstring) -{ - return !operator==(character, nstring); -} - -bool operator!=(const char* string, const NzString& nstring) -{ - return !operator==(string, nstring); -} - -bool operator!=(const std::string& string, const NzString& nstring) -{ - return !operator==(string, nstring); -} - -bool operator<(char character, const NzString& nstring) -{ - return nstring > character; -} - -bool operator<(const char* string, const NzString& nstring) -{ - return nstring > string; -} - -bool operator<(const std::string& string, const NzString& nstring) -{ - return nstring > string; -} - -bool operator<=(char character, const NzString& nstring) -{ - return !operator<(nstring, character); -} - -bool operator<=(const char* string, const NzString& nstring) -{ - return !operator<(nstring, string); -} - -bool operator<=(const std::string& string, const NzString& nstring) -{ - return !operator<(nstring, string); -} - -bool operator>(char character, const NzString& nstring) -{ - return nstring < character; -} - -bool operator>(const char* string, const NzString& nstring) -{ - return nstring < string; -} - -bool operator>(const std::string& string, const NzString& nstring) -{ - return nstring < string; -} - -bool operator>=(char character, const NzString& nstring) -{ - return !operator<(character, nstring); -} - -bool operator>=(const char* string, const NzString& nstring) -{ - return !operator<(string, nstring); -} - -bool operator>=(const std::string& string, const NzString& nstring) -{ - return !operator<(string, nstring); -} - -void NzString::EnsureOwnership() -{ - if (m_sharedString == &emptyString) - return; - - NazaraLock(m_sharedString->mutex); - if (m_sharedString->refCount > 1) - { - m_sharedString->refCount--; - - char* string = new char[m_sharedString->capacity+1]; - std::strcpy(string, m_sharedString->string); - - m_sharedString = new SharedString(1, m_sharedString->capacity, m_sharedString->size, string); - } -} - -bool NzString::FillHash(NzHashImpl* hazh) const -{ - hazh->Append(reinterpret_cast(m_sharedString->string), m_sharedString->size); - - return true; -} - -void NzString::ReleaseString() -{ - if (m_sharedString == &emptyString) - return; - - NazaraMutexLock(m_sharedString->mutex); - bool freeSharedString = (--m_sharedString->refCount == 0); - NazaraMutexUnlock(m_sharedString->mutex); - - if (freeSharedString) - { - delete[] m_sharedString->string; - delete m_sharedString; - } - - m_sharedString = &emptyString; -} - -NzString::SharedString NzString::emptyString(0, 0, 0, nullptr); -unsigned int NzString::npos(static_cast(-1)); - -namespace std -{ - istream& getline(istream& is, NzString& str) - { - str.Clear(); - - char c; - do - { - is.get(c); - if (c != '\n' && c != '\0') - str += c; - else - break; - } - while (true); - - return is; - } - - istream& getline(istream& is, NzString& str, char delim) - { - str.Clear(); - - char c; - do - { - is.get(c); - if (c != delim && c != '\0') - str += c; - else - break; - } - while (true); - - return is; - } - - void swap(NzString& lhs, NzString& rhs) - { - lhs.Swap(rhs); - } -} +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +inline unsigned int nzPow2(unsigned int n) +{ + unsigned int x = 1; + + // Tant que x est plus petit que n, on décale ses bits vers la gauche, ce qui revient à multiplier par deux + while(x <= n) + x <<= 1; + + return x; +} +// Cet algorithme est inspiré de la documentation de Qt +inline unsigned int nzGetNewSize(unsigned int newSize) +{ + if (newSize < 20) + return newSize+4; + else + { + if (newSize < (1 << 12)-12) + return nzPow2(newSize << 1)-12; + else + return newSize + (1 << 11); + } +} + +inline char nzToLower(char character) +{ + if (character >= 'A' && character <= 'Z') + return character + ('a' - 'A'); + else + return character; +} + +inline char nzToUpper(char character) +{ + if (character >= 'a' && character <= 'z') + return character + ('A' - 'a'); + else + return character; +} + +inline int nzStrcasecmp(const char* s1, const char* s2) +{ + int ret = 0; + + while (!(ret = static_cast(nzToLower(*s1)) - static_cast(nzToLower(*s2))) && *s2) + ++s1, ++s2; + + return ret != 0 ? (ret > 0 ? 1 : -1) : 0; +} + +inline int nzUnicodecasecmp(const char* s1, const char* s2) +{ + int ret = 0; + utf8::unchecked::iterator it1(s1); + utf8::unchecked::iterator it2(s2); + + while (!(ret = NzUnicode::GetLowercase(*it1) - NzUnicode::GetLowercase(*it2)) && *it2) + ++it1, ++it2; + + return ret != 0 ? (ret > 0 ? 1 : -1) : 0; +} + +NzString::NzString() : +m_sharedString(&emptyString) +{ +} + +NzString::NzString(char character) +{ + if (character == '\0') + m_sharedString = &emptyString; + else + { + m_sharedString = new SharedString; + m_sharedString->capacity = 1; + m_sharedString->size = 1; + m_sharedString->string = new char[2]; + m_sharedString->string[0] = character; + m_sharedString->string[1] = '\0'; + } +} + +NzString::NzString(const char* string) +{ + if (string) + { + unsigned int size = std::strlen(string); + if (size > 0) + { + m_sharedString = new SharedString; + m_sharedString->capacity = size; + m_sharedString->size = size; + m_sharedString->string = new char[size+1]; + std::memcpy(m_sharedString->string, string, size+1); + } + else + m_sharedString = &emptyString; + } + else + m_sharedString = &emptyString; +} + +NzString::NzString(const std::string& string) +{ + if (string.size() > 0) + { + m_sharedString = new SharedString; + m_sharedString->capacity = string.capacity(); + m_sharedString->size = string.size(); + m_sharedString->string = new char[string.capacity()+1]; + std::memcpy(m_sharedString->string, string.c_str(), string.size()+1); + } + else + m_sharedString = &emptyString; +} + +NzString::NzString(const NzString& string) : +m_sharedString(string.m_sharedString) +{ + if (m_sharedString != &emptyString) + { + NazaraMutexLock(m_sharedString->mutex); + m_sharedString->refCount++; + NazaraMutexUnlock(m_sharedString->mutex); + } +} + +NzString::NzString(NzString&& string) noexcept : +m_sharedString(string.m_sharedString) +{ + string.m_sharedString = &emptyString; +} + +NzString::NzString(SharedString* sharedString) : +m_sharedString(sharedString) +{ +} + +NzString::~NzString() +{ + ReleaseString(); +} + +NzString& NzString::Append(char character) +{ + if (character == '\0') + return *this; + + if (m_sharedString->size == 0 && m_sharedString->capacity == 0) + return operator=(character); + + if (m_sharedString->capacity > m_sharedString->size) + { + EnsureOwnership(); + + m_sharedString->string[m_sharedString->size] = character; + m_sharedString->string[m_sharedString->size+1] = '\0'; + m_sharedString->size++; + } + else + { + unsigned int newSize = m_sharedString->size+1; + unsigned int bufferSize = nzGetNewSize(newSize); + + char* str = new char[bufferSize+1]; + std::memcpy(str, m_sharedString->string, m_sharedString->size); + str[m_sharedString->size] = character; + str[newSize] = '\0'; + + ReleaseString(); + m_sharedString = new SharedString; + m_sharedString->capacity = bufferSize; + m_sharedString->size = newSize; + m_sharedString->string = str; + } + + return *this; +} + +NzString& NzString::Append(const char* string) +{ + if (!string || !string[0]) + return *this; + + unsigned int length = std::strlen(string); + if (length == 0) + return *this; + + if (m_sharedString->capacity >= m_sharedString->size + length) + { + EnsureOwnership(); + + std::memcpy(&m_sharedString->string[m_sharedString->size], string, length+1); + m_sharedString->size += length; + } + else + { + unsigned int newSize = m_sharedString->size + length; + unsigned int bufferSize = nzGetNewSize(newSize); + + char* str = new char[bufferSize+1]; + std::memcpy(str, m_sharedString->string, m_sharedString->size); + std::memcpy(&str[m_sharedString->size], string, length+1); + + ReleaseString(); + m_sharedString = new SharedString; + m_sharedString->capacity = bufferSize; + m_sharedString->size = newSize; + m_sharedString->string = str; + } + + return *this; +} + +NzString& NzString::Append(const NzString& string) +{ + if (string.m_sharedString->size == 0) + return *this; + + if (m_sharedString->size == 0 && m_sharedString->capacity < string.m_sharedString->size) + return operator=(string); + + if (m_sharedString->capacity >= m_sharedString->size + string.m_sharedString->size) + { + EnsureOwnership(); + + std::memcpy(&m_sharedString->string[m_sharedString->size], string.m_sharedString->string, string.m_sharedString->size+1); + m_sharedString->size += string.m_sharedString->size; + } + else + { + unsigned int newSize = m_sharedString->size + string.m_sharedString->size; + unsigned int bufferSize = nzGetNewSize(newSize); + + char* str = new char[bufferSize+1]; + std::memcpy(str, m_sharedString->string, m_sharedString->size); + std::memcpy(&str[m_sharedString->size], string.m_sharedString->string, string.m_sharedString->size+1); + + ReleaseString(); + m_sharedString = new SharedString; + m_sharedString->capacity = bufferSize; + m_sharedString->size = newSize; + m_sharedString->string = str; + } + + return *this; +} + +void NzString::Clear(bool keepBuffer) +{ + if (keepBuffer) + { + EnsureOwnership(); + m_sharedString->size = 0; + } + else + ReleaseString(); +} + +bool NzString::Contains(char character, int start, nzUInt32 flags) const +{ + return Find(character, start, flags) != npos; +} + +bool NzString::Contains(const char* string, int start, nzUInt32 flags) const +{ + return Find(string, start, flags) != npos; +} + +bool NzString::Contains(const NzString& string, int start, nzUInt32 flags) const +{ + return Find(string, start, flags) != npos; +} + +unsigned int NzString::Count(char character, int start, nzUInt32 flags) const +{ + if (character == '\0' || m_sharedString->size == 0) + return 0; + + if (start < 0) + start = std::max(static_cast(m_sharedString->size + start), 0); + + unsigned int pos = static_cast(start); + if (pos >= m_sharedString->size) + return 0; + + char* str = &m_sharedString->string[pos]; + unsigned int count = 0; + if (flags & CaseInsensitive) + { + char character_lower = nzToLower(character); + char character_upper = nzToUpper(character); + do + { + if (*str == character_lower || *str == character_upper) + count++; + } + while (*++str); + } + else + { + while ((str = std::strchr(str, character))) + { + count++; + str++; + } + } + + return count; +} + +unsigned int NzString::Count(const char* string, int start, nzUInt32 flags) const +{ + if (!string || !string[0] || m_sharedString->size == 0) + return 0; + + if (start < 0) + start = std::max(static_cast(m_sharedString->size + start), 0); + + unsigned int pos = static_cast(start); + if (pos >= m_sharedString->size) + return 0; + + char* str = &m_sharedString->string[pos]; + unsigned int count = 0; + if (flags & CaseInsensitive) + { + if (flags & HandleUtf8) + { + while (utf8::internal::is_trail(*str)) + str++; + + utf8::unchecked::iterator it(str); + + const char* t = string; + char32_t c = NzUnicode::GetLowercase(utf8::unchecked::next(t)); + do + { + if (NzUnicode::GetLowercase(*it) == c) + { + ++it; + + utf8::unchecked::iterator it2(t); + while (true) + { + if (*it2 == '\0') + { + count++; + break; + } + + if (*it == '\0') + return count; + + if (NzUnicode::GetLowercase(*it) != NzUnicode::GetLowercase(*it2)) + break; + + ++it; + ++it2; + } + } + } + while (*++it); + } + else + { + char c = nzToLower(string[0]); + do + { + if (nzToLower(*str) == c) + { + str++; + + const char* ptr = &string[1]; + while (true) + { + if (*ptr == '\0') + { + count++; + break; + } + + if (*str == '\0') + return count; + + if (nzToLower(*str) != nzToLower(*ptr)) + break; + + ptr++; + str++; + } + } + } + while (*++str); + } + } + else + { + while ((str = std::strstr(str, string))) + { + count++; + str++; + } + } + + return count; +} + +unsigned int NzString::Count(const NzString& string, int start, nzUInt32 flags) const +{ + if (string.m_sharedString->size == 0 || string.m_sharedString->size > m_sharedString->size) + return 0; + + if (start < 0) + start = std::max(static_cast(m_sharedString->size + start), 0); + + unsigned int pos = static_cast(start); + if (pos >= m_sharedString->size) + return 0; + + char* str = &m_sharedString->string[pos]; + unsigned int count = 0; + if (flags & CaseInsensitive) + { + if (flags & HandleUtf8) + { + while (utf8::internal::is_trail(*str)) + str++; + + utf8::unchecked::iterator it(str); + + const char* t = string.m_sharedString->string; + char32_t c = NzUnicode::GetLowercase(utf8::unchecked::next(t)); + do + { + if (NzUnicode::GetLowercase(*it) == c) + { + ++it; + + utf8::unchecked::iterator it2(t); + while (true) + { + if (*it2 == '\0') + { + count++; + break; + } + + if (*it == '\0') + return count; + + if (NzUnicode::GetLowercase(*it) != NzUnicode::GetLowercase(*it2)) + break; + + ++it; + ++it2; + } + } + } + while (*++it); + } + else + { + char c = nzToLower(string.m_sharedString->string[0]); + do + { + if (nzToLower(*str) == c) + { + str++; + + const char* ptr = &string.m_sharedString->string[1]; + while (true) + { + if (*ptr == '\0') + { + count++; + break; + } + + if (*str == '\0') + return count; + + if (nzToLower(*str) != nzToLower(*ptr)) + break; + + ptr++; + str++; + } + } + } + while (*++str); + } + } + else + { + while ((str = std::strstr(str, string.m_sharedString->string))) + { + count++; + str++; + } + } + + return count; +} + +unsigned int NzString::CountAny(const char* string, int start, nzUInt32 flags) const +{ + if (!string || !string[0] || m_sharedString->size == 0) + return 0; + + if (start < 0) + start = std::max(static_cast(m_sharedString->size + start), 0); + + unsigned int pos = static_cast(start); + if (pos >= m_sharedString->size) + return 0; + + char* str = &m_sharedString->string[pos]; + unsigned int count = 0; + if (flags & HandleUtf8) + { + while (utf8::internal::is_trail(*str)) + str++; + + utf8::unchecked::iterator it(str); + + if (flags & CaseInsensitive) + { + do + { + utf8::unchecked::iterator it2(string); + do + { + if (NzUnicode::GetLowercase(*it) == NzUnicode::GetLowercase(*it2)) + { + count++; + break; + } + } + while (*++it2); + } + while (*++str); + } + else + { + do + { + utf8::unchecked::iterator it2(string); + do + { + if (*it == *it2) + { + count++; + break; + } + } + while (*++it2); + } + while (*++str); + } + } + else + { + if (flags & CaseInsensitive) + { + do + { + const char* c = string; + do + { + if (nzToLower(*str) == nzToLower(*c)) + { + count++; + break; + } + } + while (*++c); + } + while (*++str); + } + else + { + while ((str = std::strpbrk(str, string))) + { + count++; + str++; + } + } + } + + return count; +} + +unsigned int NzString::CountAny(const NzString& string, int start, nzUInt32 flags) const +{ + if (string.m_sharedString->size == 0 || m_sharedString->size == 0) + return 0; + + if (start < 0) + start = std::max(static_cast(m_sharedString->size + start), 0); + + unsigned int pos = static_cast(start); + if (pos >= m_sharedString->size) + return 0; + + char* str = &m_sharedString->string[pos]; + unsigned int count = 0; + if (flags & HandleUtf8) + { + while (utf8::internal::is_trail(*str)) + str++; + + utf8::unchecked::iterator it(str); + + if (flags & CaseInsensitive) + { + do + { + utf8::unchecked::iterator it2(string.m_sharedString->string); + do + { + if (NzUnicode::GetLowercase(*it) == NzUnicode::GetLowercase(*it2)) + { + count++; + break; + } + } + while (*++it2); + } + while (*++str); + } + else + { + do + { + utf8::unchecked::iterator it2(string.m_sharedString->string); + do + { + if (*it == *it2) + { + count++; + break; + } + } + while (*++it2); + } + while (*++str); + } + } + else + { + if (flags & CaseInsensitive) + { + do + { + const char* c = string.m_sharedString->string; + do + { + if (nzToLower(*str) == nzToLower(*c)) + { + count++; + break; + } + } + while (*++c); + } + while (*++str); + } + else + { + while ((str = std::strpbrk(str, string.m_sharedString->string))) + { + count++; + str++; + } + } + } + + return count; +} + +bool NzString::EndsWith(char character, nzUInt32 flags) const +{ + if (m_sharedString->size == 0) + return 0; + + if (flags & CaseInsensitive) + return nzToLower(m_sharedString->string[m_sharedString->size-1]) == nzToLower(character); + else + return m_sharedString->string[m_sharedString->size-1] == character; // character == '\0' sera toujours faux +} + +bool NzString::EndsWith(const char* string, nzUInt32 flags) const +{ + if (!string || !string[0] || m_sharedString->size == 0) + return false; + + unsigned int len = std::strlen(string); + if (len > m_sharedString->size) + return false; + + if (flags & CaseInsensitive) + { + if (flags & HandleUtf8) + return nzUnicodecasecmp(&m_sharedString->string[m_sharedString->size - len], string) == 0; + else + return nzStrcasecmp(&m_sharedString->string[m_sharedString->size - len], string) == 0; + } + else + return std::strcmp(&m_sharedString->string[m_sharedString->size - len], string) == 0; +} + +bool NzString::EndsWith(const NzString& string, nzUInt32 flags) const +{ + if (string.m_sharedString->size == 0 || string.m_sharedString->size > m_sharedString->size) + return false; + + if (flags & CaseInsensitive) + { + if (flags & HandleUtf8) + return nzUnicodecasecmp(&m_sharedString->string[m_sharedString->size - string.m_sharedString->size], string.m_sharedString->string) == 0; + else + return nzStrcasecmp(&m_sharedString->string[m_sharedString->size - string.m_sharedString->size], string.m_sharedString->string) == 0; + } + else + return std::strcmp(&m_sharedString->string[m_sharedString->size - string.m_sharedString->size], string.m_sharedString->string) == 0; +} + +unsigned int NzString::Find(char character, int start, nzUInt32 flags) const +{ + if (character == '\0' || m_sharedString->size == 0) + return npos; + + if (start < 0) + start = std::max(static_cast(m_sharedString->size + start), 0); + + unsigned int pos = static_cast(start); + if (pos >= m_sharedString->size) + return npos; + + if (flags & CaseInsensitive) + { + char ch = nzToLower(character); + const char* str = m_sharedString->string; + do + { + if (nzToLower(*str) == ch) + return static_cast(str - m_sharedString->string); + } + while (*++str); + + return npos; + } + else + { + char* ch = std::strchr(&m_sharedString->string[pos], character); + if (ch) + return static_cast(ch - m_sharedString->string); + else + return npos; + } +} + +unsigned int NzString::Find(const char* string, int start, nzUInt32 flags) const +{ + if (!string || !string[0] || m_sharedString->size == 0) + return npos; + + if (start < 0) + start = std::max(static_cast(m_sharedString->size + start), 0); + + unsigned int pos = static_cast(start); + if (pos >= m_sharedString->size) + return npos; + + char* str = &m_sharedString->string[pos]; + if (flags & CaseInsensitive) + { + if (flags & HandleUtf8) + { + while (utf8::internal::is_trail(*str)) + str++; + + utf8::unchecked::iterator it(str); + + const char* t = string; + char32_t c = NzUnicode::GetLowercase(utf8::unchecked::next(t)); + do + { + if (NzUnicode::GetLowercase(*it) == c) + { + const char* ptrPos = it.base(); + ++it; + + utf8::unchecked::iterator it2(t); + while (true) + { + if (*it2 == '\0') + return static_cast(ptrPos - m_sharedString->string); + + if (*it == '\0') + return npos; + + if (NzUnicode::GetLowercase(*it) != NzUnicode::GetLowercase(*it2)) + break; + + ++it; + ++it2; + } + } + } + while (*++it); + } + else + { + char c = nzToLower(string[0]); + do + { + if (nzToLower(*str) == c) + { + char* ptrPos = str; + str++; + + const char* ptr = &string[1]; + while (true) + { + if (*ptr == '\0') + return static_cast(ptrPos - m_sharedString->string); + + if (*str == '\0') + return npos; + + if (nzToLower(*str) != nzToLower(*ptr)) + break; + + ptr++; + str++; + } + } + } + while (*++str); + } + } + else + { + char* ch = std::strstr(&m_sharedString->string[pos], string); + if (ch) + return static_cast(ch - m_sharedString->string); + } + + return npos; +} + +unsigned int NzString::Find(const NzString& string, int start, nzUInt32 flags) const +{ + if (string.m_sharedString->size == 0 || string.m_sharedString->size > m_sharedString->size) + return npos; + + if (start < 0) + start = std::max(static_cast(m_sharedString->size + start), 0); + + unsigned int pos = static_cast(start); + if (pos >= m_sharedString->size) + return npos; + + char* str = &m_sharedString->string[pos]; + if (flags & CaseInsensitive) + { + if (flags & HandleUtf8) + { + while (utf8::internal::is_trail(*str)) + str++; + + utf8::unchecked::iterator it(str); + + const char* t = string.m_sharedString->string; + char32_t c = NzUnicode::GetLowercase(utf8::unchecked::next(t)); + do + { + if (NzUnicode::GetLowercase(*it) == c) + { + const char* ptrPos = it.base(); + ++it; + + utf8::unchecked::iterator it2(t); + while (true) + { + if (*it2 == '\0') + return static_cast(ptrPos - m_sharedString->string); + + if (*it == '\0') + return npos; + + if (NzUnicode::GetLowercase(*it) != NzUnicode::GetLowercase(*it2)) + break; + + ++it; + ++it2; + } + } + } + while (*++it); + } + else + { + char c = nzToLower(string.m_sharedString->string[0]); + do + { + if (nzToLower(*str) == c) + { + char* ptrPos = str; + str++; + + const char* ptr = &string.m_sharedString->string[1]; + while (true) + { + if (*ptr == '\0') + return static_cast(ptrPos - m_sharedString->string); + + if (*str == '\0') + return npos; + + if (nzToLower(*str) != nzToLower(*ptr)) + break; + + ptr++; + str++; + } + } + } + while (*++str); + } + } + else + { + char* ch = std::strstr(&m_sharedString->string[pos], string.m_sharedString->string); + if (ch) + return static_cast(ch - m_sharedString->string); + } + + return npos; +} + +unsigned int NzString::FindAny(const char* string, int start, nzUInt32 flags) const +{ + if (m_sharedString->size == 0 || !string || !string[0]) + return npos; + + if (start < 0) + start = std::max(m_sharedString->size+start, 0U); + + unsigned int pos = static_cast(start); + if (pos >= m_sharedString->size) + return npos; + + char* str = &m_sharedString->string[pos]; + if (flags & HandleUtf8) + { + while (utf8::internal::is_trail(*str)) + str++; + + utf8::unchecked::iterator it(str); + + if (flags & CaseInsensitive) + { + do + { + utf8::unchecked::iterator it2(string); + char32_t character = NzUnicode::GetLowercase(*it); + do + { + if (character == NzUnicode::GetLowercase(*it2)) + return it.base() - m_sharedString->string; + } + while (*++it2); + } + while (*++it); + } + else + { + do + { + utf8::unchecked::iterator it2(string); + do + { + if (*it == *it2) + return it.base() - m_sharedString->string; + } + while (*++it2); + } + while (*++it); + } + } + else + { + if (flags & CaseInsensitive) + { + do + { + const char* c = string; + char character = nzToLower(*str); + do + { + if (character == nzToLower(*c)) + return str - m_sharedString->string; + } + while (*++c); + } + while (*++str); + } + else + { + str = std::strpbrk(str, string); + if (str) + return str - m_sharedString->string; + } + } + + return npos; +} + +unsigned int NzString::FindAny(const NzString& string, int start, nzUInt32 flags) const +{ + if (m_sharedString->size == 0 || string.m_sharedString->size == 0) + return npos; + + if (string.m_sharedString->size > m_sharedString->size) + return npos; + + if (start < 0) + start = std::max(m_sharedString->size+start, 0U); + + unsigned int pos = static_cast(start); + if (pos >= m_sharedString->size) + return npos; + + char* str = &m_sharedString->string[pos]; + if (flags & HandleUtf8) + { + while (utf8::internal::is_trail(*str)) + str++; + + utf8::unchecked::iterator it(str); + + if (flags & CaseInsensitive) + { + do + { + utf8::unchecked::iterator it2(string.m_sharedString->string); + char32_t character = NzUnicode::GetLowercase(*it); + do + { + if (character == NzUnicode::GetLowercase(*it2)) + return it.base() - m_sharedString->string; + } + while (*++it2); + } + while (*++it); + } + else + { + do + { + utf8::unchecked::iterator it2(string.m_sharedString->string); + do + { + if (*it == *it2) + return it.base() - m_sharedString->string; + } + while (*++it2); + } + while (*++it); + } + } + else + { + if (flags & CaseInsensitive) + { + do + { + const char* c = string.m_sharedString->string; + char character = nzToLower(*str); + do + { + if (character == nzToLower(*c)) + return str - m_sharedString->string; + } + while (*++c); + } + while (*++str); + } + else + { + str = std::strpbrk(str, string.m_sharedString->string); + if (str) + return str - m_sharedString->string; + } + } + + return npos; +} + +unsigned int NzString::FindLast(char character, int start, nzUInt32 flags) const +{ + if (character == '\0' || m_sharedString->size == 0) + return npos; + + if (start < 0) + start = std::max(static_cast(m_sharedString->size + start), 0); + + unsigned int pos = static_cast(start); + if (pos >= m_sharedString->size) + return npos; + + char* ptr = &m_sharedString->string[m_sharedString->size-1]; + + if (flags & CaseInsensitive) + { + character = nzToLower(character); + do + { + if (nzToLower(*ptr) == character) + return static_cast(ptr - m_sharedString->string); + } + while (ptr-- != m_sharedString->string); + } + else + { + // strrchr ? + do + { + if (*ptr == character) + return static_cast(ptr - m_sharedString->string); + } + while (ptr-- != m_sharedString->string); + } + + return npos; +} + +unsigned int NzString::FindLast(const char* string, int start, nzUInt32 flags) const +{ + if (!string || !string[0] ||m_sharedString->size == 0) + return npos; + + if (start < 0) + start = std::max(static_cast(m_sharedString->size + start), 0); + + unsigned int pos = static_cast(start); + if (pos >= m_sharedString->size) + return npos; + + ///Algo 1.FindLast#3 (Taille du pattern inconnue) + const char* ptr = &m_sharedString->string[pos]; + if (flags & CaseInsensitive) + { + if (flags & HandleUtf8) + { + if (utf8::internal::is_trail(*ptr)) + utf8::unchecked::prior(ptr); // On s'assure d'avoir un pointeur vers le début d'un caractère + + utf8::unchecked::iterator it(ptr); + const char* t = string; + char32_t c = NzUnicode::GetLowercase(utf8::unchecked::next(t)); + do + { + if (NzUnicode::GetLowercase(*it) == c) + { + utf8::unchecked::iterator it2(t); + utf8::unchecked::iterator tIt(it); + ++tIt; + + while (true) + { + if (*it2 == '\0') + return it.base() - m_sharedString->string; + + if (tIt.base() > &m_sharedString->string[pos]) + break; + + if (NzUnicode::GetLowercase(*tIt) != NzUnicode::GetLowercase(*it2)) + break; + + ++it2; + ++tIt; + } + } + } + while (it--.base() != m_sharedString->string); + } + else + { + char c = nzToLower(string[0]); + do + { + if (nzToLower(*ptr) == c) + { + const char* p = &string[1]; + const char* tPtr = ptr+1; + while (true) + { + if (*p == '\0') + return ptr - m_sharedString->string; + + if (tPtr > &m_sharedString->string[pos]) + break; + + if (nzToLower(*tPtr) != nzToLower(*p)) + break; + + p++; + tPtr++; + } + } + } + while (ptr-- != m_sharedString->string); + } + } + else + { + do + { + if (*ptr == string[0]) + { + const char* p = &string[1]; + const char* tPtr = ptr+1; + while (true) + { + if (*p == '\0') + return ptr - m_sharedString->string; + + if (tPtr > &m_sharedString->string[pos]) + break; + + if (*tPtr != *p) + break; + + p++; + tPtr++; + } + } + } + while (ptr-- != m_sharedString->string); + } + + return npos; +} + +unsigned int NzString::FindLast(const NzString& string, int start, nzUInt32 flags) const +{ + if (string.m_sharedString->size == 0 || string.m_sharedString->size > m_sharedString->size) + return npos; + + if (start < 0) + start = std::max(static_cast(m_sharedString->size + start), 0); + + unsigned int pos = static_cast(start); + if (pos >= m_sharedString->size || string.m_sharedString->size > m_sharedString->size) + return npos; + + const char* ptr = &m_sharedString->string[pos]; + const char* limit = &m_sharedString->string[string.m_sharedString->size-1]; + + if (flags & CaseInsensitive) + { + if (flags & HandleUtf8) + { + ///Algo 1.FindLast#3 (Itérateur non-adapté) + if (utf8::internal::is_trail(*ptr)) + utf8::unchecked::prior(ptr); // On s'assure d'avoir un pointeur vers le début d'un caractère + + utf8::unchecked::iterator it(ptr); + const char* t = string.m_sharedString->string; + char32_t c = NzUnicode::GetLowercase(utf8::unchecked::next(t)); + do + { + if (NzUnicode::GetLowercase(*it) == c) + { + utf8::unchecked::iterator it2(t); + utf8::unchecked::iterator tIt(it); + ++tIt; + + while (true) + { + if (*it2 == '\0') + return it.base() - m_sharedString->string; + + if (tIt.base() > &m_sharedString->string[pos]) + break; + + if (NzUnicode::GetLowercase(*tIt) != NzUnicode::GetLowercase(*it2)) + break; + + ++it2; + ++tIt; + } + } + } + while (it--.base() != limit); + } + else + { + ///Algo 1.FindLast#4 (Taille du pattern connue) + char c = nzToLower(string.m_sharedString->string[string.m_sharedString->size-1]); + while (true) + { + if (nzToLower(*ptr) == c) + { + const char* p = &string.m_sharedString->string[string.m_sharedString->size-1]; + for (; p >= &string.m_sharedString->string[0]; --p, --ptr) + { + if (nzToLower(*ptr) != nzToLower(*p)) + break; + + if (p == &string.m_sharedString->string[0]) + return ptr-m_sharedString->string; + + if (ptr == m_sharedString->string) + return npos; + } + } + else if (ptr-- <= limit) + break; + } + } + } + else + { + ///Algo 1.FindLast#4 (Taille du pattern connue) + while (true) + { + if (*ptr == string.m_sharedString->string[string.m_sharedString->size-1]) + { + const char* p = &string.m_sharedString->string[string.m_sharedString->size-1]; + for (; p >= &string.m_sharedString->string[0]; --p, --ptr) + { + if (*ptr != *p) + break; + + if (p == &string.m_sharedString->string[0]) + return ptr-m_sharedString->string; + + if (ptr == m_sharedString->string) + return npos; + } + } + else if (ptr-- <= limit) + break; + } + } + + return npos; +} + +unsigned int NzString::FindLastAny(const char* string, int start, nzUInt32 flags) const +{ + if (m_sharedString->size == 0 || !string || !string[0]) + return npos; + + if (start < 0) + start = std::max(m_sharedString->size+start, 0U); + + unsigned int pos = static_cast(start); + if (pos >= m_sharedString->size) + return npos; + + char* str = &m_sharedString->string[pos]; + if (flags & HandleUtf8) + { + while (utf8::internal::is_trail(*str)) + str++; + + utf8::unchecked::iterator it(str); + + if (flags & CaseInsensitive) + { + do + { + utf8::unchecked::iterator it2(string); + char32_t character = NzUnicode::GetLowercase(*it); + do + { + if (character == NzUnicode::GetLowercase(*it2)) + return it.base() - m_sharedString->string; + } + while (*++it2); + } + while (it--.base() != m_sharedString->string); + } + else + { + do + { + utf8::unchecked::iterator it2(string); + do + { + if (*it == *it2) + return it.base() - m_sharedString->string; + } + while (*++it2); + } + while (it--.base() != m_sharedString->string); + } + } + else + { + if (flags & CaseInsensitive) + { + do + { + const char* c = string; + char character = nzToLower(*str); + do + { + if (character == nzToLower(*c)) + return str - m_sharedString->string; + } + while (*++c); + } + while (str-- != m_sharedString->string); + } + else + { + do + { + const char* c = string; + do + { + if (*str == *c) + return str - m_sharedString->string; + } + while (*++c); + } + while (str-- != m_sharedString->string); + } + } + + return npos; +} + +unsigned int NzString::FindLastAny(const NzString& string, int start, nzUInt32 flags) const +{ + if (m_sharedString->size == 0 || string.m_sharedString->size == 0) + return npos; + + if (start < 0) + start = std::max(m_sharedString->size+start, 0U); + + unsigned int pos = static_cast(start); + if (pos >= m_sharedString->size) + return npos; + + char* str = &m_sharedString->string[pos]; + if (flags & HandleUtf8) + { + while (utf8::internal::is_trail(*str)) + str++; + + utf8::unchecked::iterator it(str); + + if (flags & CaseInsensitive) + { + do + { + utf8::unchecked::iterator it2(string.m_sharedString->string); + char32_t character = NzUnicode::GetLowercase(*it); + do + { + if (character == NzUnicode::GetLowercase(*it2)) + return it.base() - m_sharedString->string; + } + while (*++it2); + } + while (it--.base() != m_sharedString->string); + } + else + { + do + { + utf8::unchecked::iterator it2(string.m_sharedString->string); + do + { + if (*it == *it2) + return it.base() - m_sharedString->string; + } + while (*++it2); + } + while (it--.base() != m_sharedString->string); + } + } + else + { + if (flags & CaseInsensitive) + { + do + { + const char* c = string.m_sharedString->string; + char character = nzToLower(*str); + do + { + if (character == nzToLower(*c)) + return str - m_sharedString->string; + } + while (*++c); + } + while (str-- != m_sharedString->string); + } + else + { + do + { + const char* c = string.m_sharedString->string; + do + { + if (*str == *c) + return str - m_sharedString->string; + } + while (*++c); + } + while (str-- != m_sharedString->string); + } + } + + return npos; +} + +unsigned int NzString::FindLastWord(const char* string, int start, nzUInt32 flags) const +{ + if (!string || !string[0] || m_sharedString->size == 0) + return npos; + + if (start < 0) + start = std::max(static_cast(m_sharedString->size + start), 0); + + unsigned int pos = static_cast(start); + if (pos >= m_sharedString->size) + return npos; + + ///Algo 2.FindLastWord#1 (Taille du pattern inconnue) + const char* ptr = &m_sharedString->string[pos]; + + if (flags & HandleUtf8) + { + if (utf8::internal::is_trail(*ptr)) + utf8::unchecked::prior(ptr); // On s'assure d'avoir un pointeur vers le début d'un caractère + + utf8::unchecked::iterator it(ptr); + + if (flags & CaseInsensitive) + { + const char* t = string; // utf8(::unchecked)::next affecte l'itérateur en argument + nzUInt32 c = NzUnicode::GetLowercase(utf8::unchecked::next(t)); + do + { + if (NzUnicode::GetLowercase(*it) == c) + { + if (it.base() != m_sharedString->string) + { + it--; + if (!(NzUnicode::GetCategory(*it++) & NzUnicode::Category_Separator)) + continue; + } + + utf8::unchecked::iterator p(t); + utf8::unchecked::iterator tIt = it; + ++tIt; + + while (true) + { + if (*p == '\0') + { + if (*tIt == '\0' || NzUnicode::GetCategory(*tIt) & NzUnicode::Category_Separator) + return it.base() - m_sharedString->string; + else + break; + } + + if (tIt.base() > &m_sharedString->string[pos]) + break; + + if (NzUnicode::GetLowercase(*tIt) != NzUnicode::GetLowercase(*p)) + break; + + ++p; + ++tIt; + } + } + } + while (it--.base() != m_sharedString->string); + } + else + { + const char* t = string; // utf8(::unchecked)::next affecte l'itérateur en argument + nzUInt32 c = utf8::unchecked::next(t); + do + { + if (*it == c) + { + if (it.base() != m_sharedString->string) + { + it--; + if (!(NzUnicode::GetCategory(*it++) & NzUnicode::Category_Separator)) + continue; + } + + utf8::unchecked::iterator p(t); + utf8::unchecked::iterator tIt = it; + ++tIt; + + while (true) + { + if (*p == '\0') + { + if (*tIt == '\0' || NzUnicode::GetCategory(*tIt) & NzUnicode::Category_Separator) + return it.base() - m_sharedString->string; + else + break; + } + + if (tIt.base() > &m_sharedString->string[pos]) + break; + + if (*tIt != *p) + break; + + ++p; + ++tIt; + } + } + } + while (it--.base() != m_sharedString->string); + } + } + else + { + if (flags & CaseInsensitive) + { + char c = nzToLower(string[0]); + do + { + if (nzToLower(*ptr) == c) + { + if (ptr != m_sharedString->string && !std::isspace(*(ptr-1))) + continue; + + const char* p = &string[1]; + const char* tPtr = ptr+1; + while (true) + { + if (*p == '\0') + { + if (*tPtr == '\0' || std::isspace(*tPtr)) + return ptr-m_sharedString->string; + else + break; + } + + if (tPtr > &m_sharedString->string[pos]) + break; + + if (nzToLower(*tPtr) != nzToLower(*p)) + break; + + p++; + tPtr++; + } + } + } + while (ptr-- != m_sharedString->string); + } + else + { + do + { + if (*ptr == string[0]) + { + if (ptr != m_sharedString->string && !std::isspace(*(ptr-1))) + continue; + + const char* p = &string[1]; + const char* tPtr = ptr+1; + while (true) + { + if (*p == '\0') + { + if (*tPtr == '\0' || std::isspace(*tPtr)) + return ptr-m_sharedString->string; + else + break; + } + + if (tPtr > &m_sharedString->string[pos]) + break; + + if (*tPtr != *p) + break; + + p++; + tPtr++; + } + } + } + while (ptr-- != m_sharedString->string); + } + } + + return npos; +} + +unsigned int NzString::FindLastWord(const NzString& string, int start, nzUInt32 flags) const +{ + if (string.m_sharedString->size == 0 || string.m_sharedString->size > m_sharedString->size) + return npos; + + if (start < 0) + start = std::max(static_cast(m_sharedString->size + start), 0); + + unsigned int pos = static_cast(start); + if (pos >= m_sharedString->size) + return npos; + + const char* ptr = &m_sharedString->string[pos]; + const char* limit = &m_sharedString->string[string.m_sharedString->size-1]; + + if (flags & HandleUtf8) + { + if (utf8::internal::is_trail(*ptr)) + utf8::unchecked::prior(ptr); // On s'assure d'avoir un pointeur vers le début d'un caractère + + utf8::unchecked::iterator it(ptr); + + if (flags & CaseInsensitive) + { + const char* t = string.m_sharedString->string; // utf8(::unchecked)::next affecte l'itérateur en argument + nzUInt32 c = NzUnicode::GetLowercase(utf8::unchecked::next(t)); + do + { + if (NzUnicode::GetLowercase(*it) == c) + { + if (it.base() != m_sharedString->string) + { + it--; + if (!(NzUnicode::GetCategory(*it++) & NzUnicode::Category_Separator)) + continue; + } + + utf8::unchecked::iterator p(t); + utf8::unchecked::iterator tIt = it; + ++tIt; + + while (true) + { + if (*p == '\0') + { + if (*tIt == '\0' || NzUnicode::GetCategory(*tIt) & NzUnicode::Category_Separator) + return it.base() - m_sharedString->string; + else + break; + } + + if (tIt.base() > &m_sharedString->string[pos]) + break; + + if (NzUnicode::GetLowercase(*tIt) != NzUnicode::GetLowercase(*p)) + break; + + ++p; + ++tIt; + } + } + } + while (it--.base() != m_sharedString->string); + } + else + { + const char* t = string.m_sharedString->string; // utf8(::unchecked)::next affecte l'itérateur en argument + nzUInt32 c = utf8::unchecked::next(t); + do + { + if (*it == c) + { + if (it.base() != m_sharedString->string) + { + it--; + if (!(NzUnicode::GetCategory(*it++) & NzUnicode::Category_Separator)) + continue; + } + + utf8::unchecked::iterator p(t); + utf8::unchecked::iterator tIt = it; + ++tIt; + + while (true) + { + if (*p == '\0') + { + if (*tIt == '\0' || NzUnicode::GetCategory(*tIt) & NzUnicode::Category_Separator) + return it.base() - m_sharedString->string; + else + break; + } + + if (tIt.base() > &m_sharedString->string[pos]) + break; + + if (*tIt != *p) + break; + + ++p; + ++tIt; + } + } + } + while (it--.base() != m_sharedString->string); + } + } + else + { + ///Algo 2.FindLastWord#2 (Taille du pattern connue) + if (flags & CaseInsensitive) + { + char c = nzToLower(string.m_sharedString->string[string.m_sharedString->size-1]); + do + { + if (nzToLower(*ptr) == c) + { + if (*(ptr+1) != '\0' && !std::isspace(*(ptr+1))) + continue; + + const char* p = &string.m_sharedString->string[string.m_sharedString->size-1]; + for (; p >= &string.m_sharedString->string[0]; --p, --ptr) + { + if (nzToLower(*ptr) != nzToLower(*p)) + break; + + if (p == &string.m_sharedString->string[0]) + { + if (ptr == m_sharedString->string || std::isspace(*(ptr-1))) + return ptr-m_sharedString->string; + else + break; + } + + if (ptr == m_sharedString->string) + return npos; + } + } + } + while (ptr-- > limit); + } + else + { + do + { + if (*ptr == string.m_sharedString->string[string.m_sharedString->size-1]) + { + if (*(ptr+1) != '\0' && !std::isspace(*(ptr+1))) + continue; + + const char* p = &string.m_sharedString->string[string.m_sharedString->size-1]; + for (; p >= &string.m_sharedString->string[0]; --p, --ptr) + { + if (*ptr != *p) + break; + + if (p == &string.m_sharedString->string[0]) + { + if (ptr == m_sharedString->string || std::isspace(*(ptr-1))) + return ptr-m_sharedString->string; + else + break; + } + + if (ptr == m_sharedString->string) + return npos; + } + } + } + while (ptr-- > limit); + } + } + + return npos; +} + +unsigned int NzString::FindWord(const char* string, int start, nzUInt32 flags) const +{ + if (!string || !string[0] || m_sharedString->size == 0) + return npos; + + if (start < 0) + start = std::max(static_cast(m_sharedString->size + start), 0); + + unsigned int pos = static_cast(start); + if (pos >= m_sharedString->size) + return npos; + + ///Algo 3.FindWord#3 (Taille du pattern inconnue) + const char* ptr = m_sharedString->string; + if (flags & HandleUtf8) + { + if (utf8::internal::is_trail(*ptr)) + utf8::unchecked::prior(ptr); // On s'assure d'avoir un pointeur vers le début d'un caractère + + utf8::unchecked::iterator it(ptr); + + if (flags & CaseInsensitive) + { + const char* t = string; // utf8(::unchecked)::next affecte l'itérateur en argument + nzUInt32 c = NzUnicode::GetLowercase(utf8::unchecked::next(t)); + + do + { + if (*it == c) + { + if (it.base() != m_sharedString->string) + { + it--; + if (!(NzUnicode::GetCategory(*it++) & NzUnicode::Category_Separator)) + continue; + } + + utf8::unchecked::iterator p(t); + utf8::unchecked::iterator tIt = it; + ++tIt; + + while (true) + { + if (*p == '\0') + { + if (*tIt == '\0' || NzUnicode::GetCategory(*it++) & NzUnicode::Category_Separator) + return it.base() - m_sharedString->string; + else + break; + } + + if (NzUnicode::GetLowercase(*tIt) != NzUnicode::GetLowercase(*p)) + break; + + p++; + tIt++; + } + } + } + while (*++ptr); + } + else + { + const char* t = string; // utf8(::unchecked)::next affecte l'itérateur en argument + nzUInt32 c = NzUnicode::GetLowercase(utf8::unchecked::next(t)); + + do + { + if (*it == c) + { + if (it.base() != m_sharedString->string) + { + it--; + if (!(NzUnicode::GetCategory(*it++) & NzUnicode::Category_Separator)) + continue; + } + + utf8::unchecked::iterator p(t); + utf8::unchecked::iterator tIt = it; + ++tIt; + + while (true) + { + if (*p == '\0') + { + if (*tIt == '\0' || NzUnicode::GetCategory(*it++) & NzUnicode::Category_Separator) + return it.base() - m_sharedString->string; + else + break; + } + + if (*tIt != *p) + break; + + p++; + tIt++; + } + } + } + while (*++ptr); + } + } + else + { + if (flags & CaseInsensitive) + { + char c = nzToLower(string[0]); + do + { + if (nzToLower(*ptr) == c) + { + if (ptr != m_sharedString->string && !std::isspace(*(ptr-1))) + continue; + + const char* p = &string[1]; + const char* tPtr = ptr+1; + while (true) + { + if (*p == '\0') + { + if (*tPtr == '\0' || std::isspace(*tPtr)) + return ptr - m_sharedString->string; + else + break; + } + + if (nzToLower(*tPtr) != nzToLower(*p)) + break; + + p++; + tPtr++; + } + } + } + while (*++ptr); + } + else + { + do + { + if (*ptr == string[0]) + { + if (ptr != m_sharedString->string && !std::isspace(*(ptr-1))) + continue; + + const char* p = &string[1]; + const char* tPtr = ptr+1; + while (true) + { + if (*p == '\0') + { + if (*tPtr == '\0' || std::isspace(*tPtr)) + return ptr - m_sharedString->string; + else + break; + } + + if (*tPtr != *p) + break; + + p++; + tPtr++; + } + } + } + while (*++ptr); + } + } + + return npos; +} + +unsigned int NzString::FindWord(const NzString& string, int start, nzUInt32 flags) const +{ + if (string.m_sharedString->size == 0 || string.m_sharedString->size > m_sharedString->size) + return npos; + + if (start < 0) + start = std::max(static_cast(m_sharedString->size + start), 0); + + unsigned int pos = static_cast(start); + if (pos >= m_sharedString->size) + return npos; + + char* ptr = m_sharedString->string; + if (flags & HandleUtf8) + { + ///Algo 3.FindWord#3 (Itérateur trop lent pour #2) + if (utf8::internal::is_trail(*ptr)) + utf8::unchecked::prior(ptr); // On s'assure d'avoir un pointeur vers le début d'un caractère + + utf8::unchecked::iterator it(ptr); + + if (flags & CaseInsensitive) + { + const char* t = string.m_sharedString->string; // utf8(::unchecked)::next affecte l'itérateur en argument + nzUInt32 c = NzUnicode::GetLowercase(utf8::unchecked::next(t)); + + do + { + if (*it == c) + { + if (it.base() != m_sharedString->string) + { + it--; + if (!(NzUnicode::GetCategory(*it++) & NzUnicode::Category_Separator)) + continue; + } + + utf8::unchecked::iterator p(t); + utf8::unchecked::iterator tIt = it; + ++tIt; + + while (true) + { + if (*p == '\0') + { + if (*tIt == '\0' || NzUnicode::GetCategory(*it++) & NzUnicode::Category_Separator) + return it.base() - m_sharedString->string; + else + break; + } + + if (NzUnicode::GetLowercase(*tIt) != NzUnicode::GetLowercase(*p)) + break; + + p++; + tIt++; + } + } + } + while (*++ptr); + } + else + { + const char* t = string.m_sharedString->string; // utf8(::unchecked)::next affecte l'itérateur en argument + nzUInt32 c = NzUnicode::GetLowercase(utf8::unchecked::next(t)); + + do + { + if (*it == c) + { + if (it.base() != m_sharedString->string) + { + it--; + if (!(NzUnicode::GetCategory(*it++) & NzUnicode::Category_Separator)) + continue; + } + + utf8::unchecked::iterator p(t); + utf8::unchecked::iterator tIt = it; + ++tIt; + + while (true) + { + if (*p == '\0') + { + if (*tIt == '\0' || NzUnicode::GetCategory(*it++) & NzUnicode::Category_Separator) + return it.base() - m_sharedString->string; + else + break; + } + + if (*tIt != *p) + break; + + p++; + tIt++; + } + } + } + while (*++ptr); + } + } + else + { + ///Algo 3.FindWord#2 (Taille du pattern connue) + if (flags & CaseInsensitive) + { + char c = nzToLower(string.m_sharedString->string[0]); + do + { + if (nzToLower(*ptr) == c) + { + if (ptr != m_sharedString->string && !std::isspace(*(ptr-1))) + continue; + + const char* p = &string.m_sharedString->string[1]; + const char* tPtr = ptr+1; + while (true) + { + if (*p == '\0') + { + if (*tPtr == '\0' || std::isspace(*tPtr)) + return ptr - m_sharedString->string; + else + break; + } + + if (nzToLower(*tPtr) != nzToLower(*p)) + break; + + p++; + tPtr++; + } + } + } + while (*++ptr); + } + else + { + while ((ptr = std::strstr(ptr, string.m_sharedString->string))) + { + // Si le mot est bien isolé + if ((ptr == m_sharedString->string || std::isspace(*(ptr-1))) && (*(ptr+m_sharedString->size) == '\0' || std::isspace(*(ptr+m_sharedString->size)))) + return ptr - m_sharedString->string; + + ptr++; + } + } + } + + return npos; +} + +char* NzString::GetBuffer() +{ + EnsureOwnership(); + + return m_sharedString->string; +} + +unsigned int NzString::GetCapacity() const +{ + return m_sharedString->capacity; +} + +const char* NzString::GetConstBuffer() const +{ + return m_sharedString->string; +} + +unsigned int NzString::GetLength() const +{ + #if NAZARA_CORE_SAFE + try + { + #endif + return utf8::distance(m_sharedString->string, &m_sharedString->string[m_sharedString->size]); + #if NAZARA_CORE_SAFE + } + catch (const utf8::exception& exception) + { + NazaraError("UTF-8 error : " + NzString(exception.what())); + return 0; + } + #endif +} + +unsigned int NzString::GetSize() const +{ + return m_sharedString->size; +} + +char* NzString::GetUtf8Buffer(unsigned int* size) const +{ + if (m_sharedString->size == 0) + return nullptr; + + char* buffer = new char[m_sharedString->size+1]; + std::memcpy(buffer, m_sharedString->string, m_sharedString->size+1); + + if (size) + *size = m_sharedString->size; + + return buffer; +} + +char16_t* NzString::GetUtf16Buffer(unsigned int* size) const +{ + if (m_sharedString->size == 0) + return nullptr; + + std::vector utf16; + utf16.reserve(m_sharedString->size); + #if NAZARA_CORE_SAFE + try + { + #endif + utf8::utf8to16(m_sharedString->string, &m_sharedString->string[m_sharedString->size], std::back_inserter(utf16)); + #if NAZARA_CORE_SAFE + } + catch (const utf8::exception& exception) + { + NazaraError("UTF-8 error : " + NzString(exception.what())); + return nullptr; + } + #endif + + unsigned int bufferSize = utf16.size(); + if (bufferSize == 0) + return nullptr; + + char16_t* buffer = new char16_t[bufferSize+1]; + std::memcpy(buffer, &utf16[0], bufferSize*sizeof(char16_t)); + buffer[bufferSize] ='\0'; + + if (size) + *size = bufferSize; + + return buffer; +} + +char32_t* NzString::GetUtf32Buffer(unsigned int* size) const +{ + if (m_sharedString->size == 0) + return nullptr; + + #if NAZARA_CORE_SAFE + try + { + #endif + unsigned int bufferSize = utf8::distance(m_sharedString->string, &m_sharedString->string[m_sharedString->size]); + if (bufferSize == 0) + return nullptr; + + char32_t* buffer = new char32_t[bufferSize+1]; + utf8::utf8to32(m_sharedString->string, &m_sharedString->string[m_sharedString->size], buffer); + buffer[bufferSize] ='\0'; + + if (size) + *size = bufferSize; + + return buffer; + #if NAZARA_CORE_SAFE + } + catch (const utf8::exception& exception) + { + NazaraError("UTF-8 error : " + NzString(exception.what())); + return nullptr; + } + #endif +} + +wchar_t* NzString::GetWideBuffer(unsigned int* size) const +{ + static_assert(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4, "wchar_t size is not supported"); + if (m_sharedString->size == 0) + return nullptr; + + #if NAZARA_CORE_SAFE + try + { + #endif + unsigned int bufferSize = utf8::distance(m_sharedString->string, &m_sharedString->string[m_sharedString->size]); + if (bufferSize == 0) + return nullptr; + + wchar_t* buffer = new wchar_t[bufferSize+1]; + if (sizeof(wchar_t) == 4) + utf8::utf8to32(m_sharedString->string, &m_sharedString->string[m_sharedString->size], buffer); + else + { + wchar_t* ptr = buffer; + + utf8::unchecked::iterator it(m_sharedString->string); + do + { + char32_t cp = *it; + if (cp <= 0xFFFF && (cp < 0xD800 || cp > 0xDFFF)) // @Laurent Gomila + *ptr++ = static_cast(cp); + else + *ptr++ = L'?'; + } + while (*it++); + } + + if (size) + *size = bufferSize; + + return buffer; + #if NAZARA_CORE_SAFE + } + catch (const utf8::exception& exception) + { + NazaraError("UTF-8 error : " + NzString(exception.what())); + return nullptr; + } + #endif +} + +NzString NzString::GetWord(unsigned int index, nzUInt32 flags) const +{ + if (m_sharedString->size == 0) + return NzString(); + + NzString temp = Simplified(flags); // Évitons les mauvaises surprises + if (temp.IsEmpty()) + return NzString(); + + unsigned int foundPos = temp.Find(' '); // Simplified nous assure que nous n'avons plus que des espaces comme séparation + unsigned int lastPos = 0; + for (; index > 0; --index) + { + if (foundPos == npos) + return NzString(); + + lastPos = foundPos; + foundPos = temp.Find(' ', foundPos+1); + } + return temp.Substr((lastPos == 0) ? 0 : lastPos+1, (foundPos == npos) ? -1 : static_cast(foundPos-1)); +} + +unsigned int NzString::GetWordPosition(unsigned int index, nzUInt32 flags) const +{ + if (m_sharedString->size == 0) + return npos; + + unsigned int currentWord = 0; + bool inWord = false; + + const char* ptr = m_sharedString->string; + if (flags & HandleUtf8) + { + utf8::unchecked::iterator it(ptr); + do + { + if (NzUnicode::GetCategory(*it) & NzUnicode::Category_Separator) + inWord = false; + else + { + if (!inWord) + { + inWord = true; + if (++currentWord > index) + return static_cast(it.base() - m_sharedString->string); + } + } + } + while (*++it); + } + else + { + while (ptr != &m_sharedString->string[m_sharedString->size]) + { + if (!std::isspace(*ptr++)) + { + if (!inWord) + { + inWord = true; + if (++currentWord > index) + return ptr-m_sharedString->string; + } + } + else + inWord = false; + } + } + + return npos; +} + +NzString& NzString::Insert(int pos, char character) +{ + if (character == '\0') + return *this; + + if (m_sharedString->size == 0 && m_sharedString->capacity == 0) + return operator=(character); + + if (pos < 0) + pos = std::max(static_cast(m_sharedString->size + pos), 0); + + unsigned int start = std::min(static_cast(pos), m_sharedString->size); + + // Si le buffer est déjà suffisamment grand + if (m_sharedString->capacity >= m_sharedString->size+1) + { + EnsureOwnership(); + + std::memmove(&m_sharedString->string[start+1], &m_sharedString->string[start], m_sharedString->size); + m_sharedString->string[start] = character; + + m_sharedString->size += 1; + } + else + { + unsigned int newSize = m_sharedString->size+1; + char* newString = new char[newSize+1]; + + char* ptr = newString; + const char* s = m_sharedString->string; + while (ptr != &newString[start]) + *ptr++ = *s++; + + *ptr++ = character; + + std::strcpy(ptr, s); + + ReleaseString(); + + m_sharedString = new SharedString; + m_sharedString->capacity = newSize; + m_sharedString->size = newSize; + m_sharedString->string = newString; + } + + return *this; +} + +NzString& NzString::Insert(int pos, const char* string) +{ + if (!string || !string[0]) + return *this; + + if (pos < 0) + pos = std::max(static_cast(m_sharedString->size + pos), 0); + + unsigned int start = std::min(static_cast(pos), m_sharedString->size); + + // Si le buffer est déjà suffisamment grand + unsigned int len = std::strlen(string); + if (m_sharedString->capacity >= m_sharedString->size+len) + { + EnsureOwnership(); + + std::memmove(&m_sharedString->string[start+len], &m_sharedString->string[start], m_sharedString->size); + std::memcpy(&m_sharedString->string[start], string, len+1); + + m_sharedString->size += len; + } + else + { + unsigned int newSize = m_sharedString->size+len; + char* newString = new char[newSize+1]; + + char* ptr = newString; + const char* s = m_sharedString->string; + + while (ptr != &newString[start]) + *ptr++ = *s++; + + const char* p = string; + while (ptr != &newString[start+len]) + *ptr++ = *p++; + + std::strcpy(ptr, s); + + ReleaseString(); + m_sharedString = new SharedString; + m_sharedString->capacity = newSize; + m_sharedString->size = newSize; + m_sharedString->string = newString; + } + + return *this; +} + +NzString& NzString::Insert(int pos, const NzString& string) +{ + if (string.m_sharedString->size == 0) + return *this; + + if (m_sharedString->size == 0 && m_sharedString->capacity < string.m_sharedString->size) + return operator=(string); + + if (pos < 0) + pos = std::max(static_cast(m_sharedString->size + pos), 0); + + unsigned int start = std::min(static_cast(pos), m_sharedString->size); + + // Si le buffer est déjà suffisamment grand + if (m_sharedString->capacity >= m_sharedString->size + string.m_sharedString->size) + { + EnsureOwnership(); + + std::memmove(&m_sharedString->string[start+string.m_sharedString->size], &m_sharedString->string[start], m_sharedString->size); + std::memcpy(&m_sharedString->string[start], string.m_sharedString->string, string.m_sharedString->size+1); + + m_sharedString->size += string.m_sharedString->size; + } + else + { + unsigned int newSize = m_sharedString->size+string.m_sharedString->size; + char* newString = new char[newSize+1]; + + char* ptr = newString; + const char* s = m_sharedString->string; + + while (ptr != &newString[start]) + *ptr++ = *s++; + + const char* p = string.m_sharedString->string; + while (ptr != &newString[start+string.m_sharedString->size]) + *ptr++ = *p++; + + std::strcpy(ptr, s); + + ReleaseString(); + m_sharedString = new SharedString; + m_sharedString->capacity = newSize; + m_sharedString->size = newSize; + m_sharedString->string = newString; + } + + return *this; +} + +bool NzString::IsEmpty() const +{ + return m_sharedString->size == 0; +} + +bool NzString::IsNull() const +{ + return m_sharedString == &emptyString; +} + +bool NzString::IsNumber(nzUInt8 base, nzUInt32 flags) const +{ + #if NAZARA_CORE_SAFE + if (base < 2 || base > 36) + { + NazaraError("Base must be between 2 and 36"); + return false; + } + #endif + + if (m_sharedString->size == 0) + return false; + + NzString check = Simplified(); + if (check.m_sharedString->size) + return false; + + char* ptr = (check.m_sharedString->string[0] == '-') ? &check.m_sharedString->string[1] : check.m_sharedString->string; + + if (base > 10) + { + if (flags & CaseInsensitive) + { + do + { + char c = *ptr; + if (c != ' ' && (c < '0' || (c > '9' && c < 'A') || (c > 'A'+base-1 && c < 'a') || c > 'a'+base-1)) + return false; + } + while (*++ptr); + } + else + { + do + { + char c = *ptr; + if (c != ' ' && (c < '0' || (c > '9' && c < 'a') || c > 'a'+base-1)) + return false; + } + while (*++ptr); + } + } + else + { + do + { + char c = *ptr; + if (c != ' ' && (c < '0' || c > '0'+base-1)) + return false; + } + while (*++ptr); + } + + return true; +} + +bool NzString::Match(const char* pattern) const +{ + if (m_sharedString->size == 0 || !pattern) + return false; + + // Par Jack Handy - akkhandy@hotmail.com + // From : http://www.codeproject.com/Articles/1088/Wildcard-string-compare-globbing + const char* str = m_sharedString->string; + while (*str && *pattern != '*') + { + if (*pattern != *str && *pattern != '?') + return false; + + pattern++; + str++; + } + + const char* cp = nullptr; + const char* mp = nullptr; + while (*str) + { + if (*pattern == '*') + { + if (!*++pattern) + return true; + + mp = pattern; + cp = str+1; + } + else if (*pattern == *str || *pattern == '?') + { + pattern++; + str++; + } + else + { + pattern = mp; + str = cp++; + } + } + + while (*pattern == '*') + pattern++; + + return !*pattern; +} + +bool NzString::Match(const NzString& pattern) const +{ + return Match(pattern.m_sharedString->string); +} + +NzString& NzString::Prepend(char character) +{ + return Insert(0, character); +} + +NzString& NzString::Prepend(const char* string) +{ + return Insert(0, string); +} + +NzString& NzString::Prepend(const NzString& string) +{ + return Insert(0, string); +} + +unsigned int NzString::Replace(char oldCharacter, char newCharacter, int start, nzUInt32 flags) +{ + if (oldCharacter == '\0' || oldCharacter == newCharacter) + return 0; + + if (newCharacter == '\0') // Dans ce cas, il faut passer par un algorithme plus complexe + return Replace(NzString(oldCharacter), NzString(), start); + + if (start < 0) + start = std::max(m_sharedString->size + start, 0U); + + unsigned int pos = static_cast(start); + if (pos >= m_sharedString->size) + return npos; + + unsigned int count = 0; + char* ptr = &m_sharedString->string[pos]; + bool found = false; + if (flags & CaseInsensitive) + { + char character_lower = nzToLower(oldCharacter); + char character_upper = nzToUpper(oldCharacter); + do + { + if (*ptr == character_lower || *ptr == character_upper) + { + if (!found) + { + unsigned int offset = ptr-m_sharedString->string; + + EnsureOwnership(); + + ptr = &m_sharedString->string[offset]; + found = true; + } + + *ptr = newCharacter; + ++count; + } + } + while (*++ptr); + } + else + { + while ((ptr = std::strchr(ptr, oldCharacter))) + { + if (!found) + { + unsigned int offset = ptr-m_sharedString->string; + + EnsureOwnership(); + + ptr = &m_sharedString->string[offset]; + found = true; + } + + *ptr = newCharacter; + ++count; + } + } + + return count; +} + +unsigned int NzString::Replace(const char* oldString, const char* replaceString, int start, nzUInt32 flags) +{ + if (!oldString || !oldString[0]) + return 0; + + if (start < 0) + start = std::max(m_sharedString->size + start, 0U); + + unsigned int pos = static_cast(start); + if (pos >= m_sharedString->size) + return 0; + + unsigned int oSize = std::strlen(oldString); + if (oSize == 0) + return 0; + + unsigned int rSize = (replaceString) ? std::strlen(replaceString) : 0; + + unsigned int count = 0; + if (oSize == rSize) + { + bool found = false; + + // Si aucun changement de taille n'est nécessaire, nous pouvons alors utiliser un algorithme bien plus rapide + while ((pos = Find(oldString, pos, flags)) != npos) + { + if (!found) + { + EnsureOwnership(); + found = true; + } + + std::memcpy(&m_sharedString->string[pos], replaceString, oSize); + pos += oSize; + + ++count; + } + } + else ///TODO: Algorithme de remplacement sans changement de buffer (si rSize < oSize) + { + unsigned int newSize = m_sharedString->size + Count(oldString)*(rSize - oSize); + if (newSize == m_sharedString->size) // Alors c'est que Count(oldString) == 0 + return 0; + + char* newString = new char[newSize+1]; + + ///Algo 4.Replace#2 + char* ptr = newString; + const char* p = m_sharedString->string; + + while ((pos = Find(oldString, pos, flags)) != npos) + { + const char* r = &m_sharedString->string[pos]; + + std::memcpy(ptr, p, r-p); + ptr += r-p; + std::memcpy(ptr, replaceString, rSize); + ptr += rSize; + p = r+oSize; + pos += oSize; + + count++; + } + + std::strcpy(ptr, p); // Ajoute le caractère de fin par la même occasion + + ReleaseString(); + m_sharedString = new SharedString; + m_sharedString->capacity = newSize; + m_sharedString->size = newSize; + m_sharedString->string = newString; + } + + return count; +} + +unsigned int NzString::Replace(const NzString& oldString, const NzString& replaceString, int start, nzUInt32 flags) +{ + if (oldString.m_sharedString->size == 0) + return 0; + + if (start < 0) + start = std::max(m_sharedString->size + start, 0U); + + unsigned int pos = static_cast(start); + if (pos >= m_sharedString->size || oldString.m_sharedString->size == 0) + return 0; + + unsigned int count = 0; + if (oldString.m_sharedString->size == replaceString.m_sharedString->size) + { + bool found = false; + + // Si aucun changement de taille n'est nécessaire, nous pouvons alors utiliser un algorithme bien plus rapide + while ((pos = Find(oldString, pos, flags)) != npos) + { + if (!found) + { + EnsureOwnership(); + found = true; + } + + std::memcpy(&m_sharedString->string[pos], replaceString.m_sharedString->string, oldString.m_sharedString->size); + pos += oldString.m_sharedString->size; + + ++count; + } + } + else + { + unsigned int newSize = m_sharedString->size + Count(oldString)*(replaceString.m_sharedString->size - oldString.m_sharedString->size); + if (newSize == m_sharedString->size) // Alors c'est que Count(oldString) == 0 + return 0; + + char* newString = new char[newSize+1]; + + ///Algo 4.Replace#2 + char* ptr = newString; + const char* p = m_sharedString->string; + + while ((pos = Find(oldString, pos, flags)) != npos) + { + const char* r = &m_sharedString->string[pos]; + + std::memcpy(ptr, p, r-p); + ptr += r-p; + std::memcpy(ptr, replaceString.m_sharedString->string, replaceString.m_sharedString->size); + ptr += replaceString.m_sharedString->size; + p = r+oldString.m_sharedString->size; + pos += oldString.m_sharedString->size; + + count++; + } + + std::strcpy(ptr, p); // Ajoute le caractère de fin par la même occasion + + ReleaseString(); + m_sharedString = new SharedString; + m_sharedString->capacity = newSize; + m_sharedString->size = newSize; + m_sharedString->string = newString; + } + + return count; +} + +unsigned int NzString::ReplaceAny(const char* oldCharacters, char replaceCharacter, int start, nzUInt32 flags) +{ + ///FIXME: Ne gère pas l'UTF-8 + if (!oldCharacters || !oldCharacters[0]) + return 0; + + /*if (replaceCharacter == '\0') // Dans ce cas, il faut passer par un algorithme plus complexe + return ReplaceAny(NzString(oldCharacters), NzString(), start);*/ + + if (start < 0) + start = std::max(m_sharedString->size + start, 0U); + + unsigned int pos = static_cast(start); + if (pos >= m_sharedString->size) + return npos; + + unsigned int count = 0; + char* ptr = &m_sharedString->string[pos]; + if (flags & CaseInsensitive) + { + do + { + const char* c = oldCharacters; + char character = nzToLower(*ptr); + bool found = false; + do + { + if (character == nzToLower(*c)) + { + if (!found) + { + unsigned int offset = ptr-m_sharedString->string; + + EnsureOwnership(); + + ptr = &m_sharedString->string[offset]; + found = true; + } + + *ptr = replaceCharacter; + ++count; + break; + } + } + while (*++c); + } + while (*++ptr); + } + else + { + bool found = false; + while ((ptr = std::strpbrk(ptr, oldCharacters))) + { + if (!found) + { + unsigned int offset = ptr-m_sharedString->string; + + EnsureOwnership(); + + ptr = &m_sharedString->string[offset]; + found = true; + } + + *ptr++ = replaceCharacter; + ++count; + } + } + + return count; +} +/* + unsigned int NzString::ReplaceAny(const char* oldCharacters, const char* replaceString, int start, nzUInt32 flags) + { + if (start < 0) + { + start = m_sharedString->size+start; + if (start < 0) + start = 0; + } + + unsigned int pos = static_cast(start); + unsigned int oSize = (oldCharacters) ? std::strlen(oldCharacters) : 0; + unsigned int rSize = (replaceString) ? std::strlen(replaceString) : 0; + + if (pos >= m_sharedString->size || m_sharedString->size == 0 || oSize == 0) + return 0; + + unsigned int count = 0; + + if (rSize == 1) // On utilise un algorithme optimisé + { + EnsureOwnership(); + + f or (; pos < m_sharedString->size; ++pos) + { + for (unsigned int i = 0; i < oSize; ++i) + { + if (m_sharedString->string[pos] == oldCharacters[i]) + { + m_sharedString->string[pos] = replaceString[0]; + ++count; + + break; + } + } + } + } + else + { + unsigned int newSize; + { + unsigned int count = CountAny(oldCharacters); + newSize = m_sharedString->size - count + count*rSize; + } + char* newString = new char[newSize+1]; + + unsigned int j = 0; + for (unsigned int i = 0; i < m_sharedString->size; ++i) + { + if (i < pos) // Avant la position où on est censé commencer à remplacer, on ne fait que recopier + newString[j++] = m_sharedString->string[i]; + else + { + bool found = false; + for (unsigned int l = 0; l < oSize; ++l) + { + if (m_sharedString->string[i] == oldCharacters[l]) + { + for (unsigned int k = 0; k < rSize; ++k) + newString[j++] = replaceString[k]; + + ++count; + found = true; + break; // Simple façon d'éviter la ligne après la boucle + } + } + + if (!found) + newString[j++] = m_sharedString->string[i]; + } + } + newString[newSize] = '\0'; + + ReleaseString(); + + m_sharedString->size = newSize; + m_sharedString->string = newString; + } + + return count; + } + + unsigned int NzString::ReplaceAny(const NzString& oldCharacters, const NzString& replaceString, int start, nzUInt32 flags) + { + if (start < 0) + { + start = m_sharedString->size+start; + if (start < 0) + start = 0; + } + + unsigned int pos = static_cast(start); + + if (pos >= m_sharedString->size || m_sharedString->size == 0 || oldCharacters.m_sharedString->size == 0) + return 0; + + unsigned int count = 0; + + if (replaceString.m_sharedString->size == 1) // On utilise un algorithme optimisé + { + EnsureOwnership(); + + char character = replaceString[0]; + for (; pos < m_sharedString->size; ++pos) + { + for (unsigned int i = 0; i < oldCharacters.m_sharedString->size; ++i) + { + if (m_sharedString->string[pos] == oldCharacters[i]) + { + m_sharedString->string[pos] = character; + ++count; + break; + } + } + } + } + else + { + unsigned int newSize; + { + unsigned int count = CountAny(oldCharacters); + newSize = m_sharedString->size - count + count*replaceString.m_sharedString->size; + } + char* newString = new char[newSize+1]; + + unsigned int j = 0; + for (unsigned int i = 0; i < m_sharedString->size; ++i) + { + if (i < pos) // Avant la position où on est censé commencer à remplacer, on ne fait que recopier + newString[j++] = m_sharedString->string[i]; + else + { + bool found = false; + for (unsigned int l = 0; l < oldCharacters.m_sharedString->size; ++l) + { + if (m_sharedString->string[i] == oldCharacters[l]) + { + for (unsigned int k = 0; k < replaceString.m_sharedString->size; ++k) + newString[j++] = replaceString[k]; + + ++count; + found = true; + break; // Simple façon d'éviter la ligne après la boucle + } + } + + if (!found) + newString[j++] = m_sharedString->string[i]; + } + } + newString[newSize] = '\0'; + + ReleaseString(); + + m_sharedString->size = newSize; + m_sharedString->string = newString; + } + + return count; + } +*/ +void NzString::Reserve(unsigned int bufferSize) +{ + if (m_sharedString->capacity >= bufferSize) + return; + + char* ptr = new char[bufferSize+1]; + if (m_sharedString->size > 0) + std::memcpy(ptr, m_sharedString->string, m_sharedString->size+1); + + unsigned int size = m_sharedString->size; + + ReleaseString(); + m_sharedString = new SharedString; + m_sharedString->capacity = bufferSize; + m_sharedString->size = size; + m_sharedString->string = ptr; +} + +NzString& NzString::Resize(int size, char character) +{ + if (size == 0) + { + Clear(true); + return *this; + } + + if (size < 0) + size = std::max(static_cast(m_sharedString->size + size), 0); + + unsigned int newSize = static_cast(size); + + if (m_sharedString->capacity >= newSize) + { + EnsureOwnership(); + + // Nous avons déjà la place requise, contentons-nous de remplir le buffer + if (character != '\0' && newSize > m_sharedString->size) + { + char* ptr = &m_sharedString->string[m_sharedString->size]; + char* limit = &m_sharedString->string[newSize]; + while (ptr != limit) + *ptr++ = character; + } + + m_sharedString->size = newSize; + m_sharedString->string[newSize] = '\0'; + } + else // On veut forcément agrandir la chaine + { + char* newString = new char[newSize+1]; + if (m_sharedString->size != 0) + std::memcpy(newString, m_sharedString->string, newSize); + + char* ptr = &newString[m_sharedString->size]; + char* limit = &newString[newSize]; + while (ptr != limit) + *ptr++ = character; + + ReleaseString(); + m_sharedString = new SharedString; + m_sharedString->capacity = newSize; + m_sharedString->size = newSize; + m_sharedString->string = newString; + } + + return *this; +} + +NzString NzString::Resized(int size, char character) const +{ + if (size < 0) + size = m_sharedString->size + size; + + if (size <= 0) + return NzString(); + + unsigned int newSize = static_cast(size); + if (newSize == m_sharedString->size) + return *this; + + char* str = new char[newSize+1]; + if (newSize > m_sharedString->size) + { + std::memcpy(str, m_sharedString->string, m_sharedString->size); + if (character != '\0') + { + char* ptr = &str[m_sharedString->size]; + char* limit = &str[newSize]; + while (ptr != limit) + *ptr++ = character; + } + } + else + std::memcpy(str, m_sharedString->string, newSize); + + str[newSize] = '\0'; + + return NzString(new SharedString(1, newSize, newSize, str)); +} + +NzString& NzString::Reverse() +{ + if (m_sharedString->size != 0) + { + unsigned int i = 0; + unsigned int j = m_sharedString->size-1; + + while (i < j) + std::swap(m_sharedString->string[i++], m_sharedString->string[j--]); + } + + return *this; +} + +NzString NzString::Reversed() const +{ + if (m_sharedString->size == 0) + return NzString(); + + char* str = new char[m_sharedString->size+1]; + + char* ptr = &str[m_sharedString->size-1]; + char* p = m_sharedString->string; + + do + *ptr-- = *p; + while (*(++p)); + + str[m_sharedString->size] = '\0'; + + return NzString(new SharedString(1, m_sharedString->size, m_sharedString->size, str)); +} + +NzString NzString::Simplified(nzUInt32 flags) const +{ + char* str = new char[m_sharedString->size+1]; + char* p = str; + + const char* ptr = m_sharedString->string; + bool inword = false; + if (flags & HandleUtf8) + { + utf8::unchecked::iterator it(ptr); + do + { + if (NzUnicode::GetCategory(*it) & NzUnicode::Category_Separator) + { + if (inword) + { + *p++ = ' '; + inword = false; + } + } + else + { + p = utf8::append(*it, p); + inword = true; + } + } + while (*++it); + } + else + { + const char* limit = &m_sharedString->string[m_sharedString->size]; + do + { + if (std::isspace(*ptr)) + { + if (inword) + { + *p++ = ' '; + inword = false; + } + } + else + { + *p++ = *ptr; + inword = true; + } + } + while (++ptr != limit); + } + + if (!inword) + p--; + + *p = '\0'; + + return NzString(new SharedString(1, m_sharedString->size, p-str, str)); +} + +NzString& NzString::Simplify(nzUInt32 flags) +{ + return operator=(Simplified(flags)); +} + +unsigned int NzString::Split(std::vector& result, char separation, int start, nzUInt32 flags) const +{ + if (separation == '\0' || m_sharedString->size == 0) + return 0; + + unsigned int lastSep = Find(separation, start, flags); + if (lastSep == npos) + { + result.push_back(*this); + return 1; + } + else if (lastSep != 0) + result.push_back(Substr(0, lastSep-1)); + + while (true) + { + unsigned int sep = Find(separation, lastSep+1, flags); + if (sep == npos) + break; + + if (sep-lastSep > 1) + result.push_back(Substr(lastSep+1, sep-1)); + + lastSep = sep; + } + + if (lastSep != m_sharedString->size-1) + result.push_back(Substr(lastSep+1)); + + return result.size(); +} + +unsigned int NzString::Split(std::vector& result, const char* separation, int start, nzUInt32 flags) const +{ + unsigned int size = (separation) ? std::strlen(separation) : 0; + if (m_sharedString->size == 0) + return 0; + else if (size == 0) + { + result.reserve(m_sharedString->size); + for (unsigned int i = 0; i < m_sharedString->size; ++i) + result.push_back(NzString(m_sharedString->string[i])); + + return m_sharedString->size; + } + else if (size > m_sharedString->size) + { + result.push_back(*this); + return 1; + } + + unsigned int lastSep = Find(separation, start, flags); + unsigned int oldSize = result.size(); + if (lastSep == npos) + { + result.push_back(*this); + return 1; + } + else if (lastSep != 0) + result.push_back(Substr(0, lastSep-1)); + + unsigned int sep; + while ((sep = Find(separation, lastSep+size, flags)) != npos) + { + if (sep-lastSep > size) + result.push_back(Substr(lastSep+size, sep-1)); + + lastSep = sep; + } + + if (lastSep != m_sharedString->size-size) + result.push_back(Substr(lastSep+size)); + + return result.size()-oldSize; +} + +unsigned int NzString::Split(std::vector& result, const NzString& separation, int start, nzUInt32 flags) const +{ + if (m_sharedString->size == 0) + return 0; + else if (separation.m_sharedString->size == 0) + { + result.reserve(m_sharedString->size); + for (unsigned int i = 0; i < m_sharedString->size; ++i) + result.push_back(NzString(m_sharedString->string[i])); + + return m_sharedString->size; + } + else if (separation.m_sharedString->size > m_sharedString->size) + { + result.push_back(*this); + return 1; + } + + unsigned int lastSep = Find(separation, start, flags); + unsigned int oldSize = result.size(); + if (lastSep == npos) + { + result.push_back(*this); + return 1; + } + else if (lastSep != 0) + result.push_back(Substr(0, lastSep-1)); + + unsigned int sep; + while ((sep = Find(separation, lastSep+separation.m_sharedString->size, flags)) != npos) + { + if (sep-lastSep > separation.m_sharedString->size) + result.push_back(Substr(lastSep+separation.m_sharedString->size, sep-1)); + + lastSep = sep; + } + + if (lastSep != m_sharedString->size-separation.m_sharedString->size) + result.push_back(Substr(lastSep+separation.m_sharedString->size)); + + return result.size()-oldSize; +} + +unsigned int NzString::SplitAny(std::vector& result, const char* separations, int start, nzUInt32 flags) const +{ + if (m_sharedString->size == 0) + return 0; + + unsigned int oldSize = result.size(); + + unsigned int lastSep = FindAny(separations, start, flags); + if (lastSep == npos) + { + result.push_back(*this); + return 1; + } + else if (lastSep != 0) + result.push_back(Substr(0, lastSep-1)); + + unsigned int sep; + while ((sep = FindAny(separations, lastSep+1, flags)) != npos) + { + if (sep-lastSep > 1) + result.push_back(Substr(lastSep+1, sep-1)); + + lastSep = sep; + } + + if (lastSep != m_sharedString->size-1) + result.push_back(Substr(lastSep+1)); + + return result.size()-oldSize; +} + +unsigned int NzString::SplitAny(std::vector& result, const NzString& separations, int start, nzUInt32 flags) const +{ + if (m_sharedString->size == 0) + return 0; + + unsigned int lastSep = FindAny(separations, start, flags); + unsigned int oldSize = result.size(); + if (lastSep == npos) + { + result.push_back(*this); + return 1; + } + else if (lastSep != 0) + result.push_back(Substr(0, lastSep-1)); + + unsigned int sep; + while ((sep = FindAny(separations, lastSep+1, flags)) != npos) + { + if (sep-lastSep > 1) + result.push_back(Substr(lastSep+1, sep-1)); + + lastSep = sep; + } + + if (lastSep != m_sharedString->size-1) + result.push_back(Substr(lastSep+1)); + + return result.size()-oldSize; +} + +bool NzString::StartsWith(char character, nzUInt32 flags) const +{ + if (character == '\0' || m_sharedString->size == 0) + return false; + + if (flags & CaseInsensitive) + return nzToLower(m_sharedString->string[0]) == nzToLower(character); + else + return m_sharedString->string[0] == character; +} + +bool NzString::StartsWith(const char* string, nzUInt32 flags) const +{ + if (!string || !string[0] || m_sharedString->size == 0) + return false; + + if (flags & CaseInsensitive) + { + if (flags & HandleUtf8) + { + utf8::unchecked::iterator it(m_sharedString->string); + utf8::unchecked::iterator it2(string); + do + { + if (*it2 == '\0') + return true; + + if (NzUnicode::GetLowercase(*it) != NzUnicode::GetLowercase(*it2)) + return false; + + ++it2; + } + while (*it++); + } + else + { + char* ptr = m_sharedString->string; + const char* s = string; + do + { + if (*s == '\0') + return true; + + if (nzToLower(*ptr) != nzToLower(*s)) + return false; + + s++; + } + while (*ptr++); + } + } + else + { + char* ptr = m_sharedString->string; + const char* s = string; + do + { + if (*s == '\0') + return true; + + if (*ptr != *s) + return false; + + s++; + } + while (*ptr++); + } + + return false; +} + +bool NzString::StartsWith(const NzString& string, nzUInt32 flags) const +{ + if (string.m_sharedString->size == 0) + return false; + + if (m_sharedString->size < string.m_sharedString->size) + return false; + + if (flags & CaseInsensitive) + { + if (flags & HandleUtf8) + { + utf8::unchecked::iterator it(m_sharedString->string); + utf8::unchecked::iterator it2(string.m_sharedString->string); + do + { + if (*it2 == '\0') + return true; + + if (NzUnicode::GetLowercase(*it) != NzUnicode::GetLowercase(*it2)) + return false; + + ++it2; + } + while (*it++); + } + else + { + char* ptr = m_sharedString->string; + const char* s = string.m_sharedString->string; + do + { + if (*s == '\0') + return true; + + if (nzToLower(*ptr) != nzToLower(*s)) + return false; + + s++; + } + while (*ptr++); + } + } + else + return std::memcmp(m_sharedString->string, string.m_sharedString->string, string.m_sharedString->size) == 0; + + return false; +} + +NzString NzString::Substr(int startPos, int endPos) const +{ + if (startPos < 0) + startPos = std::max(m_sharedString->size+startPos, 0U); + + unsigned int start = static_cast(startPos); + + if (endPos < 0) + { + endPos = m_sharedString->size+endPos; + if (endPos < 0) + return NzString(); + } + + unsigned int minEnd = std::min(static_cast(endPos), m_sharedString->size-1); + + if (start > minEnd || start >= m_sharedString->size) + return NzString(); + + unsigned int size = minEnd-start+1; + char* str = new char[size+1]; + std::memcpy(str, &m_sharedString->string[start], size); + str[size] = '\0'; + + return NzString(new SharedString(1, size, size, str)); +} + +NzString NzString::SubstrFrom(char character, int startPos, bool fromLast, bool include, nzUInt32 flags) const +{ + if (character == '\0') + return *this; + + unsigned int pos; + if (fromLast) + pos = FindLast(character, startPos, flags); + else + pos = Find(character, startPos, flags); + + if (pos == 0 and include) + return *this; + else if (pos == npos) + return NzString(); + + return Substr(pos+((include) ? 0 : 1)); +} + +NzString NzString::SubstrFrom(const char* string, int startPos, bool fromLast, bool include, nzUInt32 flags) const +{ + unsigned int pos; + if (fromLast) + pos = FindLast(string, startPos, flags); + else + pos = Find(string, startPos, flags); + + if (pos == 0 && include) + return *this; + else if (pos == npos) + return NzString(); + + return Substr(pos+((include) ? 0 : std::strlen(string))); +} + +NzString NzString::SubstrFrom(const NzString& string, int startPos, bool fromLast, bool include, nzUInt32 flags) const +{ + unsigned int pos; + if (fromLast) + pos = FindLast(string, startPos, flags); + else + pos = Find(string, startPos, flags); + + if (pos == 0 && include) + return *this; + else if (pos == npos) + return NzString(); + + return Substr(pos+((include) ? 0 : string.m_sharedString->size)); +} + +NzString NzString::SubstrTo(char character, int startPos, bool toLast, bool include, nzUInt32 flags) const +{ + if (character == '\0') + return *this; + + unsigned int pos; + if (toLast) + pos = FindLast(character, startPos); + else + pos = Find(character, startPos, flags); + + if (pos == 0) + return (include) ? character : NzString(); + else if (pos == npos) + return *this; + + return Substr(0, pos+((include) ? 1 : 0)-1); +} + +NzString NzString::SubstrTo(const char* string, int startPos, bool toLast, bool include, nzUInt32 flags) const +{ + unsigned int pos; + if (toLast) + pos = FindLast(string, startPos, flags); + else + pos = Find(string, startPos, flags); + + if (pos == 0) + return (include) ? string : NzString(); + else if (pos == npos) + return *this; + + return Substr(0, pos+((include) ? std::strlen(string) : 0)-1); +} + +NzString NzString::SubstrTo(const NzString& string, int startPos, bool toLast, bool include, nzUInt32 flags) const +{ + unsigned int pos; + if (toLast) + pos = FindLast(string, startPos, flags); + else + pos = Find(string, startPos, flags); + + if (pos == 0) + return (include) ? string : NzString(); + else if (pos == npos) + return *this; + + return Substr(0, pos+((include) ? string.m_sharedString->size : 0)-1); +} + +void NzString::Swap(NzString& str) +{ + std::swap(m_sharedString, str.m_sharedString); +} + +bool NzString::ToBool(bool* value, nzUInt32 flags) const +{ + if (m_sharedString->size == 0) + return false; + + NzString word = GetWord(0); + + if (word[0] == '1') + { + if (value) + *value = true; + } + else if (word[0] == '0') + { + if (value) + *value = false; + } + else + { + if (flags & CaseInsensitive) + word = word.ToLower(); // Les mots identifiés sont en ASCII, inutile de passer le flag unicode + + if (word == "true") + { + if (value) + *value = true; + } + else if (word == "false") + { + if (value) + *value = false; + } + else + return false; + } + + return true; +} + +bool NzString::ToDouble(double* value) const +{ + if (m_sharedString->size == 0) + return false; + + if (value) + *value = std::atof(m_sharedString->string); + + return true; +} + +bool NzString::ToInteger(long long* value, nzUInt8 base) const +{ + if (value) + { + bool ok; + *value = NzStringToNumber(*this, base, &ok); + + return ok; + } + else + return IsNumber(base); +} + +NzString NzString::ToLower(nzUInt32 flags) const +{ + if (m_sharedString->size == 0) + return *this; + + if (flags & HandleUtf8) + { + NzString lower; + lower.Reserve(m_sharedString->size); + utf8::unchecked::iterator it(m_sharedString->string); + do + utf8::append(NzUnicode::GetLowercase(*it), std::back_inserter(lower)); + while (*++it); + + return lower; + } + else + { + char* str = new char[m_sharedString->size+1]; + + char* ptr = m_sharedString->string; + char* s = str; + do + *s++ = nzToLower(*ptr); + while (*++ptr); + + *s = '\0'; + + return NzString(new SharedString(1, m_sharedString->size, m_sharedString->size, str)); + } +} + +NzString NzString::ToUpper(nzUInt32 flags) const +{ + if (m_sharedString->size == 0) + return *this; + + if (flags & HandleUtf8) + { + NzString upper; + upper.Reserve(m_sharedString->size); + utf8::unchecked::iterator it(m_sharedString->string); + do + utf8::append(NzUnicode::GetUppercase(*it), std::back_inserter(upper)); + while (*++it); + + return upper; + } + else + { + char* str = new char[m_sharedString->size+1]; + + char* ptr = m_sharedString->string; + char* s = str; + do + *s++ = nzToUpper(*ptr); + while (*++ptr); + + *s = '\0'; + + return NzString(new SharedString(1, m_sharedString->size, m_sharedString->size, str)); + } +} + +NzString& NzString::Trim(nzUInt32 flags) +{ + return operator=(Trimmed(flags)); +} + +NzString& NzString::Trim(char character, nzUInt32 flags) +{ + return operator=(Trimmed(character, flags)); +} + +NzString NzString::Trimmed(nzUInt32 flags) const +{ + if (m_sharedString->size == 0) + return *this; + + unsigned int startPos; + unsigned int endPos; + if (flags & HandleUtf8) + { + if ((flags & TrimOnlyRight) == 0) + { + utf8::unchecked::iterator it(m_sharedString->string); + do + { + if (NzUnicode::GetCategory(*it) & NzUnicode::Category_Separator) + break; + } + while (*++it); + + startPos = it.base() - m_sharedString->string; + } + else + startPos = 0; + + if ((flags & TrimOnlyLeft) == 0) + { + utf8::unchecked::iterator it(&m_sharedString->string[m_sharedString->size]); + while ((it--).base() != m_sharedString->string) + { + if (NzUnicode::GetCategory(*it) & NzUnicode::Category_Separator) + break; + } + + endPos = it.base() - m_sharedString->string; + } + else + endPos = m_sharedString->size-1; + } + else + { + startPos = 0; + if ((flags & TrimOnlyRight) == 0) + { + for (; startPos < m_sharedString->size; ++startPos) + { + if (!std::isspace(m_sharedString->string[startPos])) + break; + } + } + + endPos = m_sharedString->size-1; + if ((flags & TrimOnlyLeft) == 0) + { + for (; endPos > 0; --endPos) + { + if (!std::isspace(m_sharedString->string[endPos])) + break; + } + } + } + + return Substr(startPos, endPos); +} + +NzString NzString::Trimmed(char character, nzUInt32 flags) const +{ + if (m_sharedString->size == 0) + return *this; + + unsigned int startPos = 0; + unsigned int endPos = m_sharedString->size-1; + if (flags & CaseInsensitive) + { + char ch = nzToLower(character); + if ((flags & TrimOnlyRight) == 0) + { + for (; startPos < m_sharedString->size; ++startPos) + { + if (nzToLower(m_sharedString->string[startPos]) != ch) + break; + } + } + + if ((flags & TrimOnlyLeft) == 0) + { + for (; endPos > 0; --endPos) + { + if (nzToLower(m_sharedString->string[startPos]) != ch) + break; + } + } + } + else + { + if ((flags & TrimOnlyRight) == 0) + { + for (; startPos < m_sharedString->size; ++startPos) + { + if (m_sharedString->string[startPos] != character) + break; + } + } + + if ((flags & TrimOnlyLeft) == 0) + { + for (; endPos > 0; --endPos) + { + if (m_sharedString->string[startPos] != character) + break; + } + } + } + + return Substr(startPos, endPos); +} + +char* NzString::begin() +{ + return m_sharedString->string; +} + +const char* NzString::begin() const +{ + return m_sharedString->string; +} + +char* NzString::end() +{ + return &m_sharedString->string[m_sharedString->size]; +} + +const char* NzString::end() const +{ + return &m_sharedString->string[m_sharedString->size]; +} + +void NzString::push_front(char c) +{ + operator=(c + *this); +} + +void NzString::push_back(char c) +{ + operator+=(c); +} +/* +char* NzString::rbegin() +{ + return &m_sharedString->string[m_sharedString->size-1]; +} + +const char* NzString::rbegin() const +{ + return &m_sharedString->string[m_sharedString->size-1]; +} + +char* NzString::rend() +{ + return &m_sharedString->string[-1]; +} + +const char* NzString::rend() const +{ + return &m_sharedString->string[-1]; +} +*/ + +NzString::operator std::string() const +{ + return std::string(m_sharedString->string, m_sharedString->size); +} + +char& NzString::operator[](unsigned int pos) +{ + EnsureOwnership(); + + if (pos >= m_sharedString->size) + Resize(pos+1); + + return m_sharedString->string[pos]; +} + +char NzString::operator[](unsigned int pos) const +{ + #if NAZARA_CORE_SAFE + if (pos >= m_sharedString->size) + { + NazaraError("Index out of range (" + Number(pos) + " >= " + Number(m_sharedString->size) + ')'); + return 0; + } + #endif + + return m_sharedString->string[pos]; +} + +NzString& NzString::operator=(char character) +{ + if (character != '\0') + { + if (m_sharedString->capacity >= 1) + EnsureOwnership(); + else + { + ReleaseString(); + m_sharedString = new SharedString; + m_sharedString->capacity = 1; + m_sharedString->string = new char[2]; + } + + m_sharedString->size = 1; + m_sharedString->string[0] = character; + m_sharedString->string[1] = '\0'; + } + else + ReleaseString(); + + return *this; +} + +NzString& NzString::operator=(const char* string) +{ + if (string && string[0] != '\0') + { + unsigned int size = std::strlen(string); + if (m_sharedString->capacity >= size) + EnsureOwnership(); + else + { + ReleaseString(); + + m_sharedString = new SharedString; + m_sharedString->capacity = size; + m_sharedString->string = new char[size+1]; + } + + m_sharedString->size = size; + std::memcpy(m_sharedString->string, string, size+1); + } + else + ReleaseString(); + + return *this; +} + +NzString& NzString::operator=(const std::string& string) +{ + if (string.size() > 0) + { + if (m_sharedString->capacity >= string.size()) + EnsureOwnership(); + else + { + ReleaseString(); + + m_sharedString = new SharedString; + m_sharedString->capacity = string.size(); + m_sharedString->string = new char[string.size()+1]; + } + + m_sharedString->size = string.size(); + std::memcpy(m_sharedString->string, string.c_str(), string.size()+1); + } + else + ReleaseString(); + + return *this; +} + +NzString& NzString::operator=(const NzString& string) +{ + ReleaseString(); + + m_sharedString = string.m_sharedString; + if (m_sharedString != &emptyString) + { + NazaraMutexLock(m_sharedString->mutex); + m_sharedString->refCount++; + NazaraMutexUnlock(m_sharedString->mutex); + } + + return *this; +} + +NzString& NzString::operator=(NzString&& string) noexcept +{ + std::swap(m_sharedString, string.m_sharedString); + + return *this; +} + +NzString NzString::operator+(char character) const +{ + if (character == '\0') + return *this; + + unsigned int totalSize = m_sharedString->size+1; + char* str = new char[totalSize+1]; + std::memcpy(str, m_sharedString->string, m_sharedString->size); + + str[m_sharedString->size] = character; + str[totalSize] = '\0'; + + return NzString(new SharedString(1, totalSize, totalSize, str)); +} + +NzString NzString::operator+(const char* string) const +{ + if (!string || !string[0]) + return *this; + + if (m_sharedString->size == 0) + return string; + + unsigned int length = std::strlen(string); + if (length == 0) + return *this; + + unsigned int totalSize = m_sharedString->size + length; + char* str = new char[totalSize+1]; + std::memcpy(str, m_sharedString->string, m_sharedString->size); + std::memcpy(&str[m_sharedString->size], string, length+1); + + return NzString(new SharedString(1, totalSize, totalSize, str)); +} + +NzString NzString::operator+(const std::string& string) const +{ + if (string.empty()) + return *this; + + if (m_sharedString->size == 0) + return string; + + unsigned int totalSize = m_sharedString->size + string.size(); + char* str = new char[totalSize+1]; + std::memcpy(str, m_sharedString->string, m_sharedString->size); + std::memcpy(&str[m_sharedString->size], string.c_str(), string.size()+1); + + return NzString(new SharedString(1, totalSize, totalSize, str)); +} + +NzString NzString::operator+(const NzString& string) const +{ + if (string.m_sharedString->size == 0) + return *this; + + if (m_sharedString->size == 0) + return string; + + unsigned int totalSize = m_sharedString->size + string.m_sharedString->size; + char* str = new char[totalSize+1]; + std::memcpy(str, m_sharedString->string, m_sharedString->size); + std::memcpy(&str[m_sharedString->size], string.m_sharedString->string, string.m_sharedString->size+1); + + return NzString(new SharedString(1, totalSize, totalSize, str)); +} + +NzString& NzString::operator+=(char character) +{ + if (character == '\0') + return *this; + + if (m_sharedString->size == 0) + return operator=(character); + + if (m_sharedString->capacity > m_sharedString->size) + { + EnsureOwnership(); + + m_sharedString->string[m_sharedString->size] = character; + m_sharedString->string[m_sharedString->size+1] = '\0'; + m_sharedString->size++; + } + else + { + unsigned int newSize = m_sharedString->size+1; + unsigned int bufferSize = nzGetNewSize(newSize); + + char* str = new char[bufferSize+1]; + std::memcpy(str, m_sharedString->string, m_sharedString->size); + str[m_sharedString->size] = character; + str[newSize] = '\0'; + + ReleaseString(); + m_sharedString = new SharedString; + m_sharedString->capacity = bufferSize; + m_sharedString->size = newSize; + m_sharedString->string = str; + } + + return *this; +} + +NzString& NzString::operator+=(const char* string) +{ + if (!string || !string[0]) + return *this; + + if (m_sharedString->size == 0) + return operator=(string); + + unsigned int size = std::strlen(string); + if (size == 0) + return *this; + + if (m_sharedString->capacity >= m_sharedString->size + size) + { + EnsureOwnership(); + + std::memcpy(&m_sharedString->string[m_sharedString->size], string, size+1); + m_sharedString->size += size; + } + else + { + unsigned int newSize = m_sharedString->size + size; + unsigned int bufferSize = nzGetNewSize(newSize); + + char* str = new char[bufferSize+1]; + std::memcpy(str, m_sharedString->string, m_sharedString->size); + std::memcpy(&str[m_sharedString->size], string, size+1); + + ReleaseString(); + m_sharedString = new SharedString; + m_sharedString->capacity = bufferSize; + m_sharedString->size = newSize; + m_sharedString->string = str; + } + + return *this; +} + +NzString& NzString::operator+=(const std::string& string) +{ + if (string.empty()) + return *this; + + if (m_sharedString->size == 0) + return operator=(string); + + if (m_sharedString->capacity >= m_sharedString->size + string.size()) + { + EnsureOwnership(); + + std::memcpy(&m_sharedString->string[m_sharedString->size], string.c_str(), string.size()+1); + m_sharedString->size += string.size(); + } + else + { + unsigned int newSize = m_sharedString->size + string.size(); + unsigned int bufferSize = nzGetNewSize(newSize); + + char* str = new char[bufferSize+1]; + std::memcpy(str, m_sharedString->string, m_sharedString->size); + std::memcpy(&str[m_sharedString->size], string.c_str(), string.size()+1); + + ReleaseString(); + m_sharedString = new SharedString; + m_sharedString->capacity = bufferSize; + m_sharedString->size = newSize; + m_sharedString->string = str; + } + + return *this; +} + +NzString& NzString::operator+=(const NzString& string) +{ + if (string.m_sharedString->size == 0) + return *this; + + if (m_sharedString->size == 0) + return operator=(string); + + if (m_sharedString->capacity >= m_sharedString->size + string.m_sharedString->size) + { + EnsureOwnership(); + + std::memcpy(&m_sharedString->string[m_sharedString->size], string.m_sharedString->string, string.m_sharedString->size+1); + m_sharedString->size += string.m_sharedString->size; + } + else + { + unsigned int newSize = m_sharedString->size + string.m_sharedString->size; + unsigned int bufferSize = nzGetNewSize(newSize); + + char* str = new char[bufferSize+1]; + std::memcpy(str, m_sharedString->string, m_sharedString->size); + std::memcpy(&str[m_sharedString->size], string.m_sharedString->string, string.m_sharedString->size+1); + + ReleaseString(); + m_sharedString = new SharedString; + m_sharedString->capacity = bufferSize; + m_sharedString->size = newSize; + m_sharedString->string = str; + } + + return *this; +} + +bool NzString::operator==(char character) const +{ + if (m_sharedString->size == 0) + return character == '\0'; + + if (m_sharedString->size > 1) + return false; + + return m_sharedString->string[0] == character; +} + +bool NzString::operator==(const char* string) const +{ + if (m_sharedString->size == 0) + return !string || !string[0]; + + if (!string || !string[0]) + return false; + + return std::strcmp(m_sharedString->string, string) == 0; +} + +bool NzString::operator==(const std::string& string) const +{ + if (m_sharedString->size == 0 || string.empty()) + return m_sharedString->size == string.size(); + + if (m_sharedString->size != string.size()) + return false; + + return std::strcmp(m_sharedString->string, string.c_str()) == 0; +} + +bool NzString::operator!=(char character) const +{ + if (m_sharedString->size == 0) + return character != '\0'; + + if (character == '\0' || m_sharedString->size != 1) + return true; + + if (m_sharedString->size != 1) + return true; + + return m_sharedString->string[0] != character; +} + +bool NzString::operator!=(const char* string) const +{ + if (m_sharedString->size == 0) + return string && string[0]; + + if (!string || !string[0]) + return true; + + return std::strcmp(m_sharedString->string, string) != 0; +} + +bool NzString::operator!=(const std::string& string) const +{ + if (m_sharedString->size == 0 || string.empty()) + return m_sharedString->size == string.size(); + + if (m_sharedString->size != string.size()) + return false; + + return std::strcmp(m_sharedString->string, string.c_str()) != 0; +} + +bool NzString::operator<(char character) const +{ + if (character == '\0') + return false; + + if (m_sharedString->size == 0) + return true; + + return m_sharedString->string[0] < character; +} + +bool NzString::operator<(const char* string) const +{ + if (!string || !string[0]) + return false; + + if (m_sharedString->size == 0) + return true; + + return std::strcmp(m_sharedString->string, string) < 0; +} + +bool NzString::operator<(const std::string& string) const +{ + if (string.empty()) + return false; + + if (m_sharedString->size == 0) + return true; + + return std::strcmp(m_sharedString->string, string.c_str()) < 0; +} + +bool NzString::operator<=(char character) const +{ + if (m_sharedString->size == 0) + return true; + + if (character == '\0') + return false; + + return m_sharedString->string[0] < character || (m_sharedString->string[0] == character && m_sharedString->size == 1); +} + +bool NzString::operator<=(const char* string) const +{ + if (m_sharedString->size == 0) + return true; + + if (!string || !string[0]) + return false; + + return std::strcmp(m_sharedString->string, string) <= 0; +} + +bool NzString::operator<=(const std::string& string) const +{ + if (m_sharedString->size == 0) + return true; + + if (string.empty()) + return false; + + return std::strcmp(m_sharedString->string, string.c_str()) <= 0; +} + +bool NzString::operator>(char character) const +{ + if (m_sharedString->size == 0) + return false; + + if (character == '\0') + return true; + + return m_sharedString->string[0] > character; +} + +bool NzString::operator>(const char* string) const +{ + if (m_sharedString->size == 0) + return false; + + if (!string || !string[0]) + return true; + + return std::strcmp(m_sharedString->string, string) > 0; +} + +bool NzString::operator>(const std::string& string) const +{ + if (m_sharedString->size == 0) + return false; + + if (string.empty()) + return true; + + return std::strcmp(m_sharedString->string, string.c_str()) > 0; +} + +bool NzString::operator>=(char character) const +{ + if (character == '\0') + return true; + + if (m_sharedString->size == 0) + return false; + + return m_sharedString->string[0] > character || (m_sharedString->string[0] == character && m_sharedString->size == 1); +} + +bool NzString::operator>=(const char* string) const +{ + if (!string || !string[0]) + return true; + + if (m_sharedString->size == 0) + return false; + + return std::strcmp(m_sharedString->string, string) >= 0; +} + +bool NzString::operator>=(const std::string& string) const +{ + if (string.empty()) + return true; + + if (m_sharedString->size == 0) + return false; + + return std::strcmp(m_sharedString->string, string.c_str()) >= 0; +} + +NzString NzString::Boolean(bool boolean) +{ + unsigned int size = (boolean) ? 4 : 5; + char* str = new char[size+1]; + std::strcpy(str, (boolean) ? "true" : "false"); + + return NzString(new SharedString(1, size, size, str)); +} + +int NzString::Compare(const NzString& first, const NzString& second) +{ + if (first.m_sharedString->size == 0) + return (second.m_sharedString->size == 0) ? 0 : -1; + + if (second.m_sharedString->size == 0) + return 1; + + return std::strcmp(first.m_sharedString->string, second.m_sharedString->string); +} + +NzString NzString::Number(float number) +{ + std::ostringstream oss; + oss.precision(NAZARA_CORE_REAL_PRECISION); + oss << number; + + return NzString(oss.str()); +} + +NzString NzString::Number(double number) +{ + std::ostringstream oss; + oss.precision(NAZARA_CORE_REAL_PRECISION); + oss << number; + + return NzString(oss.str()); +} + +NzString NzString::Number(long double number) +{ + std::ostringstream oss; + oss.precision(NAZARA_CORE_REAL_PRECISION); + oss << number; + + return NzString(oss.str()); +} + +NzString NzString::Number(signed char number, nzUInt8 radix) +{ + return NzNumberToString(number, radix); +} + +NzString NzString::Number(unsigned char number, nzUInt8 radix) +{ + return NzNumberToString(number, radix); +} + +NzString NzString::Number(short number, nzUInt8 radix) +{ + return NzNumberToString(number, radix); +} + +NzString NzString::Number(unsigned short number, nzUInt8 radix) +{ + return NzNumberToString(number, radix); +} + +NzString NzString::Number(int number, nzUInt8 radix) +{ + return NzNumberToString(number, radix); +} + +NzString NzString::Number(unsigned int number, nzUInt8 radix) +{ + return NzNumberToString(number, radix); +} + +NzString NzString::Number(long number, nzUInt8 radix) +{ + return NzNumberToString(number, radix); +} + +NzString NzString::Number(unsigned long number, nzUInt8 radix) +{ + return NzNumberToString(number, radix); +} + +NzString NzString::Number(long long number, nzUInt8 radix) +{ + return NzNumberToString(number, radix); +} + +NzString NzString::Number(unsigned long long number, nzUInt8 radix) +{ + return NzNumberToString(number, radix); +} + +NzString NzString::Pointer(const void* ptr) +{ + unsigned int size = sizeof(ptr)*2+2; + char* str = new char[size+1]; + std::sprintf(str, "0x%p", ptr); + + return NzString(new SharedString(1, size, size, str)); +} + +NzString NzString::Unicode(char32_t character) +{ + if (character == '\0') + return NzString(); + + unsigned int count = 0; + if (character < 0x80) + count = 1; + else if (character < 0x800) + count = 2; + else if (character < 0x10000) + count = 3; + else + count = 4; + + char* str = new char[count+1]; + utf8::append(character, str); + str[count] = '\0'; + + return NzString(new SharedString(1, count, count, str)); +} + +NzString NzString::Unicode(const char* u8String) +{ + return NzString(u8String); +} + +NzString NzString::Unicode(const char16_t* u16String) +{ + if (!u16String || !u16String[0]) + return NzString(); + + const char16_t* ptr = u16String; + unsigned int count = 0; + do + count++; + while (*++ptr); + + count *= 2; // On s'assure d'avoir la place suffisante + + char* str = new char[count+1]; + char* r = utf8::utf16to8(u16String, ptr, str); + *r = '\0'; + + return NzString(new SharedString(1, count, r-str, str)); +} + +NzString NzString::Unicode(const char32_t* u32String) +{ + if (!u32String || !u32String[0]) + return NzString(); + + const char32_t* ptr = u32String; + unsigned int count = 0; + do + { + char32_t cp = *ptr; + if (cp < 0x80) + count += 1; + else if (cp < 0x800) + count += 2; + else if (cp < 0x10000) + count += 3; + else + count += 4; + } + while (*++ptr); + + char* str = new char[count+1]; + char* r = utf8::utf32to8(u32String, ptr, str); + *r = '\0'; + + return NzString(new SharedString(1, count, count, str)); +} + +NzString NzString::Unicode(const wchar_t* wString) +{ + if (!wString || !wString[0]) + return NzString(); + + const wchar_t* ptr = wString; + unsigned int count = 0; + do + { + char32_t cp = *ptr; + if (cp < 0x80) + count += 1; + else if (cp < 0x800) + count += 2; + else if (cp < 0x10000) + count += 3; + else + count += 4; + } + while (*++ptr); + + char* str = new char[count+1]; + char* r = utf8::utf32to8(wString, ptr, str); + *r = '\0'; + + return NzString(new SharedString(1, count, count, str)); +} + +std::istream& operator>>(std::istream& is, NzString& str) +{ + str.Clear(); + + char c; + do + { + is.get(c); + if (c == '\0') + break; + else if (std::isspace(c)) + { + if (str.IsNull()) + continue; + else + break; + } + else + str += c; + } + while (true); + + return is; +} + +std::ostream& operator<<(std::ostream& os, const NzString& str) +{ + if (str.IsEmpty()) + return os; + + return operator<<(os, str.m_sharedString->string); +} + +NzString operator+(char character, const NzString& string) +{ + if (character == '\0') + return string; + + if (string.IsEmpty()) + return character; + + unsigned int totalSize = string.m_sharedString->size+1; + char* str = new char[totalSize+1]; + str[0] = character; + std::memcpy(str, string.m_sharedString->string, string.m_sharedString->size+1); + + return NzString(new NzString::SharedString(1, totalSize, totalSize, str)); +} + +NzString operator+(const char* string, const NzString& nstring) +{ + if (!string || !string[0]) + return nstring; + + if (nstring.IsEmpty()) + return string; + + unsigned int size = std::strlen(string); + unsigned int totalSize = size + nstring.m_sharedString->size; + char* str = new char[totalSize+1]; + std::memcpy(str, string, size); + std::memcpy(&str[size], nstring.m_sharedString->string, nstring.m_sharedString->size+1); + + return NzString(new NzString::SharedString(1, totalSize, totalSize, str)); +} + +NzString operator+(const std::string& string, const NzString& nstring) +{ + if (string.empty()) + return nstring; + + if (nstring.m_sharedString->size == 0) + return string; + + unsigned int totalSize = string.size() + nstring.m_sharedString->size; + char* str = new char[totalSize+1]; + std::memcpy(str, string.c_str(), string.size()); + std::memcpy(&str[string.size()], nstring.m_sharedString->string, nstring.m_sharedString->size+1); + + return NzString(new NzString::SharedString(1, totalSize, totalSize, str)); +} + +bool operator==(const NzString& first, const NzString& second) +{ + if (first.m_sharedString->size == 0 || second.m_sharedString->size == 0) + return first.m_sharedString->size == second.m_sharedString->size; + + if (first.m_sharedString->size != second.m_sharedString->size) + return false; + + return std::strcmp(first.m_sharedString->string, second.m_sharedString->string) == 0; +} + +bool operator!=(const NzString& first, const NzString& second) +{ + return !operator==(first, second); +} + +bool operator<(const NzString& first, const NzString& second) +{ + if (second.m_sharedString->size == 0) + return false; + + if (first.m_sharedString->size == 0) + return true; + + return std::strcmp(first.m_sharedString->string, second.m_sharedString->string) < 0; +} + +bool operator<=(const NzString& first, const NzString& second) +{ + return !operator<(second, first); +} + +bool operator>(const NzString& first, const NzString& second) +{ + return second < first; +} + +bool operator>=(const NzString& first, const NzString& second) +{ + return !operator<(first, second); +} + +bool operator==(char character, const NzString& nstring) +{ + return nstring == character; +} + +bool operator==(const char* string, const NzString& nstring) +{ + return nstring == string; +} + +bool operator==(const std::string& string, const NzString& nstring) +{ + return nstring == string; +} + +bool operator!=(char character, const NzString& nstring) +{ + return !operator==(character, nstring); +} + +bool operator!=(const char* string, const NzString& nstring) +{ + return !operator==(string, nstring); +} + +bool operator!=(const std::string& string, const NzString& nstring) +{ + return !operator==(string, nstring); +} + +bool operator<(char character, const NzString& nstring) +{ + return nstring > character; +} + +bool operator<(const char* string, const NzString& nstring) +{ + return nstring > string; +} + +bool operator<(const std::string& string, const NzString& nstring) +{ + return nstring > string; +} + +bool operator<=(char character, const NzString& nstring) +{ + return !operator<(nstring, character); +} + +bool operator<=(const char* string, const NzString& nstring) +{ + return !operator<(nstring, string); +} + +bool operator<=(const std::string& string, const NzString& nstring) +{ + return !operator<(nstring, string); +} + +bool operator>(char character, const NzString& nstring) +{ + return nstring < character; +} + +bool operator>(const char* string, const NzString& nstring) +{ + return nstring < string; +} + +bool operator>(const std::string& string, const NzString& nstring) +{ + return nstring < string; +} + +bool operator>=(char character, const NzString& nstring) +{ + return !operator<(character, nstring); +} + +bool operator>=(const char* string, const NzString& nstring) +{ + return !operator<(string, nstring); +} + +bool operator>=(const std::string& string, const NzString& nstring) +{ + return !operator<(string, nstring); +} + +void NzString::EnsureOwnership() +{ + if (m_sharedString == &emptyString) + return; + + NazaraLock(m_sharedString->mutex); + if (m_sharedString->refCount > 1) + { + m_sharedString->refCount--; + + char* string = new char[m_sharedString->capacity+1]; + std::memcpy(string, m_sharedString->string, m_sharedString->size+1); + + m_sharedString = new SharedString(1, m_sharedString->capacity, m_sharedString->size, string); + } +} + +bool NzString::FillHash(NzHashImpl* hazh) const +{ + hazh->Append(reinterpret_cast(m_sharedString->string), m_sharedString->size); + + return true; +} + +void NzString::ReleaseString() +{ + if (m_sharedString == &emptyString) + return; + + NazaraMutexLock(m_sharedString->mutex); + bool freeSharedString = (--m_sharedString->refCount == 0); + NazaraMutexUnlock(m_sharedString->mutex); + + if (freeSharedString) + { + delete[] m_sharedString->string; + delete m_sharedString; + } + + m_sharedString = &emptyString; +} + +NzString::SharedString NzString::emptyString(0, 0, 0, nullptr); +const unsigned int NzString::npos(std::numeric_limits::max()); + +namespace std +{ + istream& getline(istream& is, NzString& str) + { + str.Clear(); + + char c; + do + { + is.get(c); + if (c != '\n' && c != '\0') + str += c; + else + break; + } + while (true); + + return is; + } + + istream& getline(istream& is, NzString& str, char delim) + { + str.Clear(); + + char c; + do + { + is.get(c); + if (c != delim && c != '\0') + str += c; + else + break; + } + while (true); + + return is; + } + + void swap(NzString& lhs, NzString& rhs) + { + lhs.Swap(rhs); + } +} diff --git a/src/Nazara/Renderer/Context.cpp b/src/Nazara/Renderer/Context.cpp index 1dfc9787e..e10588943 100644 --- a/src/Nazara/Renderer/Context.cpp +++ b/src/Nazara/Renderer/Context.cpp @@ -1,355 +1,359 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Renderer module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include -#include - -#if defined(NAZARA_PLATFORM_WINDOWS) - #include -#elif defined(NAZARA_PLATFORM_LINUX) - #include -#else - #error Lack of implementation: Context -#endif - -#include - -namespace -{ - NAZARA_THREADLOCAL NzContext* currentContext = nullptr; - NAZARA_THREADLOCAL NzContext* threadContext = nullptr; - - std::vector contexts; - - void CALLBACK DebugCallback(unsigned int source, unsigned int type, unsigned int id, unsigned int severity, int length, const char* message, void* userParam) - { - NazaraUnused(length); - - NzStringStream ss; - ss << "OpenGL debug message (ID: 0x" << NzString::Number(id, 16) << "):\n"; - ss << "Sent by context: " << userParam; - ss << "\n-Source: "; - switch (source) - { - case GL_DEBUG_SOURCE_API_ARB: - ss << "OpenGL"; - break; - - case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB: - ss << "Operating system"; - break; - - case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB: - ss << "Shader compiler"; - break; - - case GL_DEBUG_SOURCE_THIRD_PARTY_ARB: - ss << "Shader compiler"; - break; - - case GL_DEBUG_SOURCE_APPLICATION_ARB: - ss << "Application"; - break; - - case GL_DEBUG_SOURCE_OTHER_ARB: - ss << "Other"; - break; - - default: - // Peut être rajouté par une extension - ss << "Unknown"; - break; - } - ss << '\n'; - - ss << "-Type: "; - switch (type) - { - case GL_DEBUG_TYPE_ERROR_ARB: - ss << "Error"; - break; - - case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB: - ss << "Deprecated behavior"; - break; - - case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB: - ss << "Undefined behavior"; - break; - - case GL_DEBUG_TYPE_PORTABILITY_ARB: - ss << "Portability"; - break; - - case GL_DEBUG_TYPE_PERFORMANCE_ARB: - ss << "Performance"; - break; - - case GL_DEBUG_TYPE_OTHER_ARB: - ss << "Other"; - break; - - default: - // Peut être rajouté par une extension - ss << "Unknown"; - break; - } - ss << '\n'; - - ss << "-Severity: "; - switch (severity) - { - case GL_DEBUG_SEVERITY_HIGH_ARB: - ss << "High"; - break; - - case GL_DEBUG_SEVERITY_MEDIUM_ARB: - ss << "Medium"; - break; - - case GL_DEBUG_SEVERITY_LOW_ARB: - ss << "Low"; - break; - - default: - // Peut être rajouté par une extension - ss << "Unknown"; - break; - } - ss << '\n'; - - ss << "Message: " << message << '\n'; - - NazaraNotice(ss); - } -} - -NzContext::NzContext() : -m_impl(nullptr) -{ -} - -NzContext::~NzContext() -{ - Destroy(); -} - -bool NzContext::Create(const NzContextParameters& parameters) -{ - Destroy(); - - m_parameters = parameters; - if (m_parameters.shared && !m_parameters.shareContext) - m_parameters.shareContext = s_reference; - - m_impl = new NzContextImpl; - if (!m_impl->Create(m_parameters)) - { - NazaraError("Failed to create context implementation"); - delete m_impl; - m_impl = nullptr; - - return false; - } - - if (!m_impl->Activate()) - { - NazaraError("Failed to activate context"); - - m_impl->Destroy(); - delete m_impl; - m_impl = nullptr; - - return false; - } - - if (m_parameters.antialiasingLevel > 0) - glEnable(GL_MULTISAMPLE); - - if (NzOpenGL::IsSupported(NzOpenGL::DebugOutput) && m_parameters.debugMode) - { - glDebugMessageCallback(&DebugCallback, this); - - #ifdef NAZARA_DEBUG - glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); - #endif - } - - return true; -} - -void NzContext::Destroy() -{ - if (m_impl) - { - if (currentContext == this) - NzContextImpl::Desactivate(); - - m_impl->Destroy(); - delete m_impl; - m_impl = nullptr; - } -} - -const NzContextParameters& NzContext::GetParameters() const -{ - #ifdef NAZARA_RENDERER_SAFE - if (!m_impl) - NazaraError("No context has been created"); - #endif - - return m_parameters; -} - -bool NzContext::IsActive() const -{ - #ifdef NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("No context has been created"); - return false; - } - #endif - - return currentContext == this; -} - -bool NzContext::SetActive(bool active) -{ - #ifdef NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("No context has been created"); - return false; - } - #endif - - // Si le contexte est déjà activé/désactivé - if ((currentContext == this) == active) - return true; - - if (active) - { - if (!m_impl->Activate()) - return false; - - currentContext = this; - } - else - { - if (!NzContextImpl::Desactivate()) - return false; - - currentContext = nullptr; - } - - return true; -} - -void NzContext::SwapBuffers() -{ - #ifdef NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("No context has been created"); - return; - } - - if (!m_parameters.doubleBuffered) - { - NazaraError("Context is not double buffered"); - return; - } - #endif - - m_impl->SwapBuffers(); -} - -bool NzContext::EnsureContext() -{ - if (!currentContext) - { - if (!threadContext) - { - NzContext* context = new NzContext; - if (!context->Create()) - { - NazaraError("Failed to create context"); - delete context; - - return false; - } - - contexts.push_back(context); - - threadContext = context; - } - - if (!threadContext->SetActive(true)) - { - NazaraError("Failed to active thread context"); - return false; - } - } - - return true; -} - -NzContext* NzContext::GetCurrent() -{ - return currentContext; -} - -const NzContext* NzContext::GetReference() -{ - return s_reference; -} - -NzContext* NzContext::GetThreadContext() -{ - EnsureContext(); - - return threadContext; -} - -bool NzContext::Initialize() -{ - NzContextParameters parameters; -// parameters.compatibilityProfile = true; - parameters.shared = false; // Difficile de partager le contexte de référence avec lui-même - - s_reference = new NzContext; - if (!s_reference->Create(parameters)) - { - delete s_reference; - s_reference = nullptr; - - return false; - } - - // Le contexte de référence doit rester désactivé pour le partage - s_reference->SetActive(false); - - NzContextParameters::defaultShareContext = s_reference; - - return true; -} - -void NzContext::Uninitialize() -{ - for (NzContext* context : contexts) - delete context; - - contexts.clear(); // On supprime tous les contextes créés - - delete s_reference; - s_reference = nullptr; -} - -NzContext* NzContext::s_reference = nullptr; +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include + +#if defined(NAZARA_PLATFORM_WINDOWS) + #include +#elif defined(NAZARA_PLATFORM_LINUX) + #include +#else + #error Lack of implementation: Context +#endif + +#include + +namespace +{ + NAZARA_THREADLOCAL NzContext* currentContext = nullptr; + NAZARA_THREADLOCAL NzContext* threadContext = nullptr; + + std::vector contexts; + + void CALLBACK DebugCallback(unsigned int source, unsigned int type, unsigned int id, unsigned int severity, int length, const char* message, void* userParam) + { + NazaraUnused(length); + + NzStringStream ss; + ss << "OpenGL debug message (ID: 0x" << NzString::Number(id, 16) << "):\n"; + ss << "Sent by context: " << userParam; + ss << "\n-Source: "; + switch (source) + { + case GL_DEBUG_SOURCE_API_ARB: + ss << "OpenGL"; + break; + + case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB: + ss << "Operating system"; + break; + + case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB: + ss << "Shader compiler"; + break; + + case GL_DEBUG_SOURCE_THIRD_PARTY_ARB: + ss << "Shader compiler"; + break; + + case GL_DEBUG_SOURCE_APPLICATION_ARB: + ss << "Application"; + break; + + case GL_DEBUG_SOURCE_OTHER_ARB: + ss << "Other"; + break; + + default: + // Peut être rajouté par une extension + ss << "Unknown"; + break; + } + ss << '\n'; + + ss << "-Type: "; + switch (type) + { + case GL_DEBUG_TYPE_ERROR_ARB: + ss << "Error"; + break; + + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB: + ss << "Deprecated behavior"; + break; + + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB: + ss << "Undefined behavior"; + break; + + case GL_DEBUG_TYPE_PORTABILITY_ARB: + ss << "Portability"; + break; + + case GL_DEBUG_TYPE_PERFORMANCE_ARB: + ss << "Performance"; + break; + + case GL_DEBUG_TYPE_OTHER_ARB: + ss << "Other"; + break; + + default: + // Peut être rajouté par une extension + ss << "Unknown"; + break; + } + ss << '\n'; + + ss << "-Severity: "; + switch (severity) + { + case GL_DEBUG_SEVERITY_HIGH_ARB: + ss << "High"; + break; + + case GL_DEBUG_SEVERITY_MEDIUM_ARB: + ss << "Medium"; + break; + + case GL_DEBUG_SEVERITY_LOW_ARB: + ss << "Low"; + break; + + default: + // Peut être rajouté par une extension + ss << "Unknown"; + break; + } + ss << '\n'; + + ss << "Message: " << message << '\n'; + + NazaraNotice(ss); + } +} + +NzContext::NzContext() : +m_impl(nullptr) +{ +} + +NzContext::~NzContext() +{ + Destroy(); +} + +bool NzContext::Create(const NzContextParameters& parameters) +{ + Destroy(); + + m_parameters = parameters; + if (m_parameters.shared && !m_parameters.shareContext) + m_parameters.shareContext = s_reference; + + m_impl = new NzContextImpl; + if (!m_impl->Create(m_parameters)) + { + NazaraError("Failed to create context implementation"); + delete m_impl; + m_impl = nullptr; + + return false; + } + + if (!m_impl->Activate()) + { + NazaraError("Failed to activate context"); + + m_impl->Destroy(); + delete m_impl; + m_impl = nullptr; + + return false; + } + + if (m_parameters.antialiasingLevel > 0) + glEnable(GL_MULTISAMPLE); + + if (NzOpenGL::IsSupported(nzOpenGLExtension_DebugOutput) && m_parameters.debugMode) + { + glDebugMessageCallback(&DebugCallback, this); + + #ifdef NAZARA_DEBUG + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + #endif + } + + NotifyCreated(); + + return true; +} + +void NzContext::Destroy() +{ + if (m_impl) + { + NotifyDestroy(); + + if (currentContext == this) + NzContextImpl::Desactivate(); + + m_impl->Destroy(); + delete m_impl; + m_impl = nullptr; + } +} + +const NzContextParameters& NzContext::GetParameters() const +{ + #ifdef NAZARA_RENDERER_SAFE + if (!m_impl) + NazaraError("No context has been created"); + #endif + + return m_parameters; +} + +bool NzContext::IsActive() const +{ + #ifdef NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("No context has been created"); + return false; + } + #endif + + return currentContext == this; +} + +bool NzContext::SetActive(bool active) +{ + #ifdef NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("No context has been created"); + return false; + } + #endif + + // Si le contexte est déjà activé/désactivé + if ((currentContext == this) == active) + return true; + + if (active) + { + if (!m_impl->Activate()) + return false; + + currentContext = this; + } + else + { + if (!NzContextImpl::Desactivate()) + return false; + + currentContext = nullptr; + } + + return true; +} + +void NzContext::SwapBuffers() +{ + #ifdef NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("No context has been created"); + return; + } + + if (!m_parameters.doubleBuffered) + { + NazaraError("Context is not double buffered"); + return; + } + #endif + + m_impl->SwapBuffers(); +} + +bool NzContext::EnsureContext() +{ + if (!currentContext) + { + if (!threadContext) + { + NzContext* context = new NzContext; + if (!context->Create()) + { + NazaraError("Failed to create context"); + delete context; + + return false; + } + + contexts.push_back(context); + + threadContext = context; + } + + if (!threadContext->SetActive(true)) + { + NazaraError("Failed to active thread context"); + return false; + } + } + + return true; +} + +NzContext* NzContext::GetCurrent() +{ + return currentContext; +} + +const NzContext* NzContext::GetReference() +{ + return s_reference; +} + +NzContext* NzContext::GetThreadContext() +{ + EnsureContext(); + + return threadContext; +} + +bool NzContext::Initialize() +{ + NzContextParameters parameters; +// parameters.compatibilityProfile = true; + parameters.shared = false; // Difficile de partager le contexte de référence avec lui-même + + s_reference = new NzContext; + if (!s_reference->Create(parameters)) + { + delete s_reference; + s_reference = nullptr; + + return false; + } + + // Le contexte de référence doit rester désactivé pour le partage + s_reference->SetActive(false); + + NzContextParameters::defaultShareContext = s_reference; + + return true; +} + +void NzContext::Uninitialize() +{ + for (NzContext* context : contexts) + delete context; + + contexts.clear(); // On supprime tous les contextes créés + + delete s_reference; + s_reference = nullptr; +} + +NzContext* NzContext::s_reference = nullptr; diff --git a/src/Nazara/Renderer/GLSLShader.cpp b/src/Nazara/Renderer/GLSLShader.cpp index 22ebcc6fe..ecc0e6199 100644 --- a/src/Nazara/Renderer/GLSLShader.cpp +++ b/src/Nazara/Renderer/GLSLShader.cpp @@ -1,578 +1,712 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Renderer module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace -{ - ///FIXME: Déclaré deux fois (ici et dans Renderer.cpp) - const nzUInt8 attribIndex[] = - { - 2, // nzElementUsage_Diffuse - 1, // nzElementUsage_Normal - 0, // nzElementUsage_Position - 3, // nzElementUsage_Tangent - 4 // nzElementUsage_TexCoord - }; - - const GLenum shaderType[nzShaderType_Max+1] = { - GL_FRAGMENT_SHADER, // nzShaderType_Fragment - GL_GEOMETRY_SHADER, // nzShaderType_Geometry - GL_VERTEX_SHADER // nzShaderType_Vertex - }; - - GLuint lockedPrevious = 0; - nzUInt8 lockedLevel = 0; -} - -NzGLSLShader::NzGLSLShader(NzShader* parent) : -m_parent(parent) -{ -} - -NzGLSLShader::~NzGLSLShader() -{ -} - -bool NzGLSLShader::Bind() -{ - #if NAZARA_RENDERER_SAFE - if (lockedLevel > 0) - { - NazaraError("Cannot bind shader while a shader is locked"); - return false; - } - #endif - - #ifdef NAZARA_DEBUG - if (NzContext::GetCurrent() == nullptr) - { - NazaraError("No active context"); - return false; - } - #endif - - glUseProgram(m_program); - - return true; -} - -bool NzGLSLShader::BindTextures() -{ - for (auto it = m_textures.begin(); it != m_textures.end(); ++it) - { - TextureSlot& slot = it->second; - if (!slot.updated) - { - glActiveTexture(GL_TEXTURE0 + slot.unit); - if (!slot.texture->Bind()) - NazaraWarning("Failed to bind texture"); - - slot.updated = true; - } - } - - return true; -} - -bool NzGLSLShader::Compile() -{ - NzContext::EnsureContext(); - - m_idCache.clear(); - m_textures.clear(); - - glLinkProgram(m_program); - - GLint success; - glGetProgramiv(m_program, GL_LINK_STATUS, &success); - - if (success == GL_TRUE) - { - static NzString successStr("Linkage successful"); - m_log = successStr; - - return true; - } - else - { - // On remplit le log avec l'erreur de compilation - GLint length = 0; - glGetProgramiv(m_program, GL_INFO_LOG_LENGTH, &length); - if (length > 1) - { - m_log.Clear(true); - m_log.Reserve(length+19-1); // La taille retournée est celle du buffer (Avec caractère de fin) - m_log.Prepend("Linkage error: "); - m_log.Resize(length+19-1); // Extension du buffer d'écriture pour ajouter le log - - glGetProgramInfoLog(m_program, length-1, nullptr, &m_log[19]); - } - else - m_log = "Linkage failed but no info log found"; - - NazaraError(m_log); - - return false; - } -} - -bool NzGLSLShader::Create() -{ - NzContext::EnsureContext(); - - m_program = glCreateProgram(); - if (!m_program) - { - NazaraError("Failed to create program"); - return false; - } - - glBindAttribLocation(m_program, attribIndex[nzElementUsage_Position], "Position"); - glBindAttribLocation(m_program, attribIndex[nzElementUsage_Normal], "Normal"); - glBindAttribLocation(m_program, attribIndex[nzElementUsage_Diffuse], "Diffuse"); - glBindAttribLocation(m_program, attribIndex[nzElementUsage_Tangent], "Tangent"); - - NzString uniform = "TexCoord"; - unsigned int maxTexCoords = NzRenderer::GetMaxTextureUnits(); - for (unsigned int i = 0; i < maxTexCoords; ++i) - { - NzString uniformName = uniform + NzString::Number(i); - glBindAttribLocation(m_program, attribIndex[nzElementUsage_TexCoord]+i, uniformName.GetConstBuffer()); - } - - for (int i = 0; i <= nzShaderType_Max; ++i) - m_shaders[i] = 0; - - return true; -} - -void NzGLSLShader::Destroy() -{ - NzContext::EnsureContext(); - - for (auto it = m_textures.begin(); it != m_textures.end(); ++it) - it->second.texture->RemoveResourceReference(); - - for (GLuint shader : m_shaders) - if (shader) - glDeleteShader(shader); - - if (m_program) - glDeleteProgram(m_program); -} - -NzString NzGLSLShader::GetLog() const -{ - return m_log; -} - -nzShaderLanguage NzGLSLShader::GetLanguage() const -{ - return nzShaderLanguage_GLSL; -} - -NzString NzGLSLShader::GetSourceCode(nzShaderType type) const -{ - NzContext::EnsureContext(); - - NzString source; - - GLint length; - glGetShaderiv(m_shaders[type], GL_SHADER_SOURCE_LENGTH, &length); - if (length > 1) - { - source.Resize(length-1); // La taille retournée est celle du buffer (Avec caractère de fin) - glGetShaderSource(m_shaders[type], length, nullptr, &source[0]); - } - - return source; -} - -int NzGLSLShader::GetUniformLocation(const NzString& name) const -{ - std::map::const_iterator it = m_idCache.find(name); - GLint id; - if (it == m_idCache.end()) - { - NzContext::EnsureContext(); - - id = glGetUniformLocation(m_program, name.GetConstBuffer()); - m_idCache[name] = id; - } - else - id = it->second; - - return id; -} - -bool NzGLSLShader::IsLoaded(nzShaderType type) const -{ - return m_shaders[type] != 0; -} - -bool NzGLSLShader::Load(nzShaderType type, const NzString& source) -{ - NzContext::EnsureContext(); - - GLuint shader = glCreateShader(shaderType[type]); - if (!shader) - { - m_log = "Failed to create shader object"; - NazaraError(m_log); - - return false; - } - - const char* tmp = source.GetConstBuffer(); - GLint length = source.GetSize(); - glShaderSource(shader, 1, &tmp, &length); - - glCompileShader(shader); - - GLint success; - glGetShaderiv(shader, GL_COMPILE_STATUS, &success); - - if (success == GL_TRUE) - { - glAttachShader(m_program, shader); - m_shaders[type] = shader; - - static NzString successStr("Compilation successful"); - m_log = successStr; - - return true; - } - else - { - // On remplit le log avec l'erreur de compilation - length = 0; - glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length); - if (length > 1) - { - m_log.Clear(true); - m_log.Reserve(length+19-1); // La taille retournée est celle du buffer (Avec caractère de fin) - m_log.Prepend("Compilation error: "); - m_log.Resize(length+19-1); // Extension du buffer d'écriture pour ajouter le log - - glGetShaderInfoLog(shader, length-1, nullptr, &m_log[19]); - } - else - m_log = "Compilation failed but no info log found"; - - NazaraError(m_log); - - glDeleteShader(shader); - - return false; - } -} - -bool NzGLSLShader::Lock() -{ - if (lockedLevel++ == 0) - { - NzContext::EnsureContext(); - - GLint previous; - glGetIntegerv(GL_CURRENT_PROGRAM, &previous); - - lockedPrevious = previous; - - if (lockedPrevious != m_program) - glUseProgram(m_program); - } - - return true; -} - -bool NzGLSLShader::SendBoolean(int location, bool value) -{ - if (glProgramUniform1i) - glProgramUniform1i(m_program, location, value); - else - { - Lock(); - glUniform1i(location, value); - Unlock(); - } - - return true; -} - -bool NzGLSLShader::SendDouble(int location, double value) -{ - if (glProgramUniform1d) - glProgramUniform1d(m_program, location, value); - else - { - Lock(); - glUniform1d(location, value); - Unlock(); - } - - return true; -} - -bool NzGLSLShader::SendFloat(int location, float value) -{ - if (glProgramUniform1f) - glProgramUniform1f(m_program, location, value); - else - { - Lock(); - glUniform1f(location, value); - Unlock(); - } - - return true; -} - -bool NzGLSLShader::SendInteger(int location, int value) -{ - if (glProgramUniform1i) - glProgramUniform1i(m_program, location, value); - else - { - Lock(); - glUniform1i(location, value); - Unlock(); - } - - return true; -} - -bool NzGLSLShader::SendMatrix(int location, const NzMatrix4d& matrix) -{ - if (glProgramUniformMatrix4dv) - glProgramUniformMatrix4dv(m_program, location, 1, GL_FALSE, matrix); - else - { - Lock(); - glUniformMatrix4dv(location, 1, GL_FALSE, matrix); - Unlock(); - } - - return true; -} - -bool NzGLSLShader::SendMatrix(int location, const NzMatrix4f& matrix) -{ - if (glProgramUniformMatrix4fv) - glProgramUniformMatrix4fv(m_program, location, 1, GL_FALSE, matrix); - else - { - Lock(); - glUniformMatrix4fv(location, 1, GL_FALSE, matrix); - Unlock(); - } - - return true; -} - -bool NzGLSLShader::SendTexture(int location, const NzTexture* texture) -{ - auto it = m_textures.find(location); - if (it != m_textures.end()) - { - // Slot déjà utilisé - TextureSlot& slot = it->second; - if (slot.texture != texture) - { - slot.texture->RemoveResourceReference(); - - if (texture) - { - slot.texture = texture; - slot.texture->AddResourceReference(); - - slot.updated = false; - } - else - m_textures.erase(it); // On supprime le slot - } - } - else - { - static const unsigned int maxUnits = NzRenderer::GetMaxTextureUnits(); - - unsigned int unitUsed = m_textures.size(); - if (unitUsed >= maxUnits) - { - NazaraError("Unable to use texture for shader: all available texture units are used"); - return false; - } - - // À partir d'ici nous savons qu'il y a au moins un identifiant de texture libre - nzUInt8 unit; - if (unitUsed == 0) - // Pas d'unité utilisée, la tâche est simple - unit = 0; - else - { - auto it2 = m_textures.rbegin(); // Itérateur vers la fin de la map - unit = it2->second.unit; - if (unit == maxUnits-1) - { - // Il y a une place libre, mais pas à la fin - for (; it2 != m_textures.rend(); ++it2) - { - if (unit - it2->second.unit > 1) // Si l'espace entre les indices est supérieur à 1, alors il y a une place libre - { - unit--; - break; - } - } - } - else - // Il y a une place libre à la fin - unit++; - } - - TextureSlot slot; - slot.unit = unit; - slot.texture = texture; - texture->AddResourceReference(); - - m_textures[location] = slot; - - if (glProgramUniform1i) - glProgramUniform1i(m_program, location, unit); - else - { - Lock(); - glUniform1i(location, unit); - Unlock(); - } - } - - return true; -} - -bool NzGLSLShader::SendVector(int location, const NzVector2d& vector) -{ - if (glProgramUniform2dv) - glProgramUniform2dv(m_program, location, 1, vector); - else - { - Lock(); - glUniform2dv(location, 1, vector); - Unlock(); - } - - return true; -} - -bool NzGLSLShader::SendVector(int location, const NzVector2f& vector) -{ - if (glProgramUniform2fv) - glProgramUniform2fv(m_program, location, 1, vector); - else - { - Lock(); - glUniform2fv(location, 1, vector); - Unlock(); - } - - return true; -} - -bool NzGLSLShader::SendVector(int location, const NzVector3d& vector) -{ - if (glProgramUniform3dv) - glProgramUniform3dv(m_program, location, 1, vector); - else - { - Lock(); - glUniform3dv(location, 1, vector); - Unlock(); - } - - return true; -} - -bool NzGLSLShader::SendVector(int location, const NzVector3f& vector) -{ - if (glProgramUniform3fv) - glProgramUniform3fv(m_program, location, 1, vector); - else - { - Lock(); - glUniform3fv(location, 1, vector); - Unlock(); - } - - return true; -} - -bool NzGLSLShader::SendVector(int location, const NzVector4d& vector) -{ - if (glProgramUniform4dv) - glProgramUniform4dv(m_program, location, 1, vector); - else - { - Lock(); - glUniform4dv(location, 1, vector); - Unlock(); - } - - return true; -} - -bool NzGLSLShader::SendVector(int location, const NzVector4f& vector) -{ - if (glProgramUniform4fv) - glProgramUniform4fv(m_program, location, 1, vector); - else - { - Lock(); - glUniform4fv(location, 1, vector); - Unlock(); - } - - return true; -} - -void NzGLSLShader::Unbind() -{ - #ifdef NAZARA_DEBUG - if (NzContext::GetCurrent() == nullptr) - { - NazaraError("No active context"); - return; - } - #endif - - glUseProgram(0); -} - -void NzGLSLShader::Unlock() -{ - #ifdef NAZARA_DEBUG - if (NzContext::GetCurrent() == nullptr) - { - NazaraError("No active context"); - return; - } - #endif - - #if NAZARA_RENDERER_SAFE - if (lockedLevel == 0) - { - NazaraWarning("Unlock called on non-locked texture"); - return; - } - #endif - - if (--lockedLevel == 0 && lockedPrevious != m_program) - glUseProgram(lockedPrevious); -} +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + GLuint lockedPrevious = 0; + nzUInt8 lockedLevel = 0; +} + +NzGLSLShader::NzGLSLShader(NzShader* parent) : +m_parent(parent) +{ +} + +NzGLSLShader::~NzGLSLShader() +{ + for (auto it = m_textures.begin(); it != m_textures.end(); ++it) + it->second.texture->RemoveResourceListener(this); +} + +bool NzGLSLShader::Bind() +{ + #if NAZARA_RENDERER_SAFE + if (lockedLevel > 0) + { + NazaraError("Cannot bind shader while a shader is locked"); + return false; + } + #endif + + #ifdef NAZARA_DEBUG + if (NzContext::GetCurrent() == nullptr) + { + NazaraError("No active context"); + return false; + } + #endif + + glUseProgram(m_program); + + return true; +} + +bool NzGLSLShader::BindTextures() +{ + for (auto it = m_textures.begin(); it != m_textures.end(); ++it) + { + TextureSlot& slot = it->second; + if (slot.enabled && !slot.updated) + { + glActiveTexture(GL_TEXTURE0 + slot.unit); + if (!slot.texture->Prepare()) + NazaraWarning("Failed to prepare texture"); + + slot.updated = true; + } + } + + return true; +} + +bool NzGLSLShader::Compile() +{ + NzContext::EnsureContext(); + + m_idCache.clear(); + m_textures.clear(); + + glLinkProgram(m_program); + + GLint success; + glGetProgramiv(m_program, GL_LINK_STATUS, &success); + + if (success == GL_TRUE) + { + static NzString successStr("Linkage successful"); + m_log = successStr; + + return true; + } + else + { + // On remplit le log avec l'erreur de compilation + GLint length = 0; + glGetProgramiv(m_program, GL_INFO_LOG_LENGTH, &length); + if (length > 1) + { + m_log.Clear(true); + m_log.Reserve(length+19-1); // La taille retournée est celle du buffer (Avec caractère de fin) + m_log.Prepend("Linkage error: "); + m_log.Resize(length+19-1); // Extension du buffer d'écriture pour ajouter le log + + glGetProgramInfoLog(m_program, length-1, nullptr, &m_log[19]); + } + else + m_log = "Linkage failed but no info log found"; + + NazaraError(m_log); + + return false; + } +} + +bool NzGLSLShader::Create() +{ + NzContext::EnsureContext(); + + m_program = glCreateProgram(); + if (!m_program) + { + NazaraError("Failed to create program"); + return false; + } + + glBindAttribLocation(m_program, NzOpenGL::AttributeIndex[nzElementUsage_Position], "Position"); + glBindAttribLocation(m_program, NzOpenGL::AttributeIndex[nzElementUsage_Normal], "Normal"); + glBindAttribLocation(m_program, NzOpenGL::AttributeIndex[nzElementUsage_Diffuse], "Diffuse"); + glBindAttribLocation(m_program, NzOpenGL::AttributeIndex[nzElementUsage_Tangent], "Tangent"); + + NzString uniform; + + static const unsigned int maxTexCoords = NzRenderer::GetMaxTextureUnits(); + + uniform.Reserve(10); // 8 + 2 + uniform = "TexCoord"; + for (unsigned int i = 0; i < maxTexCoords; ++i) + { + NzString uniformName = uniform + NzString::Number(i); + glBindAttribLocation(m_program, NzOpenGL::AttributeIndex[nzElementUsage_TexCoord]+i, uniformName.GetConstBuffer()); + } + + static const bool mrtSupported = NzRenderer::HasCapability(nzRendererCap_MultipleRenderTargets); + if (mrtSupported) + { + static const unsigned int maxRenderTargets = NzRenderer::GetMaxRenderTargets(); + + uniform.Reserve(14); // 12 + 2 + uniform = "RenderTarget"; + for (unsigned int i = 0; i < maxRenderTargets; ++i) + { + NzString uniformName = uniform + NzString::Number(i); + glBindFragDataLocation(m_program, i, uniformName.GetConstBuffer()); + } + } + + for (int i = 0; i <= nzShaderType_Max; ++i) + m_shaders[i] = 0; + + return true; +} + +void NzGLSLShader::Destroy() +{ + NzContext::EnsureContext(); + + for (auto it = m_textures.begin(); it != m_textures.end(); ++it) + it->second.texture->RemoveResourceReference(); + + for (GLuint shader : m_shaders) + if (shader) + glDeleteShader(shader); + + if (m_program) + glDeleteProgram(m_program); +} + +NzString NzGLSLShader::GetLog() const +{ + return m_log; +} + +nzShaderLanguage NzGLSLShader::GetLanguage() const +{ + return nzShaderLanguage_GLSL; +} + +NzString NzGLSLShader::GetSourceCode(nzShaderType type) const +{ + NzContext::EnsureContext(); + + NzString source; + + GLint length; + glGetShaderiv(m_shaders[type], GL_SHADER_SOURCE_LENGTH, &length); + if (length > 1) + { + source.Resize(length-1); // La taille retournée est celle du buffer (Avec caractère de fin) + glGetShaderSource(m_shaders[type], length, nullptr, &source[0]); + } + + return source; +} + +int NzGLSLShader::GetUniformLocation(const NzString& name) const +{ + std::map::const_iterator it = m_idCache.find(name); + GLint id; + if (it == m_idCache.end()) + { + NzContext::EnsureContext(); + + id = glGetUniformLocation(m_program, name.GetConstBuffer()); + m_idCache[name] = id; + } + else + id = it->second; + + return id; +} + +bool NzGLSLShader::IsLoaded(nzShaderType type) const +{ + return m_shaders[type] != 0; +} + +bool NzGLSLShader::Load(nzShaderType type, const NzString& source) +{ + NzContext::EnsureContext(); + + GLuint shader = glCreateShader(NzOpenGL::ShaderType[type]); + if (!shader) + { + m_log = "Failed to create shader object"; + NazaraError(m_log); + + return false; + } + + const char* tmp = source.GetConstBuffer(); + GLint length = source.GetSize(); + glShaderSource(shader, 1, &tmp, &length); + + glCompileShader(shader); + + GLint success; + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + + if (success == GL_TRUE) + { + glAttachShader(m_program, shader); + m_shaders[type] = shader; + + static NzString successStr("Compilation successful"); + m_log = successStr; + + return true; + } + else + { + // On remplit le log avec l'erreur de compilation + length = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length); + if (length > 1) + { + m_log.Clear(true); + m_log.Reserve(length+19-1); // La taille retournée est celle du buffer (Avec caractère de fin) + m_log.Prepend("Compilation error: "); + m_log.Resize(length+19-1); // Extension du buffer d'écriture pour ajouter le log + + glGetShaderInfoLog(shader, length-1, nullptr, &m_log[19]); + } + else + m_log = "Compilation failed but no info log found"; + + NazaraError(m_log); + + glDeleteShader(shader); + + return false; + } +} + +bool NzGLSLShader::Lock() +{ + if (lockedLevel++ == 0) + { + NzContext::EnsureContext(); + + GLint previous; + glGetIntegerv(GL_CURRENT_PROGRAM, &previous); + + lockedPrevious = previous; + + if (lockedPrevious != m_program) + glUseProgram(m_program); + } + + return true; +} + +bool NzGLSLShader::SendBoolean(int location, bool value) +{ + if (glProgramUniform1i) + glProgramUniform1i(m_program, location, value); + else + { + if (!Lock()) + { + NazaraError("Failed to lock shader"); + return false; + } + + glUniform1i(location, value); + Unlock(); + } + + return true; +} + +bool NzGLSLShader::SendDouble(int location, double value) +{ + if (glProgramUniform1d) + glProgramUniform1d(m_program, location, value); + else + { + if (!Lock()) + { + NazaraError("Failed to lock shader"); + return false; + } + + glUniform1d(location, value); + Unlock(); + } + + return true; +} + +bool NzGLSLShader::SendFloat(int location, float value) +{ + if (glProgramUniform1f) + glProgramUniform1f(m_program, location, value); + else + { + if (!Lock()) + { + NazaraError("Failed to lock shader"); + return false; + } + + glUniform1f(location, value); + Unlock(); + } + + return true; +} + +bool NzGLSLShader::SendInteger(int location, int value) +{ + if (glProgramUniform1i) + glProgramUniform1i(m_program, location, value); + else + { + if (!Lock()) + { + NazaraError("Failed to lock shader"); + return false; + } + + glUniform1i(location, value); + Unlock(); + } + + return true; +} + +bool NzGLSLShader::SendMatrix(int location, const NzMatrix4d& matrix) +{ + if (glProgramUniformMatrix4dv) + glProgramUniformMatrix4dv(m_program, location, 1, GL_FALSE, matrix); + else + { + if (!Lock()) + { + NazaraError("Failed to lock shader"); + return false; + } + + glUniformMatrix4dv(location, 1, GL_FALSE, matrix); + Unlock(); + } + + return true; +} + +bool NzGLSLShader::SendMatrix(int location, const NzMatrix4f& matrix) +{ + if (glProgramUniformMatrix4fv) + glProgramUniformMatrix4fv(m_program, location, 1, GL_FALSE, matrix); + else + { + if (!Lock()) + { + NazaraError("Failed to lock shader"); + return false; + } + + glUniformMatrix4fv(location, 1, GL_FALSE, matrix); + Unlock(); + } + + return true; +} + +bool NzGLSLShader::SendTexture(int location, const NzTexture* texture) +{ + auto it = m_textures.find(location); + if (it != m_textures.end()) + { + // Slot déjà utilisé + TextureSlot& slot = it->second; + if (slot.texture != texture) + { + slot.texture->RemoveResourceListener(this); + + if (texture) + { + slot.texture = texture; + slot.texture->AddResourceListener(this, location); + + slot.updated = false; + } + else + m_textures.erase(it); // On supprime le slot + } + } + else + { + static const unsigned int maxUnits = NzRenderer::GetMaxTextureUnits(); + + unsigned int unitUsed = m_textures.size(); + if (unitUsed >= maxUnits) + { + NazaraError("Unable to use texture for shader: all available texture units are used"); + return false; + } + + // À partir d'ici nous savons qu'il y a au moins un identifiant de texture libre + nzUInt8 unit; + if (unitUsed == 0) + // Pas d'unité utilisée, la tâche est simple + unit = 0; + else + { + auto it2 = m_textures.rbegin(); // Itérateur vers la fin de la map + unit = it2->second.unit; + if (unit == maxUnits-1) + { + // Il y a une place libre, mais pas à la fin + for (; it2 != m_textures.rend(); ++it2) + { + if (unit - it2->second.unit > 1) // Si l'espace entre les indices est supérieur à 1, alors il y a une place libre + { + unit--; + break; + } + } + } + else + // Il y a une place libre à la fin + unit++; + } + + TextureSlot slot; + slot.enabled = texture->IsValid(); + slot.unit = unit; + slot.texture = texture; + texture->AddResourceListener(this, location); + + m_textures[location] = slot; + + if (slot.enabled) + { + if (glProgramUniform1i) + glProgramUniform1i(m_program, location, unit); + else + { + if (!Lock()) + { + NazaraError("Failed to lock shader"); + return false; + } + + glUniform1i(location, unit); + Unlock(); + } + } + } + + return true; +} + +bool NzGLSLShader::SendVector(int location, const NzVector2d& vector) +{ + if (glProgramUniform2dv) + glProgramUniform2dv(m_program, location, 1, vector); + else + { + if (!Lock()) + { + NazaraError("Failed to lock shader"); + return false; + } + + glUniform2dv(location, 1, vector); + Unlock(); + } + + return true; +} + +bool NzGLSLShader::SendVector(int location, const NzVector2f& vector) +{ + if (glProgramUniform2fv) + glProgramUniform2fv(m_program, location, 1, vector); + else + { + if (!Lock()) + { + NazaraError("Failed to lock shader"); + return false; + } + + glUniform2fv(location, 1, vector); + Unlock(); + } + + return true; +} + +bool NzGLSLShader::SendVector(int location, const NzVector3d& vector) +{ + if (glProgramUniform3dv) + glProgramUniform3dv(m_program, location, 1, vector); + else + { + if (!Lock()) + { + NazaraError("Failed to lock shader"); + return false; + } + + glUniform3dv(location, 1, vector); + Unlock(); + } + + return true; +} + +bool NzGLSLShader::SendVector(int location, const NzVector3f& vector) +{ + if (glProgramUniform3fv) + glProgramUniform3fv(m_program, location, 1, vector); + else + { + if (!Lock()) + { + NazaraError("Failed to lock shader"); + return false; + } + + glUniform3fv(location, 1, vector); + Unlock(); + } + + return true; +} + +bool NzGLSLShader::SendVector(int location, const NzVector4d& vector) +{ + if (glProgramUniform4dv) + glProgramUniform4dv(m_program, location, 1, vector); + else + { + if (!Lock()) + { + NazaraError("Failed to lock shader"); + return false; + } + + glUniform4dv(location, 1, vector); + Unlock(); + } + + return true; +} + +bool NzGLSLShader::SendVector(int location, const NzVector4f& vector) +{ + if (glProgramUniform4fv) + glProgramUniform4fv(m_program, location, 1, vector); + else + { + if (!Lock()) + { + NazaraError("Failed to lock shader"); + return false; + } + + glUniform4fv(location, 1, vector); + Unlock(); + } + + return true; +} + +void NzGLSLShader::Unbind() +{ + #ifdef NAZARA_DEBUG + if (NzContext::GetCurrent() == nullptr) + { + NazaraError("No active context"); + return; + } + #endif + + glUseProgram(0); +} + +void NzGLSLShader::Unlock() +{ + #ifdef NAZARA_DEBUG + if (NzContext::GetCurrent() == nullptr) + { + NazaraError("No active context"); + return; + } + #endif + + #if NAZARA_RENDERER_SAFE + if (lockedLevel == 0) + { + NazaraWarning("Unlock called on non-locked texture"); + return; + } + #endif + + if (--lockedLevel == 0 && lockedPrevious != m_program) + glUseProgram(lockedPrevious); +} + +void NzGLSLShader::OnResourceCreated(const NzResource* resource, int index) +{ + NazaraUnused(resource); + + auto it = m_textures.find(index); + + #ifdef NAZARA_DEBUG + if (it == m_textures.end()) + { + NazaraInternalError("Invalid index (" + NzString::Number(index) + ')'); + return; + } + #endif + + TextureSlot& slot = it->second; + + #ifdef NAZARA_DEBUG + if (slot.texture != resource) + { + NazaraInternalError("Wrong texture at location #" + NzString::Number(index)); + return; + } + #endif + + slot.enabled = true; + slot.updated = false; +} + +void NzGLSLShader::OnResourceDestroy(const NzResource* resource, int index) +{ + NazaraUnused(resource); + + auto it = m_textures.find(index); + + #ifdef NAZARA_DEBUG + if (it == m_textures.end()) + { + NazaraInternalError("Invalid index (" + NzString::Number(index) + ')'); + return; + } + #endif + + TextureSlot& slot = it->second; + + #ifdef NAZARA_DEBUG + if (slot.texture != resource) + { + NazaraInternalError("Wrong texture at location #" + NzString::Number(index)); + return; + } + #endif + + slot.enabled = false; +} + +void NzGLSLShader::OnResourceReleased(const NzResource* resource, int index) +{ + if (m_textures.erase(index) == 0) + NazaraInternalError("Texture " + NzString::Pointer(resource) + " not found"); +} diff --git a/src/Nazara/Renderer/GLSLShader.hpp b/src/Nazara/Renderer/GLSLShader.hpp index 4cfb4d269..33ce45aff 100644 --- a/src/Nazara/Renderer/GLSLShader.hpp +++ b/src/Nazara/Renderer/GLSLShader.hpp @@ -1,73 +1,81 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Renderer module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#pragma once - -#ifndef NAZARA_GLSLSHADER_HPP -#define NAZARA_GLSLSHADER_HPP - -#include -#include -#include -#include -#include - -class NzGLSLShader : public NzShaderImpl -{ - public: - NzGLSLShader(NzShader* parent); - ~NzGLSLShader(); - - bool Bind(); - bool BindTextures(); - - bool Compile(); - - bool Create(); - void Destroy(); - - NzString GetLog() const; - nzShaderLanguage GetLanguage() const; - NzString GetSourceCode(nzShaderType type) const; - int GetUniformLocation(const NzString& name) const; - - bool IsLoaded(nzShaderType type) const; - - bool Load(nzShaderType type, const NzString& source); - bool Lock(); - - bool SendBoolean(int location, bool value); - bool SendDouble(int location, double value); - bool SendFloat(int location, float value); - bool SendInteger(int location, int value); - bool SendMatrix(int location, const NzMatrix4d& matrix); - bool SendMatrix(int location, const NzMatrix4f& matrix); - bool SendTexture(int location, const NzTexture* texture); - bool SendVector(int location, const NzVector2d& vector); - bool SendVector(int location, const NzVector2f& vector); - bool SendVector(int location, const NzVector3d& vector); - bool SendVector(int location, const NzVector3f& vector); - bool SendVector(int location, const NzVector4d& vector); - bool SendVector(int location, const NzVector4f& vector); - - void Unbind(); - void Unlock(); - - private: - struct TextureSlot - { - bool updated = false; - nzUInt8 unit; - const NzTexture* texture; - }; - - mutable std::map m_idCache; - std::map m_textures; - GLuint m_program; - GLuint m_shaders[nzShaderType_Max+1]; - NzShader* m_parent; - NzString m_log; -}; - -#endif // NAZARA_GLSLSHADER_HPPs +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_GLSLSHADER_HPP +#define NAZARA_GLSLSHADER_HPP + +#include +#include +#include +#include +#include +#include + +class NzResource; + +class NzGLSLShader : public NzShaderImpl, NzResourceListener +{ + public: + NzGLSLShader(NzShader* parent); + ~NzGLSLShader(); + + bool Bind(); + bool BindTextures(); + + bool Compile(); + + bool Create(); + void Destroy(); + + NzString GetLog() const; + nzShaderLanguage GetLanguage() const; + NzString GetSourceCode(nzShaderType type) const; + int GetUniformLocation(const NzString& name) const; + + bool IsLoaded(nzShaderType type) const; + + bool Load(nzShaderType type, const NzString& source); + bool Lock(); + + bool SendBoolean(int location, bool value); + bool SendDouble(int location, double value); + bool SendFloat(int location, float value); + bool SendInteger(int location, int value); + bool SendMatrix(int location, const NzMatrix4d& matrix); + bool SendMatrix(int location, const NzMatrix4f& matrix); + bool SendTexture(int location, const NzTexture* texture); + bool SendVector(int location, const NzVector2d& vector); + bool SendVector(int location, const NzVector2f& vector); + bool SendVector(int location, const NzVector3d& vector); + bool SendVector(int location, const NzVector3f& vector); + bool SendVector(int location, const NzVector4d& vector); + bool SendVector(int location, const NzVector4f& vector); + + void Unbind(); + void Unlock(); + + private: + void OnResourceCreated(const NzResource* resource, int index) override; + void OnResourceDestroy(const NzResource* resource, int index) override; + void OnResourceReleased(const NzResource* resource, int index) override; + + struct TextureSlot + { + bool enabled; + bool updated = false; + nzUInt8 unit; + const NzTexture* texture; + }; + + mutable std::map m_idCache; + std::map m_textures; + GLuint m_program; + GLuint m_shaders[nzShaderType_Max+1]; + NzShader* m_parent; + NzString m_log; +}; + +#endif // NAZARA_GLSLSHADER_HPPs diff --git a/src/Nazara/Renderer/HardwareBuffer.cpp b/src/Nazara/Renderer/HardwareBuffer.cpp index ce3f2c7e7..404382c7d 100644 --- a/src/Nazara/Renderer/HardwareBuffer.cpp +++ b/src/Nazara/Renderer/HardwareBuffer.cpp @@ -1,250 +1,218 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Renderer module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include -#include - -namespace -{ - GLenum bufferLock[] = { - GL_WRITE_ONLY, // nzBufferAccess_DiscardAndWrite - GL_READ_ONLY, // nzBufferAccess_ReadOnly - GL_READ_WRITE, // nzBufferAccess_ReadWrite - GL_WRITE_ONLY // nzBufferAccess_WriteOnly - }; - - GLenum bufferLockRange[] = { - GL_MAP_INVALIDATE_BUFFER_BIT | GL_MAP_WRITE_BIT, // nzBufferAccess_DiscardAndWrite - GL_MAP_READ_BIT, // nzBufferAccess_ReadOnly - GL_MAP_READ_BIT | GL_MAP_WRITE_BIT, // nzBufferAccess_ReadWrite - GL_MAP_WRITE_BIT // nzBufferAccess_WriteOnly - }; - - GLenum bufferTarget[] = { - GL_ELEMENT_ARRAY_BUFFER, // nzBufferType_Index, - GL_ARRAY_BUFFER, // nzBufferType_Vertex - }; - - GLenum bufferTargetBinding[] = { - GL_ELEMENT_ARRAY_BUFFER_BINDING, // nzBufferType_Index, - GL_ARRAY_BUFFER_BINDING, // nzBufferType_Vertex - }; - - GLenum bufferUsage[] = { - // J'ai choisi DYNAMIC à la place de STREAM car DYNAMIC semble plus adapté au profil "une mise à jour pour quelques rendus" - // Ce qui est je pense le scénario qui arrivera le plus souvent (Prévoir une option pour permettre d'utiliser le STREAM_DRAW ?) - // Source: http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=160839 - GL_DYNAMIC_DRAW, // nzBufferUsage_Dynamic - GL_STATIC_DRAW // nzBufferUsage_Static - }; - - using LockRoutine = nzUInt8* (*)(nzBufferType type, nzBufferAccess access, unsigned int offset, unsigned int size); - - nzUInt8* LockBuffer(nzBufferType type, nzBufferAccess access, unsigned int offset, unsigned int size) - { - NazaraUnused(size); - - if (access == nzBufferAccess_DiscardAndWrite) - { - GLint bufSize; - glGetBufferParameteriv(bufferTargetBinding[type], GL_BUFFER_SIZE, &bufSize); - - GLint bufUsage; - glGetBufferParameteriv(bufferTargetBinding[type], GL_BUFFER_USAGE, &bufUsage); - - // On discard le buffer - glBufferData(bufferTargetBinding[type], bufSize, nullptr, bufUsage); - } - - void* ptr = glMapBuffer(bufferTarget[type], bufferLock[access]); - if (ptr) - return reinterpret_cast(ptr) + offset; - else - return nullptr; - } - - nzUInt8* LockBufferRange(nzBufferType type, nzBufferAccess access, unsigned int offset, unsigned int size) - { - return reinterpret_cast(glMapBufferRange(bufferTarget[type], offset, size, bufferLockRange[access])); - } - - nzUInt8* LockBufferFirstRun(nzBufferType type, nzBufferAccess access, unsigned int offset, unsigned int size); - - LockRoutine mapBuffer = LockBufferFirstRun; - - nzUInt8* LockBufferFirstRun(nzBufferType type, nzBufferAccess access, unsigned int offset, unsigned int size) - { - if (glMapBufferRange) - mapBuffer = LockBufferRange; - else - mapBuffer = LockBuffer; - - return mapBuffer(type, access, offset, size); - } -} - -NzHardwareBuffer::NzHardwareBuffer(NzBuffer* parent, nzBufferType type) : -m_type(type), -m_parent(parent) -{ -} - -NzHardwareBuffer::~NzHardwareBuffer() -{ -} - -void NzHardwareBuffer::Bind() -{ - #ifdef NAZARA_DEBUG - if (NzContext::GetCurrent() == nullptr) - { - NazaraError("No active context"); - return; - } - #endif - - glBindBuffer(bufferTarget[m_type], m_buffer); -} - -bool NzHardwareBuffer::Create(unsigned int size, nzBufferUsage usage) -{ - NzContext::EnsureContext(); - - m_buffer = 0; - glGenBuffers(1, &m_buffer); - - GLint previous; - glGetIntegerv(bufferTargetBinding[m_type], &previous); - - glBindBuffer(bufferTarget[m_type], m_buffer); - glBufferData(bufferTarget[m_type], size, nullptr, bufferUsage[usage]); - - // Pour ne pas perturber le rendu, on interfère pas avec le binding déjà présent - if (previous != 0) - glBindBuffer(bufferTarget[m_type], previous); - - return true; -} - -void NzHardwareBuffer::Destroy() -{ - NzContext::EnsureContext(); - - glDeleteBuffers(1, &m_buffer); -} - -bool NzHardwareBuffer::Fill(const void* data, unsigned int offset, unsigned int size) -{ - NzContext::EnsureContext(); - - GLuint previous; - glGetIntegerv(bufferTargetBinding[m_type], reinterpret_cast(&previous)); - - if (previous != m_buffer) - glBindBuffer(bufferTarget[m_type], m_buffer); - - // Il semblerait que glBuffer(Sub)Data soit plus performant que glMapBuffer(Range) en dessous d'un certain seuil - // http://www.stevestreeting.com/2007/03/17/glmapbuffer-vs-glbuffersubdata-the-return/ - if (size < 32*1024) - { - // http://www.opengl.org/wiki/Vertex_Specification_Best_Practices - if (size == m_parent->GetSize()) - glBufferData(bufferTarget[m_type], m_parent->GetSize(), nullptr, bufferUsage[m_parent->GetUsage()]); // Discard - - glBufferSubData(bufferTarget[m_type], offset, size, data); - } - else - { - nzUInt8* ptr = mapBuffer(m_type, (size == m_parent->GetSize()) ? nzBufferAccess_DiscardAndWrite : nzBufferAccess_WriteOnly, offset, size); - if (!ptr) - { - NazaraError("Failed to map buffer"); - return false; - } - - std::memcpy(ptr, data, size); - - if (glUnmapBuffer(bufferTarget[m_type]) != GL_TRUE) - { - // Une erreur rare est survenue, nous devons réinitialiser le buffer - NazaraError("Failed to unmap buffer, reinitialising content... (OpenGL error : 0x" + NzString::Number(glGetError(), 16) + ')'); - - glBufferData(bufferTarget[m_type], m_parent->GetSize(), nullptr, bufferUsage[m_parent->GetStorage()]); - - return false; - } - } - - // Inutile de rebinder s'il n'y avait aucun buffer (Optimise les opérations chaînées) - if (previous != m_buffer && previous != 0) - glBindBuffer(bufferTarget[m_type], previous); - - return true; -} - -void* NzHardwareBuffer::GetPointer() -{ - return nullptr; -} - -bool NzHardwareBuffer::IsHardware() const -{ - return true; -} - -void* NzHardwareBuffer::Map(nzBufferAccess access, unsigned int offset, unsigned int size) -{ - NzContext::EnsureContext(); - - // Pour ne pas perturber le rendu, on interfère pas avec le binding déjà présent - GLuint previous; - glGetIntegerv(bufferTargetBinding[m_type], reinterpret_cast(&previous)); - - if (previous != m_buffer) - glBindBuffer(bufferTarget[m_type], m_buffer); - - void* ptr = mapBuffer(m_type, access, offset, size); - - // Inutile de rebinder s'il n'y avait aucun buffer (Optimise les opérrations chaînées) - if (previous != m_buffer && previous != 0) - glBindBuffer(bufferTarget[m_type], previous); - - return ptr; -} - -bool NzHardwareBuffer::Unmap() -{ - NzContext::EnsureContext(); - - GLuint previous; - glGetIntegerv(bufferTargetBinding[m_type], reinterpret_cast(&previous)); - - if (previous != m_buffer) - glBindBuffer(bufferTarget[m_type], m_buffer); - - if (glUnmapBuffer(bufferTarget[m_type]) != GL_TRUE) - { - // Une erreur rare est survenue, nous devons réinitialiser le buffer - NazaraError("Failed to unmap buffer, reinitialising content... (OpenGL error : 0x" + NzString::Number(glGetError(), 16) + ')'); - - glBufferData(bufferTarget[m_type], m_parent->GetSize(), nullptr, bufferUsage[m_parent->GetStorage()]); - - // Inutile de rebinder s'il n'y avait aucun buffer (Optimise les opérations chaînées) - if (previous != m_buffer && previous != 0) - glBindBuffer(bufferTarget[m_type], previous); - - return false; - } - - // Inutile de rebinder s'il n'y avait aucun buffer (Optimise les opérations chaînées) - if (previous != m_buffer && previous != 0) - glBindBuffer(bufferTarget[m_type], previous); - - return true; -} +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + using LockRoutine = nzUInt8* (*)(nzBufferType type, nzBufferAccess access, unsigned int offset, unsigned int size); + + nzUInt8* LockBuffer(nzBufferType type, nzBufferAccess access, unsigned int offset, unsigned int size) + { + NazaraUnused(size); + + if (access == nzBufferAccess_DiscardAndWrite) + { + GLint bufSize; + glGetBufferParameteriv(NzOpenGL::BufferTargetBinding[type], GL_BUFFER_SIZE, &bufSize); + + GLint bufUsage; + glGetBufferParameteriv(NzOpenGL::BufferTargetBinding[type], GL_BUFFER_USAGE, &bufUsage); + + // On discard le buffer + glBufferData(NzOpenGL::BufferTargetBinding[type], bufSize, nullptr, bufUsage); + } + + void* ptr = glMapBuffer(NzOpenGL::BufferTarget[type], NzOpenGL::BufferLock[access]); + if (ptr) + return reinterpret_cast(ptr) + offset; + else + return nullptr; + } + + nzUInt8* LockBufferRange(nzBufferType type, nzBufferAccess access, unsigned int offset, unsigned int size) + { + return reinterpret_cast(glMapBufferRange(NzOpenGL::BufferTarget[type], offset, size, NzOpenGL::BufferLockRange[access])); + } + + nzUInt8* LockBufferFirstRun(nzBufferType type, nzBufferAccess access, unsigned int offset, unsigned int size); + + LockRoutine mapBuffer = LockBufferFirstRun; + + nzUInt8* LockBufferFirstRun(nzBufferType type, nzBufferAccess access, unsigned int offset, unsigned int size) + { + if (glMapBufferRange) + mapBuffer = LockBufferRange; + else + mapBuffer = LockBuffer; + + return mapBuffer(type, access, offset, size); + } +} + +NzHardwareBuffer::NzHardwareBuffer(NzBuffer* parent, nzBufferType type) : +m_type(type), +m_parent(parent) +{ +} + +NzHardwareBuffer::~NzHardwareBuffer() +{ +} + +void NzHardwareBuffer::Bind() +{ + #ifdef NAZARA_DEBUG + if (NzContext::GetCurrent() == nullptr) + { + NazaraError("No active context"); + return; + } + #endif + + glBindBuffer(NzOpenGL::BufferTarget[m_type], m_buffer); +} + +bool NzHardwareBuffer::Create(unsigned int size, nzBufferUsage usage) +{ + NzContext::EnsureContext(); + + m_buffer = 0; + glGenBuffers(1, &m_buffer); + + GLint previous; + glGetIntegerv(NzOpenGL::BufferTargetBinding[m_type], &previous); + + glBindBuffer(NzOpenGL::BufferTarget[m_type], m_buffer); + glBufferData(NzOpenGL::BufferTarget[m_type], size, nullptr, NzOpenGL::BufferUsage[usage]); + + // Pour ne pas perturber le rendu, on interfère pas avec le binding déjà présent + if (previous != 0) + glBindBuffer(NzOpenGL::BufferTarget[m_type], previous); + + return true; +} + +void NzHardwareBuffer::Destroy() +{ + NzContext::EnsureContext(); + + glDeleteBuffers(1, &m_buffer); +} + +bool NzHardwareBuffer::Fill(const void* data, unsigned int offset, unsigned int size) +{ + NzContext::EnsureContext(); + + GLuint previous; + glGetIntegerv(NzOpenGL::BufferTargetBinding[m_type], reinterpret_cast(&previous)); + + if (previous != m_buffer) + glBindBuffer(NzOpenGL::BufferTarget[m_type], m_buffer); + + // Il semblerait que glBuffer(Sub)Data soit plus performant que glMapBuffer(Range) en dessous d'un certain seuil + // http://www.stevestreeting.com/2007/03/17/glmapbuffer-vs-glbuffersubdata-the-return/ + if (size < 32*1024) + { + // http://www.opengl.org/wiki/Vertex_Specification_Best_Practices + if (size == m_parent->GetSize()) + glBufferData(NzOpenGL::BufferTarget[m_type], m_parent->GetSize(), nullptr, NzOpenGL::BufferUsage[m_parent->GetUsage()]); // Discard + + glBufferSubData(NzOpenGL::BufferTarget[m_type], offset, size, data); + } + else + { + nzUInt8* ptr = mapBuffer(m_type, (size == m_parent->GetSize()) ? nzBufferAccess_DiscardAndWrite : nzBufferAccess_WriteOnly, offset, size); + if (!ptr) + { + NazaraError("Failed to map buffer"); + return false; + } + + std::memcpy(ptr, data, size); + + if (glUnmapBuffer(NzOpenGL::BufferTarget[m_type]) != GL_TRUE) + { + // Une erreur rare est survenue, nous devons réinitialiser le buffer + NazaraError("Failed to unmap buffer, reinitialising content... (OpenGL error : 0x" + NzString::Number(glGetError(), 16) + ')'); + + glBufferData(NzOpenGL::BufferTarget[m_type], m_parent->GetSize(), nullptr, NzOpenGL::BufferUsage[m_parent->GetStorage()]); + + return false; + } + } + + // Inutile de rebinder s'il n'y avait aucun buffer (Optimise les opérations chaînées) + if (previous != m_buffer && previous != 0) + glBindBuffer(NzOpenGL::BufferTarget[m_type], previous); + + return true; +} + +void* NzHardwareBuffer::GetPointer() +{ + return nullptr; +} + +bool NzHardwareBuffer::IsHardware() const +{ + return true; +} + +void* NzHardwareBuffer::Map(nzBufferAccess access, unsigned int offset, unsigned int size) +{ + NzContext::EnsureContext(); + + // Pour ne pas perturber le rendu, on interfère pas avec le binding déjà présent + GLuint previous; + glGetIntegerv(NzOpenGL::BufferTargetBinding[m_type], reinterpret_cast(&previous)); + + if (previous != m_buffer) + glBindBuffer(NzOpenGL::BufferTarget[m_type], m_buffer); + + void* ptr = mapBuffer(m_type, access, offset, size); + + // Inutile de rebinder s'il n'y avait aucun buffer (Optimise les opérrations chaînées) + if (previous != m_buffer && previous != 0) + glBindBuffer(NzOpenGL::BufferTarget[m_type], previous); + + return ptr; +} + +bool NzHardwareBuffer::Unmap() +{ + NzContext::EnsureContext(); + + GLuint previous; + glGetIntegerv(NzOpenGL::BufferTargetBinding[m_type], reinterpret_cast(&previous)); + + if (previous != m_buffer) + glBindBuffer(NzOpenGL::BufferTarget[m_type], m_buffer); + + if (glUnmapBuffer(NzOpenGL::BufferTarget[m_type]) != GL_TRUE) + { + // Une erreur rare est survenue, nous devons réinitialiser le buffer + NazaraError("Failed to unmap buffer, reinitialising content... (OpenGL error : 0x" + NzString::Number(glGetError(), 16) + ')'); + + glBufferData(NzOpenGL::BufferTarget[m_type], m_parent->GetSize(), nullptr, NzOpenGL::BufferUsage[m_parent->GetStorage()]); + + // Inutile de rebinder s'il n'y avait aucun buffer (Optimise les opérations chaînées) + if (previous != m_buffer && previous != 0) + glBindBuffer(NzOpenGL::BufferTarget[m_type], previous); + + return false; + } + + // Inutile de rebinder s'il n'y avait aucun buffer (Optimise les opérations chaînées) + if (previous != m_buffer && previous != 0) + glBindBuffer(NzOpenGL::BufferTarget[m_type], previous); + + return true; +} diff --git a/src/Nazara/Renderer/OcclusionQuery.cpp b/src/Nazara/Renderer/OcclusionQuery.cpp index de2c7a660..228caab8f 100644 --- a/src/Nazara/Renderer/OcclusionQuery.cpp +++ b/src/Nazara/Renderer/OcclusionQuery.cpp @@ -1,102 +1,98 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Renderer module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include -#include -#include - -NzOcclusionQuery::NzOcclusionQuery() : -m_id(0) -{ - #if NAZARA_RENDERER_SAFE - if (IsSupported()) - { - #endif - NzContext::EnsureContext(); - - glGenQueries(1, reinterpret_cast(&m_id)); - #if NAZARA_RENDERER_SAFE - } - else - { - NazaraError("Occlusion queries not supported"); - return; - } - #endif - - #ifdef NAZARA_DEBUG - if (!m_id) - { - NazaraError("Failed to create occlusion query"); - throw std::runtime_error("Constructor failed"); - } - #endif -} - -NzOcclusionQuery::~NzOcclusionQuery() -{ - if (m_id) - { - NzContext::EnsureContext(); - - GLuint query = static_cast(m_id); - glDeleteQueries(1, &query); - } -} - -void NzOcclusionQuery::Begin() -{ - #ifdef NAZARA_DEBUG - if (NzContext::GetCurrent() == nullptr) - { - NazaraError("No active context"); - return; - } - #endif - - glBeginQuery(GL_SAMPLES_PASSED, m_id); -} - -void NzOcclusionQuery::End() -{ - #ifdef NAZARA_DEBUG - if (NzContext::GetCurrent() == nullptr) - { - NazaraError("No active context"); - return; - } - #endif - - glEndQuery(GL_SAMPLES_PASSED); -} - -unsigned int NzOcclusionQuery::GetResult() const -{ - NzContext::EnsureContext(); - - GLuint result; - glGetQueryObjectuiv(m_id, GL_QUERY_RESULT, &result); - - return result; -} - -bool NzOcclusionQuery::IsResultAvailable() const -{ - NzContext::EnsureContext(); - - GLint available; - glGetQueryObjectiv(m_id, GL_QUERY_RESULT_AVAILABLE, &available); - - return available == GL_TRUE; -} - -bool NzOcclusionQuery::IsSupported() -{ - return NzRenderer::HasCapability(nzRendererCap_OcclusionQuery); -} +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include +#include + +NzOcclusionQuery::NzOcclusionQuery() : +m_id(0) +{ + if (IsSupported()) + { + NzContext::EnsureContext(); + + glGenQueries(1, reinterpret_cast(&m_id)); + } + else + { + NazaraError("Occlusion queries not supported"); + return; + } + + #ifdef NAZARA_DEBUG + if (!m_id) + { + NazaraError("Failed to create occlusion query"); + throw std::runtime_error("Constructor failed"); + } + #endif +} + +NzOcclusionQuery::~NzOcclusionQuery() +{ + if (m_id) + { + NzContext::EnsureContext(); + + GLuint query = static_cast(m_id); + glDeleteQueries(1, &query); + } +} + +void NzOcclusionQuery::Begin() +{ + #ifdef NAZARA_DEBUG + if (NzContext::GetCurrent() == nullptr) + { + NazaraError("No active context"); + return; + } + #endif + + glBeginQuery(GL_SAMPLES_PASSED, m_id); +} + +void NzOcclusionQuery::End() +{ + #ifdef NAZARA_DEBUG + if (NzContext::GetCurrent() == nullptr) + { + NazaraError("No active context"); + return; + } + #endif + + glEndQuery(GL_SAMPLES_PASSED); +} + +unsigned int NzOcclusionQuery::GetResult() const +{ + NzContext::EnsureContext(); + + GLuint result; + glGetQueryObjectuiv(m_id, GL_QUERY_RESULT, &result); + + return result; +} + +bool NzOcclusionQuery::IsResultAvailable() const +{ + NzContext::EnsureContext(); + + GLint available; + glGetQueryObjectiv(m_id, GL_QUERY_RESULT_AVAILABLE, &available); + + return available == GL_TRUE; +} + +bool NzOcclusionQuery::IsSupported() +{ + return NzRenderer::HasCapability(nzRendererCap_OcclusionQuery); +} diff --git a/src/Nazara/Renderer/OpenGL.cpp b/src/Nazara/Renderer/OpenGL.cpp index 3871b7169..452db653b 100644 --- a/src/Nazara/Renderer/OpenGL.cpp +++ b/src/Nazara/Renderer/OpenGL.cpp @@ -1,703 +1,1014 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Renderer module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include -#include - -namespace -{ - #ifdef NAZARA_PLATFORM_WINDOWS - HMODULE openGLlibrary; - #endif - - NzOpenGLFunc LoadEntry(const char* name, bool launchException = true) - { - #if defined(NAZARA_PLATFORM_WINDOWS) - NzOpenGLFunc entry = reinterpret_cast(wglGetProcAddress(name)); - if (!entry) // wglGetProcAddress ne fonctionne pas sur les fonctions OpenGL <= 1.1 - entry = reinterpret_cast(GetProcAddress(openGLlibrary, name)); - #elif defined(NAZARA_PLATFORM_LINUX) - NzOpenGLFunc entry = reinterpret_cast(glXGetProcAddress(name)); - #else - #error OS not handled - #endif - - if (!entry && launchException) - { - std::ostringstream oss; - oss << "failed to load \"" << name << '"'; - - throw std::runtime_error(oss.str()); - } - - return entry; - } - - bool LoadLibrary() - { - #ifdef NAZARA_PLATFORM_WINDOWS - openGLlibrary = ::LoadLibraryA("opengl32.dll"); - - return openGLlibrary != nullptr; - #else - return true; - #endif - } - - void UnloadLibrary() - { - #ifdef NAZARA_PLATFORM_WINDOWS - FreeLibrary(openGLlibrary); - #endif - } - - std::set openGLextensionSet; - bool openGLextensions[NzOpenGL::Max+1] = {false}; - unsigned int openGLversion = 0; - - bool LoadExtensionsString(const NzString& extensionString) - { - if (extensionString.IsEmpty()) - { - NazaraError("Unable to get extension string"); - return false; - } - - // On peut sûrement faire plus rapide mais comme ça ne se fait qu'une fois et que NzString implémente le COW... - std::vector ext; - extensionString.Split(ext); - - for (std::vector::iterator it = ext.begin(); it != ext.end(); ++it) - openGLextensionSet.insert(*it); - - return true; - } - - bool LoadExtensions3() - { - GLint extensionCount = 0; - glGetIntegerv(GL_NUM_EXTENSIONS, &extensionCount); - - if (extensionCount <= 0) - { - NazaraError("Unable to get extension count"); - return false; - } - - for (int i = 0; i < extensionCount; ++i) - { - NzString extension(reinterpret_cast(glGetStringi(GL_EXTENSIONS, i))); - if (extension.IsEmpty()) - { - NazaraWarning("Unable to get extension #" + NzString::Number(i)); - continue; - } - - openGLextensionSet.insert(extension); - } - - return true; - } -} - -NzOpenGLFunc NzOpenGL::GetEntry(const NzString& entryPoint) -{ - return LoadEntry(entryPoint.GetConstBuffer(), false); -} - -unsigned int NzOpenGL::GetVersion() -{ - return openGLversion; -} - -bool NzOpenGL::Initialize() -{ - if (!LoadLibrary()) - { - NazaraError("Failed to load OpenGL library"); - return false; - } - - // Le chargement des fonctions OpenGL nécessite un contexte OpenGL - NzContextParameters parameters; - parameters.majorVersion = 2; - parameters.minorVersion = 0; - parameters.shared = false; - - /* - Note: Même le contexte de chargement nécessite quelques fonctions de base pour correctement s'initialiser - Pour cette raison, deux contextes sont créés, le premier sert à récupérer les fonctions permetttant - de créer le second avec les bons paramètres. - - Non sérieusement si quelqu'un a une meilleure idée qu'il me le dise - */ - - /****************************************Initialisation****************************************/ - - NzContext loadContext; - if (!loadContext.Create(parameters)) - { - NazaraError("Failed to create load context"); - Uninitialize(); - - return false; - } - - #if defined(NAZARA_PLATFORM_WINDOWS) - wglCreateContextAttribs = reinterpret_cast(LoadEntry("wglCreateContextAttribsARB", false)); - wglChoosePixelFormat = reinterpret_cast(LoadEntry("wglChoosePixelFormatARB", false)); - if (!wglChoosePixelFormat) - wglChoosePixelFormat = reinterpret_cast(LoadEntry("wglChoosePixelFormatEXT", false)); - #elif defined(NAZARA_PLATFORM_LINUX) - glXCreateContextAttribs = reinterpret_cast(LoadEntry("glXCreateContextAttribsARB", false)); - #endif - - // Récupération de la version d'OpenGL - // Ce code se base sur le fait que la carte graphique renverra un contexte de compatibilité avec la plus haute version supportée - // Ce qui semble vrai au moins chez ATI/AMD et NVidia, mais si quelqu'un à une meilleure idée ... - glGetString = reinterpret_cast(LoadEntry("glGetString", false)); - if (!glGetString) - { - NazaraError("Unable to load OpenGL: failed to load glGetString"); - Uninitialize(); - - return false; - } - - const GLubyte* version = glGetString(GL_VERSION); - if (!version) - { - NazaraError("Unable to retrieve OpenGL version"); - Uninitialize(); - - return false; - } - - unsigned int major = version[0] - '0'; - unsigned int minor = version[2] - '0'; - - if (major == 0 || major > 9) - { - NazaraError("Unable to retrieve OpenGL major version"); - return false; - } - - if (minor > 9) - { - NazaraWarning("Unable to retrieve OpenGL minor version (using 0)"); - minor = 0; - } - - openGLversion = major*100 + minor*10; - if (openGLversion < 200) - { - NazaraError("OpenGL version is too low, please upgrade your drivers or your graphics card"); - Uninitialize(); - - return false; - } - - parameters.debugMode = true; // Certaines extensions n'apparaissent qu'avec un contexte de debug (e.g. ARB_debug_output) - parameters.majorVersion = NzContextParameters::defaultMajorVersion = major; - parameters.minorVersion = NzContextParameters::defaultMinorVersion = minor; - - // Destruction implicite du premier contexte - if (!loadContext.Create(parameters)) - { - NazaraError("Failed to create load context"); - Uninitialize(); - - return false; - } - - /****************************************Noyau****************************************/ - - try - { - glActiveTexture = reinterpret_cast(LoadEntry("glActiveTexture")); - glAttachShader = reinterpret_cast(LoadEntry("glAttachShader")); - glBeginQuery = reinterpret_cast(LoadEntry("glBeginQuery")); - glBindAttribLocation = reinterpret_cast(LoadEntry("glBindAttribLocation")); - glBindBuffer = reinterpret_cast(LoadEntry("glBindBuffer")); - glBindTexture = reinterpret_cast(LoadEntry("glBindTexture")); - glBlendFunc = reinterpret_cast(LoadEntry("glBlendFunc")); - glBufferData = reinterpret_cast(LoadEntry("glBufferData")); - glBufferSubData = reinterpret_cast(LoadEntry("glBufferSubData")); - glClear = reinterpret_cast(LoadEntry("glClear")); - glClearColor = reinterpret_cast(LoadEntry("glClearColor")); - glClearDepth = reinterpret_cast(LoadEntry("glClearDepth")); - glClearStencil = reinterpret_cast(LoadEntry("glClearStencil")); - glCreateProgram = reinterpret_cast(LoadEntry("glCreateProgram")); - glCreateShader = reinterpret_cast(LoadEntry("glCreateShader")); - glColorMask = reinterpret_cast(LoadEntry("glColorMask")); - glCullFace = reinterpret_cast(LoadEntry("glCullFace")); - glCompileShader = reinterpret_cast(LoadEntry("glCompileShader")); - glCopyTexSubImage2D = reinterpret_cast(LoadEntry("glCopyTexSubImage2D")); - glDeleteBuffers = reinterpret_cast(LoadEntry("glDeleteBuffers")); - glDeleteQueries = reinterpret_cast(LoadEntry("glDeleteQueries")); - glDeleteProgram = reinterpret_cast(LoadEntry("glDeleteProgram")); - glDeleteShader = reinterpret_cast(LoadEntry("glDeleteShader")); - glDeleteTextures = reinterpret_cast(LoadEntry("glDeleteTextures")); - glDepthFunc = reinterpret_cast(LoadEntry("glDepthFunc")); - glDepthMask = reinterpret_cast(LoadEntry("glDepthMask")); - glDisable = reinterpret_cast(LoadEntry("glDisable")); - glDisableVertexAttribArray = reinterpret_cast(LoadEntry("glDisableVertexAttribArray")); - glDrawArrays = reinterpret_cast(LoadEntry("glDrawArrays")); - glDrawBuffer = reinterpret_cast(LoadEntry("glDrawBuffer")); - glDrawBuffers = reinterpret_cast(LoadEntry("glDrawBuffers")); - glDrawElements = reinterpret_cast(LoadEntry("glDrawElements")); - glFlush = reinterpret_cast(LoadEntry("glFlush")); - glEnable = reinterpret_cast(LoadEntry("glEnable")); - glEnableVertexAttribArray = reinterpret_cast(LoadEntry("glEnableVertexAttribArray")); - glEndQuery = reinterpret_cast(LoadEntry("glEndQuery")); - glGenBuffers = reinterpret_cast(LoadEntry("glGenBuffers")); - glGenQueries = reinterpret_cast(LoadEntry("glGenQueries")); - glGenTextures = reinterpret_cast(LoadEntry("glGenTextures")); - glGetBufferParameteriv = reinterpret_cast(LoadEntry("glGetBufferParameteriv")); - glGetError = reinterpret_cast(LoadEntry("glGetError")); - glGetQueryiv = reinterpret_cast(LoadEntry("glGetQueryiv")); - glGetQueryObjectiv = reinterpret_cast(LoadEntry("glGetQueryObjectiv")); - glGetQueryObjectuiv = reinterpret_cast(LoadEntry("glGetQueryObjectuiv")); - glGetIntegerv = reinterpret_cast(LoadEntry("glGetIntegerv")); - glGetProgramiv = reinterpret_cast(LoadEntry("glGetProgramiv")); - glGetProgramInfoLog = reinterpret_cast(LoadEntry("glGetProgramInfoLog")); - glGetShaderInfoLog = reinterpret_cast(LoadEntry("glGetShaderInfoLog")); - glGetShaderiv = reinterpret_cast(LoadEntry("glGetShaderiv")); - glGetShaderSource = reinterpret_cast(LoadEntry("glGetShaderSource")); - glGetTexImage = reinterpret_cast(LoadEntry("glGetTexImage")); - glGetTexLevelParameterfv = reinterpret_cast(LoadEntry("glGetTexLevelParameterfv")); - glGetTexLevelParameteriv = reinterpret_cast(LoadEntry("glGetTexLevelParameteriv")); - glGetTexParameterfv = reinterpret_cast(LoadEntry("glGetTexParameterfv")); - glGetTexParameteriv = reinterpret_cast(LoadEntry("glGetTexParameteriv")); - glGetUniformLocation = reinterpret_cast(LoadEntry("glGetUniformLocation")); - glLinkProgram = reinterpret_cast(LoadEntry("glLinkProgram")); - glMapBuffer = reinterpret_cast(LoadEntry("glMapBuffer")); - glPixelStorei = reinterpret_cast(LoadEntry("glPixelStorei")); - glPolygonMode = reinterpret_cast(LoadEntry("glPolygonMode")); - glReadPixels = reinterpret_cast(LoadEntry("glReadPixels")); - glScissor = reinterpret_cast(LoadEntry("glScissor")); - glShaderSource = reinterpret_cast(LoadEntry("glShaderSource")); - glStencilFunc = reinterpret_cast(LoadEntry("glStencilFunc")); - glStencilOp = reinterpret_cast(LoadEntry("glStencilOp")); - glTexImage2D = reinterpret_cast(LoadEntry("glTexImage2D")); - glTexParameterf = reinterpret_cast(LoadEntry("glTexParameterf")); - glTexParameteri = reinterpret_cast(LoadEntry("glTexParameteri")); - glTexSubImage2D = reinterpret_cast(LoadEntry("glTexSubImage2D")); - glUniform1f = reinterpret_cast(LoadEntry("glUniform1f")); - glUniform1i = reinterpret_cast(LoadEntry("glUniform1i")); - glUniform2fv = reinterpret_cast(LoadEntry("glUniform2fv")); - glUniform3fv = reinterpret_cast(LoadEntry("glUniform3fv")); - glUniform4fv = reinterpret_cast(LoadEntry("glUniform4fv")); - glUniformMatrix4fv = reinterpret_cast(LoadEntry("glUniformMatrix4fv")); - glUnmapBuffer = reinterpret_cast(LoadEntry("glUnmapBuffer")); - glUseProgram = reinterpret_cast(LoadEntry("glUseProgram")); - glVertexAttrib4f = reinterpret_cast(LoadEntry("glVertexAttrib4f")); - glVertexAttribPointer = reinterpret_cast(LoadEntry("glVertexAttribPointer")); - glViewport = reinterpret_cast(LoadEntry("glViewport")); - } - catch (const std::exception& e) - { - NazaraError("Unable to load OpenGL: " + NzString(e.what())); - Uninitialize(); - - return false; - } - - /****************************************Extensions****************************************/ - - glGetStringi = reinterpret_cast(LoadEntry("glGetStringi", false)); - glMapBufferRange = reinterpret_cast(LoadEntry("glMapBufferRange", false)); - - #if defined(NAZARA_PLATFORM_WINDOWS) - wglGetExtensionsStringARB = reinterpret_cast(LoadEntry("wglGetExtensionsStringARB", false)); - wglGetExtensionsStringEXT = reinterpret_cast(LoadEntry("wglGetExtensionsStringEXT", false)); - wglSwapInterval = reinterpret_cast(LoadEntry("wglSwapIntervalEXT", false)); - #elif defined(NAZARA_PLATFORM_LINUX) - glXSwapInterval = reinterpret_cast(LoadEntry("glXSwapIntervalSGI", false)); - #endif - - if (!glGetStringi || !LoadExtensions3()) - { - if (openGLversion >= 300) // Dans le cas contraire c'est normal - NazaraWarning("Failed to load OpenGL 3 extension system, switching to OpenGL 2 extension system..."); - - if (!LoadExtensionsString(reinterpret_cast(glGetString(GL_EXTENSIONS)))) - NazaraWarning("Failed to load extension system"); - } - - #ifdef NAZARA_PLATFORM_WINDOWS - { - bool loaded; - if (wglGetExtensionsStringARB) - loaded = LoadExtensionsString(reinterpret_cast(wglGetExtensionsStringARB(wglGetCurrentDC()))); - else if (wglGetExtensionsStringEXT) - loaded = LoadExtensionsString(reinterpret_cast(wglGetExtensionsStringEXT())); - else - loaded = false; - - if (!loaded) - NazaraWarning("Failed to load wgl extension string"); - } - #endif - - // AnisotropicFilter - openGLextensions[NzOpenGL::AnisotropicFilter] = IsSupported("GL_EXT_texture_filter_anisotropic"); - - // DebugOutput - if (openGLversion >= 430 || IsSupported("GL_KHR_debug")) - { - try - { - glDebugMessageCallback = reinterpret_cast(LoadEntry("glDebugMessageCallback")); - glDebugMessageControl = reinterpret_cast(LoadEntry("glDebugMessageControl")); - glDebugMessageInsert = reinterpret_cast(LoadEntry("glDebugMessageInsert")); - glGetDebugMessageLog = reinterpret_cast(LoadEntry("glGetDebugMessageLog")); - - openGLextensions[NzOpenGL::DebugOutput] = true; - } - catch (const std::exception& e) - { - NazaraWarning("Failed to load GL_KHR_debug: " + NzString(e.what())); - } - } - - if (!openGLextensions[NzOpenGL::DebugOutput] && IsSupported("GL_ARB_debug_output")) - { - try - { - glDebugMessageCallback = reinterpret_cast(LoadEntry("glDebugMessageCallbackARB")); - glDebugMessageControl = reinterpret_cast(LoadEntry("glDebugMessageControlARB")); - glDebugMessageInsert = reinterpret_cast(LoadEntry("glDebugMessageInsertARB")); - glGetDebugMessageLog = reinterpret_cast(LoadEntry("glGetDebugMessageLogARB")); - - openGLextensions[NzOpenGL::DebugOutput] = true; - } - catch (const std::exception& e) - { - NazaraWarning("Failed to load GL_ARB_debug_output: " + NzString(e.what())); - } - } - - // FP64 - if (openGLversion >= 400 || IsSupported("GL_ARB_gpu_shader_fp64")) - { - try - { - glUniform1d = reinterpret_cast(LoadEntry("glUniform1d")); - glUniform2dv = reinterpret_cast(LoadEntry("glUniform2dv")); - glUniform3dv = reinterpret_cast(LoadEntry("glUniform3dv")); - glUniform4dv = reinterpret_cast(LoadEntry("glUniform4dv")); - - openGLextensions[NzOpenGL::FP64] = true; - } - catch (const std::exception& e) - { - NazaraWarning("Failed to load ARB_gpu_shader_fp64: " + NzString(e.what())); - } - } - - // FrameBufferObject - if (openGLversion >= 300 || IsSupported("GL_ARB_framebuffer_object")) - { - try - { - glBindFramebuffer = reinterpret_cast(LoadEntry("glBindFramebuffer")); - glBindRenderbuffer = reinterpret_cast(LoadEntry("glBindRenderbuffer")); - glCheckFramebufferStatus = reinterpret_cast(LoadEntry("glCheckFramebufferStatus")); - glDeleteFramebuffers = reinterpret_cast(LoadEntry("glDeleteFramebuffers")); - glDeleteRenderbuffers = reinterpret_cast(LoadEntry("glDeleteRenderbuffers")); - glFramebufferRenderbuffer = reinterpret_cast(LoadEntry("glFramebufferRenderbuffer")); - glFramebufferTexture2D = reinterpret_cast(LoadEntry("glFramebufferTexture2D")); - glGenerateMipmap = reinterpret_cast(LoadEntry("glGenerateMipmap")); - glGenFramebuffers = reinterpret_cast(LoadEntry("glGenFramebuffers")); - glGenRenderbuffers = reinterpret_cast(LoadEntry("glGenRenderbuffers")); - glRenderbufferStorage = reinterpret_cast(LoadEntry("glRenderbufferStorage")); - - openGLextensions[NzOpenGL::FrameBufferObject] = true; - } - catch (const std::exception& e) - { - NazaraWarning("Failed to load ARB_framebuffer_object: (" + NzString(e.what()) + ")"); - } - } - - // PixelBufferObject - openGLextensions[NzOpenGL::PixelBufferObject] = (openGLversion >= 210 || IsSupported("GL_ARB_pixel_buffer_object")); - - // SeparateShaderObjects - if (openGLversion >= 400 || IsSupported("GL_ARB_separate_shader_objects")) - { - glProgramUniform1f = reinterpret_cast(LoadEntry("glProgramUniform1f")); - glProgramUniform1i = reinterpret_cast(LoadEntry("glProgramUniform1i")); - glProgramUniform2fv = reinterpret_cast(LoadEntry("glProgramUniform2fv")); - glProgramUniform3fv = reinterpret_cast(LoadEntry("glProgramUniform3fv")); - glProgramUniform4fv = reinterpret_cast(LoadEntry("glProgramUniform4fv")); - glProgramUniformMatrix4fv = reinterpret_cast(LoadEntry("glProgramUniformMatrix4fv")); - - // Si ARB_gpu_shader_fp64 est supporté, alors cette extension donne également accès aux fonctions utilisant des double - if (openGLextensions[NzOpenGL::FP64]) - { - glProgramUniform1d = reinterpret_cast(LoadEntry("glProgramUniform1d")); - glProgramUniform2dv = reinterpret_cast(LoadEntry("glProgramUniform2dv")); - glProgramUniform3dv = reinterpret_cast(LoadEntry("glProgramUniform3dv")); - glProgramUniform4dv = reinterpret_cast(LoadEntry("glProgramUniform4dv")); - glProgramUniformMatrix4dv = reinterpret_cast(LoadEntry("glProgramUniformMatrix4dv")); - } - - openGLextensions[NzOpenGL::SeparateShaderObjects] = true; - } - - // Texture3D - try - { - glTexImage3D = reinterpret_cast(LoadEntry("glTexImage3D")); - glTexSubImage3D = reinterpret_cast(LoadEntry("glTexSubImage3D")); - - openGLextensions[NzOpenGL::Texture3D] = true; - } - catch (const std::exception& e) - { - NazaraWarning("Failed to load core texture 3D (" + NzString(e.what()) + ")"); - - if (IsSupported("GL_EXT_texture3D")) - { - try - { - // Hacky: Normalement incompatible à cause du internalFormat, GLenum pour l'extension et GLint pour le noyau - // Mais la taille du type étant la même (GLenum est un typedef équivalent à GLint) et Nazara n'utilisant pas - // Ce qui cause l'incompatibilité (les paramètres 1,2,3,4), je prends cette liberté - glTexImage3D = reinterpret_cast(LoadEntry("glTexImage3DEXT")); - glTexSubImage3D = reinterpret_cast(LoadEntry("glTexSubImage3DEXT")); - - openGLextensions[NzOpenGL::Texture3D] = true; - } - catch (const std::exception& e2) - { - NazaraWarning("Failed to load EXT_texture3D: " + NzString(e2.what())); - } - } - } - - // TextureArray - openGLextensions[NzOpenGL::TextureArray] = (openGLversion >= 300 || IsSupported("GL_EXT_texture_array")); - - // TextureCompression_s3tc - openGLextensions[NzOpenGL::TextureCompression_s3tc] = IsSupported("GL_EXT_texture_compression_s3tc"); - - // TextureStorage - if (openGLversion >= 420 || IsSupported("GL_ARB_texture_storage")) - { - try - { - glTexStorage1D = reinterpret_cast(LoadEntry("glTexStorage1D")); - glTexStorage2D = reinterpret_cast(LoadEntry("glTexStorage2D")); - glTexStorage3D = reinterpret_cast(LoadEntry("glTexStorage3D")); - - openGLextensions[NzOpenGL::TextureStorage] = true; - } - catch (const std::exception& e) - { - NazaraWarning("Failed to load ARB_texture_storage: " + NzString(e.what())); - } - } - - // VertexArrayObject - if (openGLversion >= 300 || IsSupported("GL_ARB_vertex_array_object")) - { - try - { - glBindVertexArray = reinterpret_cast(LoadEntry("glBindVertexArray")); - glDeleteVertexArrays = reinterpret_cast(LoadEntry("glDeleteVertexArrays")); - glGenVertexArrays = reinterpret_cast(LoadEntry("glGenVertexArrays")); - - openGLextensions[NzOpenGL::VertexArrayObject] = true; - } - catch (const std::exception& e) - { - NazaraWarning("Failed to load ARB_vertex_array_object: " + NzString(e.what())); - } - } - - // Fonctions de substitut - if (!glGenerateMipmap) - glGenerateMipmap = reinterpret_cast(LoadEntry("glGenerateMipmapEXT", false)); - - /****************************************Contexte de référence****************************************/ - - ///FIXME: Utiliser le contexte de chargement comme référence ? (Vérifier mode debug) - if (!NzContext::Initialize()) - { - NazaraError("Failed to initialize contexts"); - Uninitialize(); - - return false; - } - - NzContextParameters::defaultShareContext = NzContext::GetReference(); - - return true; -} - -bool NzOpenGL::IsSupported(Extension extension) -{ - return openGLextensions[extension]; -} - -bool NzOpenGL::IsSupported(const NzString& string) -{ - return openGLextensionSet.find(string) != openGLextensionSet.end(); -} - -void NzOpenGL::Uninitialize() -{ - NzContext::Uninitialize(); - - for (bool& ext : openGLextensions) - ext = false; - - openGLextensionSet.clear(); - openGLversion = 0; - - UnloadLibrary(); -} - -PFNGLACTIVETEXTUREPROC glActiveTexture = nullptr; -PFNGLATTACHSHADERPROC glAttachShader = nullptr; -PFNGLBEGINQUERYPROC glBeginQuery = nullptr; -PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation = nullptr; -PFNGLBINDBUFFERPROC glBindBuffer = nullptr; -PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer = nullptr; -PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer = nullptr; -PFNGLBINDTEXTUREPROC glBindTexture = nullptr; -PFNGLBINDVERTEXARRAYPROC glBindVertexArray = nullptr; -PFNGLBLENDFUNCPROC glBlendFunc = nullptr; -PFNGLBUFFERDATAPROC glBufferData = nullptr; -PFNGLBUFFERSUBDATAPROC glBufferSubData = nullptr; -PFNGLCLEARPROC glClear = nullptr; -PFNGLCLEARCOLORPROC glClearColor = nullptr; -PFNGLCLEARDEPTHPROC glClearDepth = nullptr; -PFNGLCLEARSTENCILPROC glClearStencil = nullptr; -PFNGLCREATEPROGRAMPROC glCreateProgram = nullptr; -PFNGLCREATESHADERPROC glCreateShader = nullptr; -PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus = nullptr; -PFNGLCOLORMASKPROC glColorMask = nullptr; -PFNGLCULLFACEPROC glCullFace = nullptr; -PFNGLCOMPILESHADERPROC glCompileShader = nullptr; -PFNGLCOPYTEXSUBIMAGE2DPROC glCopyTexSubImage2D = nullptr; -PFNGLDEBUGMESSAGECALLBACKPROC glDebugMessageCallback = nullptr; -PFNGLDEBUGMESSAGECONTROLPROC glDebugMessageControl = nullptr; -PFNGLDEBUGMESSAGEINSERTPROC glDebugMessageInsert = nullptr; -PFNGLDELETEBUFFERSPROC glDeleteBuffers = nullptr; -PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers = nullptr; -PFNGLDELETEPROGRAMPROC glDeleteProgram = nullptr; -PFNGLDELETEQUERIESPROC glDeleteQueries = nullptr; -PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers = nullptr; -PFNGLDELETESHADERPROC glDeleteShader = nullptr; -PFNGLDELETETEXTURESPROC glDeleteTextures = nullptr; -PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays = nullptr; -PFNGLDEPTHFUNCPROC glDepthFunc = nullptr; -PFNGLDEPTHMASKPROC glDepthMask = nullptr; -PFNGLDISABLEPROC glDisable = nullptr; -PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray = nullptr; -PFNGLDRAWARRAYSPROC glDrawArrays = nullptr; -PFNGLDRAWBUFFERPROC glDrawBuffer = nullptr; -PFNGLDRAWBUFFERSPROC glDrawBuffers = nullptr; -PFNGLDRAWELEMENTSPROC glDrawElements = nullptr; -PFNGLENDQUERYPROC glEndQuery = nullptr; -PFNGLFLUSHPROC glFlush = nullptr; -PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer = nullptr; -PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D = nullptr; -PFNGLENABLEPROC glEnable = nullptr; -PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray = nullptr; -PFNGLGENERATEMIPMAPPROC glGenerateMipmap = nullptr; -PFNGLGENBUFFERSPROC glGenBuffers = nullptr; -PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers = nullptr; -PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers = nullptr; -PFNGLGENQUERIESPROC glGenQueries = nullptr; -PFNGLGENTEXTURESPROC glGenTextures = nullptr; -PFNGLGENVERTEXARRAYSPROC glGenVertexArrays = nullptr; -PFNGLGETBUFFERPARAMETERIVPROC glGetBufferParameteriv = nullptr; -PFNGLGETDEBUGMESSAGELOGPROC glGetDebugMessageLog = nullptr; -PFNGLGETERRORPROC glGetError = nullptr; -PFNGLGETINTEGERVPROC glGetIntegerv = nullptr; -PFNGLGETPROGRAMIVPROC glGetProgramiv = nullptr; -PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog = nullptr; -PFNGLGETQUERYIVPROC glGetQueryiv = nullptr; -PFNGLGETQUERYOBJECTIVPROC glGetQueryObjectiv = nullptr; -PFNGLGETQUERYOBJECTUIVPROC glGetQueryObjectuiv = nullptr; -PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog = nullptr; -PFNGLGETSHADERIVPROC glGetShaderiv = nullptr; -PFNGLGETSHADERSOURCEPROC glGetShaderSource = nullptr; -PFNGLGETSTRINGPROC glGetString = nullptr; -PFNGLGETSTRINGIPROC glGetStringi = nullptr; -PFNGLGETTEXIMAGEPROC glGetTexImage = nullptr; -PFNGLGETTEXLEVELPARAMETERFVPROC glGetTexLevelParameterfv = nullptr; -PFNGLGETTEXLEVELPARAMETERIVPROC glGetTexLevelParameteriv = nullptr; -PFNGLGETTEXPARAMETERFVPROC glGetTexParameterfv = nullptr; -PFNGLGETTEXPARAMETERIVPROC glGetTexParameteriv = nullptr; -PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation = nullptr; -PFNGLLINKPROGRAMPROC glLinkProgram = nullptr; -PFNGLMAPBUFFERPROC glMapBuffer = nullptr; -PFNGLMAPBUFFERRANGEPROC glMapBufferRange = nullptr; -PFNGLPIXELSTOREIPROC glPixelStorei = nullptr; -PFNGLPOLYGONMODEPROC glPolygonMode = nullptr; -PFNGLPROGRAMUNIFORM1DPROC glProgramUniform1d = nullptr; -PFNGLPROGRAMUNIFORM1FPROC glProgramUniform1f = nullptr; -PFNGLPROGRAMUNIFORM1IPROC glProgramUniform1i = nullptr; -PFNGLPROGRAMUNIFORM2DVPROC glProgramUniform2dv = nullptr; -PFNGLPROGRAMUNIFORM2FVPROC glProgramUniform2fv = nullptr; -PFNGLPROGRAMUNIFORM3DVPROC glProgramUniform3dv = nullptr; -PFNGLPROGRAMUNIFORM3FVPROC glProgramUniform3fv = nullptr; -PFNGLPROGRAMUNIFORM4DVPROC glProgramUniform4dv = nullptr; -PFNGLPROGRAMUNIFORM4FVPROC glProgramUniform4fv = nullptr; -PFNGLPROGRAMUNIFORMMATRIX4DVPROC glProgramUniformMatrix4dv = nullptr; -PFNGLPROGRAMUNIFORMMATRIX4FVPROC glProgramUniformMatrix4fv = nullptr; -PFNGLREADPIXELSPROC glReadPixels = nullptr; -PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage = nullptr; -PFNGLSCISSORPROC glScissor = nullptr; -PFNGLSHADERSOURCEPROC glShaderSource = nullptr; -PFNGLSTENCILFUNCPROC glStencilFunc = nullptr; -PFNGLSTENCILOPPROC glStencilOp = nullptr; -PFNGLTEXIMAGE1DPROC glTexImage1D = nullptr; -PFNGLTEXIMAGE2DPROC glTexImage2D = nullptr; -PFNGLTEXIMAGE3DPROC glTexImage3D = nullptr; -PFNGLTEXPARAMETERFPROC glTexParameterf = nullptr; -PFNGLTEXPARAMETERIPROC glTexParameteri = nullptr; -PFNGLTEXSTORAGE1DPROC glTexStorage1D = nullptr; -PFNGLTEXSTORAGE2DPROC glTexStorage2D = nullptr; -PFNGLTEXSTORAGE3DPROC glTexStorage3D = nullptr; -PFNGLTEXSUBIMAGE1DPROC glTexSubImage1D = nullptr; -PFNGLTEXSUBIMAGE2DPROC glTexSubImage2D = nullptr; -PFNGLTEXSUBIMAGE3DPROC glTexSubImage3D = nullptr; -PFNGLUNIFORM1DPROC glUniform1d = nullptr; -PFNGLUNIFORM1FPROC glUniform1f = nullptr; -PFNGLUNIFORM1IPROC glUniform1i = nullptr; -PFNGLUNIFORM2DVPROC glUniform2dv = nullptr; -PFNGLUNIFORM2FVPROC glUniform2fv = nullptr; -PFNGLUNIFORM3DVPROC glUniform3dv = nullptr; -PFNGLUNIFORM3FVPROC glUniform3fv = nullptr; -PFNGLUNIFORM4DVPROC glUniform4dv = nullptr; -PFNGLUNIFORM4FVPROC glUniform4fv = nullptr; -PFNGLUNIFORMMATRIX4DVPROC glUniformMatrix4dv = nullptr; -PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv = nullptr; -PFNGLUNMAPBUFFERPROC glUnmapBuffer = nullptr; -PFNGLUSEPROGRAMPROC glUseProgram = nullptr; -PFNGLVERTEXATTRIB4FPROC glVertexAttrib4f = nullptr; -PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer = nullptr; -PFNGLVIEWPORTPROC glViewport = nullptr; -#if defined(NAZARA_PLATFORM_WINDOWS) -PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormat = nullptr; -PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribs = nullptr; -PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = nullptr; -PFNWGLGETEXTENSIONSSTRINGEXTPROC wglGetExtensionsStringEXT = nullptr; -PFNWGLSWAPINTERVALEXTPROC wglSwapInterval = nullptr; -#elif defined(NAZARA_PLATFORM_LINUX) -PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribs = nullptr; -PFNGLXSWAPINTERVALSGIPROC glXSwapInterval = nullptr; -#endif +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + #ifdef NAZARA_PLATFORM_WINDOWS + HMODULE openGLlibrary; + #endif + + NzOpenGLFunc LoadEntry(const char* name, bool launchException = true) + { + #if defined(NAZARA_PLATFORM_WINDOWS) + NzOpenGLFunc entry = reinterpret_cast(wglGetProcAddress(name)); + if (!entry) // wglGetProcAddress ne fonctionne pas sur les fonctions OpenGL <= 1.1 + entry = reinterpret_cast(GetProcAddress(openGLlibrary, name)); + #elif defined(NAZARA_PLATFORM_LINUX) + NzOpenGLFunc entry = reinterpret_cast(glXGetProcAddress(name)); + #else + #error OS not handled + #endif + + if (!entry && launchException) + { + std::ostringstream oss; + oss << "failed to load \"" << name << '"'; + + throw std::runtime_error(oss.str()); + } + + return entry; + } + + bool LoadLibrary() + { + #ifdef NAZARA_PLATFORM_WINDOWS + openGLlibrary = ::LoadLibraryA("opengl32.dll"); + + return openGLlibrary != nullptr; + #else + return true; + #endif + } + + void UnloadLibrary() + { + #ifdef NAZARA_PLATFORM_WINDOWS + FreeLibrary(openGLlibrary); + #endif + } + + std::set openGLextensionSet; + bool openGLextensions[nzOpenGLExtension_Max+1] = {false}; + unsigned int openGLversion = 0; + + bool LoadExtensionsString(const NzString& extensionString) + { + if (extensionString.IsEmpty()) + { + NazaraError("Unable to get extension string"); + return false; + } + + // On peut sûrement faire plus rapide mais comme ça ne se fait qu'une fois et que NzString implémente le COW... + std::vector ext; + extensionString.Split(ext); + + for (std::vector::iterator it = ext.begin(); it != ext.end(); ++it) + openGLextensionSet.insert(*it); + + return true; + } + + bool LoadExtensions3() + { + GLint extensionCount = 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &extensionCount); + + if (extensionCount <= 0) + { + NazaraError("Unable to get extension count"); + return false; + } + + for (int i = 0; i < extensionCount; ++i) + { + NzString extension(reinterpret_cast(glGetStringi(GL_EXTENSIONS, i))); + if (extension.IsEmpty()) + { + NazaraWarning("Unable to get extension #" + NzString::Number(i)); + continue; + } + + openGLextensionSet.insert(extension); + } + + return true; + } +} + +namespace NzOpenGL +{ + NzOpenGLFunc GetEntry(const NzString& entryPoint) + { + return LoadEntry(entryPoint.GetConstBuffer(), false); + } + + unsigned int GetVersion() + { + return openGLversion; + } + + bool Initialize() + { + if (!LoadLibrary()) + { + NazaraError("Failed to load OpenGL library"); + return false; + } + + // Le chargement des fonctions OpenGL nécessite un contexte OpenGL + NzContextParameters parameters; + parameters.majorVersion = 2; + parameters.minorVersion = 0; + parameters.shared = false; + + /* + Note: Même le contexte de chargement nécessite quelques fonctions de base pour correctement s'initialiser + Pour cette raison, deux contextes sont créés, le premier sert à récupérer les fonctions permetttant + de créer le second avec les bons paramètres. + + Non sérieusement si quelqu'un a une meilleure idée qu'il me le dise. + */ + + /****************************************Initialisation****************************************/ + + NzContext loadContext; + if (!loadContext.Create(parameters)) + { + NazaraError("Failed to create load context"); + Uninitialize(); + + return false; + } + + #if defined(NAZARA_PLATFORM_WINDOWS) + wglCreateContextAttribs = reinterpret_cast(LoadEntry("wglCreateContextAttribsARB", false)); + wglChoosePixelFormat = reinterpret_cast(LoadEntry("wglChoosePixelFormatARB", false)); + if (!wglChoosePixelFormat) + wglChoosePixelFormat = reinterpret_cast(LoadEntry("wglChoosePixelFormatEXT", false)); + #elif defined(NAZARA_PLATFORM_LINUX) + glXCreateContextAttribs = reinterpret_cast(LoadEntry("glXCreateContextAttribsARB", false)); + #endif + + // Récupération de la version d'OpenGL + // Ce code se base sur le fait que la carte graphique renverra un contexte de compatibilité avec la plus haute version supportée + // Ce qui semble vrai au moins chez ATI/AMD et NVidia, mais si quelqu'un à une meilleure idée ... + glGetString = reinterpret_cast(LoadEntry("glGetString", false)); + if (!glGetString) + { + NazaraError("Unable to load OpenGL: failed to load glGetString"); + Uninitialize(); + + return false; + } + + const GLubyte* version = glGetString(GL_VERSION); + if (!version) + { + NazaraError("Unable to retrieve OpenGL version"); + Uninitialize(); + + return false; + } + + unsigned int major = version[0] - '0'; + unsigned int minor = version[2] - '0'; + + if (major == 0 || major > 9) + { + NazaraError("Unable to retrieve OpenGL major version"); + return false; + } + + if (minor > 9) + { + NazaraWarning("Unable to retrieve OpenGL minor version (using 0)"); + minor = 0; + } + + openGLversion = major*100 + minor*10; + if (openGLversion < 200) + { + NazaraError("OpenGL version is too low, please upgrade your drivers or your video card"); + Uninitialize(); + + return false; + } + + parameters.debugMode = true; // Certaines extensions n'apparaissent qu'avec un contexte de debug (e.g. ARB_debug_output) + parameters.majorVersion = NzContextParameters::defaultMajorVersion = major; + parameters.minorVersion = NzContextParameters::defaultMinorVersion = minor; + + if (!loadContext.Create(parameters)) // Destruction implicite du premier contexte + { + NazaraError("Failed to create load context"); + Uninitialize(); + + return false; + } + + /****************************************Noyau****************************************/ + + try + { + glActiveTexture = reinterpret_cast(LoadEntry("glActiveTexture")); + glAttachShader = reinterpret_cast(LoadEntry("glAttachShader")); + glBeginQuery = reinterpret_cast(LoadEntry("glBeginQuery")); + glBindAttribLocation = reinterpret_cast(LoadEntry("glBindAttribLocation")); + glBindBuffer = reinterpret_cast(LoadEntry("glBindBuffer")); + glBindTexture = reinterpret_cast(LoadEntry("glBindTexture")); + glBlendFunc = reinterpret_cast(LoadEntry("glBlendFunc")); + glBufferData = reinterpret_cast(LoadEntry("glBufferData")); + glBufferSubData = reinterpret_cast(LoadEntry("glBufferSubData")); + glClear = reinterpret_cast(LoadEntry("glClear")); + glClearColor = reinterpret_cast(LoadEntry("glClearColor")); + glClearDepth = reinterpret_cast(LoadEntry("glClearDepth")); + glClearStencil = reinterpret_cast(LoadEntry("glClearStencil")); + glCreateProgram = reinterpret_cast(LoadEntry("glCreateProgram")); + glCreateShader = reinterpret_cast(LoadEntry("glCreateShader")); + glColorMask = reinterpret_cast(LoadEntry("glColorMask")); + glCullFace = reinterpret_cast(LoadEntry("glCullFace")); + glCompileShader = reinterpret_cast(LoadEntry("glCompileShader")); + glCopyTexSubImage2D = reinterpret_cast(LoadEntry("glCopyTexSubImage2D")); + glDeleteBuffers = reinterpret_cast(LoadEntry("glDeleteBuffers")); + glDeleteQueries = reinterpret_cast(LoadEntry("glDeleteQueries")); + glDeleteProgram = reinterpret_cast(LoadEntry("glDeleteProgram")); + glDeleteShader = reinterpret_cast(LoadEntry("glDeleteShader")); + glDeleteTextures = reinterpret_cast(LoadEntry("glDeleteTextures")); + glDepthFunc = reinterpret_cast(LoadEntry("glDepthFunc")); + glDepthMask = reinterpret_cast(LoadEntry("glDepthMask")); + glDisable = reinterpret_cast(LoadEntry("glDisable")); + glDisableVertexAttribArray = reinterpret_cast(LoadEntry("glDisableVertexAttribArray")); + glDrawArrays = reinterpret_cast(LoadEntry("glDrawArrays")); + glDrawBuffer = reinterpret_cast(LoadEntry("glDrawBuffer")); + glDrawBuffers = reinterpret_cast(LoadEntry("glDrawBuffers")); + glDrawElements = reinterpret_cast(LoadEntry("glDrawElements")); + glFlush = reinterpret_cast(LoadEntry("glFlush")); + glEnable = reinterpret_cast(LoadEntry("glEnable")); + glEnableVertexAttribArray = reinterpret_cast(LoadEntry("glEnableVertexAttribArray")); + glEndQuery = reinterpret_cast(LoadEntry("glEndQuery")); + glGenBuffers = reinterpret_cast(LoadEntry("glGenBuffers")); + glGenQueries = reinterpret_cast(LoadEntry("glGenQueries")); + glGenTextures = reinterpret_cast(LoadEntry("glGenTextures")); + glGetBufferParameteriv = reinterpret_cast(LoadEntry("glGetBufferParameteriv")); + glGetError = reinterpret_cast(LoadEntry("glGetError")); + glGetQueryiv = reinterpret_cast(LoadEntry("glGetQueryiv")); + glGetQueryObjectiv = reinterpret_cast(LoadEntry("glGetQueryObjectiv")); + glGetQueryObjectuiv = reinterpret_cast(LoadEntry("glGetQueryObjectuiv")); + glGetIntegerv = reinterpret_cast(LoadEntry("glGetIntegerv")); + glGetProgramiv = reinterpret_cast(LoadEntry("glGetProgramiv")); + glGetProgramInfoLog = reinterpret_cast(LoadEntry("glGetProgramInfoLog")); + glGetShaderInfoLog = reinterpret_cast(LoadEntry("glGetShaderInfoLog")); + glGetShaderiv = reinterpret_cast(LoadEntry("glGetShaderiv")); + glGetShaderSource = reinterpret_cast(LoadEntry("glGetShaderSource")); + glGetTexImage = reinterpret_cast(LoadEntry("glGetTexImage")); + glGetTexLevelParameterfv = reinterpret_cast(LoadEntry("glGetTexLevelParameterfv")); + glGetTexLevelParameteriv = reinterpret_cast(LoadEntry("glGetTexLevelParameteriv")); + glGetTexParameterfv = reinterpret_cast(LoadEntry("glGetTexParameterfv")); + glGetTexParameteriv = reinterpret_cast(LoadEntry("glGetTexParameteriv")); + glGetUniformLocation = reinterpret_cast(LoadEntry("glGetUniformLocation")); + glLinkProgram = reinterpret_cast(LoadEntry("glLinkProgram")); + glMapBuffer = reinterpret_cast(LoadEntry("glMapBuffer")); + glPixelStorei = reinterpret_cast(LoadEntry("glPixelStorei")); + glPolygonMode = reinterpret_cast(LoadEntry("glPolygonMode")); + glReadPixels = reinterpret_cast(LoadEntry("glReadPixels")); + glScissor = reinterpret_cast(LoadEntry("glScissor")); + glShaderSource = reinterpret_cast(LoadEntry("glShaderSource")); + glStencilFunc = reinterpret_cast(LoadEntry("glStencilFunc")); + glStencilOp = reinterpret_cast(LoadEntry("glStencilOp")); + glTexImage2D = reinterpret_cast(LoadEntry("glTexImage2D")); + glTexImage3D = reinterpret_cast(LoadEntry("glTexImage3D")); + glTexParameterf = reinterpret_cast(LoadEntry("glTexParameterf")); + glTexParameteri = reinterpret_cast(LoadEntry("glTexParameteri")); + glTexSubImage2D = reinterpret_cast(LoadEntry("glTexSubImage2D")); + glTexSubImage3D = reinterpret_cast(LoadEntry("glTexSubImage3D")); + glUniform1f = reinterpret_cast(LoadEntry("glUniform1f")); + glUniform1i = reinterpret_cast(LoadEntry("glUniform1i")); + glUniform2fv = reinterpret_cast(LoadEntry("glUniform2fv")); + glUniform3fv = reinterpret_cast(LoadEntry("glUniform3fv")); + glUniform4fv = reinterpret_cast(LoadEntry("glUniform4fv")); + glUniformMatrix4fv = reinterpret_cast(LoadEntry("glUniformMatrix4fv")); + glUnmapBuffer = reinterpret_cast(LoadEntry("glUnmapBuffer")); + glUseProgram = reinterpret_cast(LoadEntry("glUseProgram")); + glVertexAttrib4f = reinterpret_cast(LoadEntry("glVertexAttrib4f")); + glVertexAttribPointer = reinterpret_cast(LoadEntry("glVertexAttribPointer")); + glViewport = reinterpret_cast(LoadEntry("glViewport")); + } + catch (const std::exception& e) + { + NazaraError("Unable to load OpenGL: " + NzString(e.what())); + Uninitialize(); + + return false; + } + + /****************************************Extensions****************************************/ + + // Fonctions optionnelles + glBindFragDataLocation = reinterpret_cast(LoadEntry("glBindFragDataLocation", false)); + if (!glBindFragDataLocation) + glBindFragDataLocation = reinterpret_cast(LoadEntry("glBindFragDataLocationEXT", false)); + + glGetStringi = reinterpret_cast(LoadEntry("glGetStringi", false)); + glMapBufferRange = reinterpret_cast(LoadEntry("glMapBufferRange", false)); + + #if defined(NAZARA_PLATFORM_WINDOWS) + wglGetExtensionsStringARB = reinterpret_cast(LoadEntry("wglGetExtensionsStringARB", false)); + wglGetExtensionsStringEXT = reinterpret_cast(LoadEntry("wglGetExtensionsStringEXT", false)); + wglSwapInterval = reinterpret_cast(LoadEntry("wglSwapIntervalEXT", false)); + #elif defined(NAZARA_PLATFORM_LINUX) + glXSwapInterval = reinterpret_cast(LoadEntry("glXSwapIntervalSGI", false)); + #endif + + if (!glGetStringi || !LoadExtensions3()) + { + if (openGLversion >= 300) // Dans le cas contraire c'est normal + NazaraWarning("Failed to load OpenGL 3 extension system, switching to OpenGL 2 extension system..."); + + if (!LoadExtensionsString(reinterpret_cast(glGetString(GL_EXTENSIONS)))) + NazaraWarning("Failed to load extension system"); + } + + #ifdef NAZARA_PLATFORM_WINDOWS + { + bool loaded; + if (wglGetExtensionsStringARB) + loaded = LoadExtensionsString(reinterpret_cast(wglGetExtensionsStringARB(wglGetCurrentDC()))); + else if (wglGetExtensionsStringEXT) + loaded = LoadExtensionsString(reinterpret_cast(wglGetExtensionsStringEXT())); + else + loaded = false; + + if (!loaded) + NazaraWarning("Failed to load wgl extension string"); + } + #endif + + // AnisotropicFilter + openGLextensions[nzOpenGLExtension_AnisotropicFilter] = IsSupported("GL_EXT_texture_filter_anisotropic"); + + // DebugOutput + if (openGLversion >= 430 || IsSupported("GL_KHR_debug")) + { + try + { + glDebugMessageCallback = reinterpret_cast(LoadEntry("glDebugMessageCallback")); + glDebugMessageControl = reinterpret_cast(LoadEntry("glDebugMessageControl")); + glDebugMessageInsert = reinterpret_cast(LoadEntry("glDebugMessageInsert")); + glGetDebugMessageLog = reinterpret_cast(LoadEntry("glGetDebugMessageLog")); + + openGLextensions[nzOpenGLExtension_DebugOutput] = true; + } + catch (const std::exception& e) + { + NazaraWarning("Failed to load GL_KHR_debug: " + NzString(e.what())); + } + } + + if (!openGLextensions[nzOpenGLExtension_DebugOutput] && IsSupported("GL_ARB_debug_output")) + { + try + { + glDebugMessageCallback = reinterpret_cast(LoadEntry("glDebugMessageCallbackARB")); + glDebugMessageControl = reinterpret_cast(LoadEntry("glDebugMessageControlARB")); + glDebugMessageInsert = reinterpret_cast(LoadEntry("glDebugMessageInsertARB")); + glGetDebugMessageLog = reinterpret_cast(LoadEntry("glGetDebugMessageLogARB")); + + openGLextensions[nzOpenGLExtension_DebugOutput] = true; + } + catch (const std::exception& e) + { + NazaraWarning("Failed to load GL_ARB_debug_output: " + NzString(e.what())); + } + } + + // FP64 + if (openGLversion >= 400 || IsSupported("GL_ARB_gpu_shader_fp64")) + { + try + { + glUniform1d = reinterpret_cast(LoadEntry("glUniform1d")); + glUniform2dv = reinterpret_cast(LoadEntry("glUniform2dv")); + glUniform3dv = reinterpret_cast(LoadEntry("glUniform3dv")); + glUniform4dv = reinterpret_cast(LoadEntry("glUniform4dv")); + + openGLextensions[nzOpenGLExtension_FP64] = true; + } + catch (const std::exception& e) + { + NazaraWarning("Failed to load ARB_gpu_shader_fp64: " + NzString(e.what())); + } + } + + // FrameBufferObject + if (openGLversion >= 300 || IsSupported("GL_ARB_framebuffer_object")) + { + try + { + glBindFramebuffer = reinterpret_cast(LoadEntry("glBindFramebuffer")); + glBindRenderbuffer = reinterpret_cast(LoadEntry("glBindRenderbuffer")); + glCheckFramebufferStatus = reinterpret_cast(LoadEntry("glCheckFramebufferStatus")); + glDeleteFramebuffers = reinterpret_cast(LoadEntry("glDeleteFramebuffers")); + glDeleteRenderbuffers = reinterpret_cast(LoadEntry("glDeleteRenderbuffers")); + glFramebufferRenderbuffer = reinterpret_cast(LoadEntry("glFramebufferRenderbuffer")); + glFramebufferTexture = reinterpret_cast(LoadEntry("glFramebufferTexture")); + glFramebufferTexture1D = reinterpret_cast(LoadEntry("glFramebufferTexture1D")); + glFramebufferTexture2D = reinterpret_cast(LoadEntry("glFramebufferTexture2D")); + glFramebufferTexture3D = reinterpret_cast(LoadEntry("glFramebufferTexture3D")); + glFramebufferTextureLayer = reinterpret_cast(LoadEntry("glFramebufferTextureLayer")); + glGenerateMipmap = reinterpret_cast(LoadEntry("glGenerateMipmap")); + glGenFramebuffers = reinterpret_cast(LoadEntry("glGenFramebuffers")); + glGenRenderbuffers = reinterpret_cast(LoadEntry("glGenRenderbuffers")); + glRenderbufferStorage = reinterpret_cast(LoadEntry("glRenderbufferStorage")); + + openGLextensions[nzOpenGLExtension_FrameBufferObject] = true; + } + catch (const std::exception& e) + { + NazaraWarning("Failed to load ARB_framebuffer_object: (" + NzString(e.what()) + ")"); + } + } + + // PixelBufferObject + openGLextensions[nzOpenGLExtension_PixelBufferObject] = (openGLversion >= 210 || IsSupported("GL_ARB_pixel_buffer_object")); + + // SeparateShaderObjects + if (openGLversion >= 400 || IsSupported("GL_ARB_separate_shader_objects")) + { + glProgramUniform1f = reinterpret_cast(LoadEntry("glProgramUniform1f")); + glProgramUniform1i = reinterpret_cast(LoadEntry("glProgramUniform1i")); + glProgramUniform2fv = reinterpret_cast(LoadEntry("glProgramUniform2fv")); + glProgramUniform3fv = reinterpret_cast(LoadEntry("glProgramUniform3fv")); + glProgramUniform4fv = reinterpret_cast(LoadEntry("glProgramUniform4fv")); + glProgramUniformMatrix4fv = reinterpret_cast(LoadEntry("glProgramUniformMatrix4fv")); + + // Si ARB_gpu_shader_fp64 est supporté, alors cette extension donne également accès aux fonctions utilisant des double + if (openGLextensions[nzOpenGLExtension_FP64]) + { + glProgramUniform1d = reinterpret_cast(LoadEntry("glProgramUniform1d")); + glProgramUniform2dv = reinterpret_cast(LoadEntry("glProgramUniform2dv")); + glProgramUniform3dv = reinterpret_cast(LoadEntry("glProgramUniform3dv")); + glProgramUniform4dv = reinterpret_cast(LoadEntry("glProgramUniform4dv")); + glProgramUniformMatrix4dv = reinterpret_cast(LoadEntry("glProgramUniformMatrix4dv")); + } + + openGLextensions[nzOpenGLExtension_SeparateShaderObjects] = true; + } + + // TextureArray + openGLextensions[nzOpenGLExtension_TextureArray] = (openGLversion >= 300 || IsSupported("GL_EXT_texture_array")); + + // TextureCompression_s3tc + openGLextensions[nzOpenGLExtension_TextureCompression_s3tc] = IsSupported("GL_EXT_texture_compression_s3tc"); + + // TextureStorage + if (openGLversion >= 420 || IsSupported("GL_ARB_texture_storage")) + { + try + { + glTexStorage1D = reinterpret_cast(LoadEntry("glTexStorage1D")); + glTexStorage2D = reinterpret_cast(LoadEntry("glTexStorage2D")); + glTexStorage3D = reinterpret_cast(LoadEntry("glTexStorage3D")); + + openGLextensions[nzOpenGLExtension_TextureStorage] = true; + } + catch (const std::exception& e) + { + NazaraWarning("Failed to load ARB_texture_storage: " + NzString(e.what())); + } + } + + // VertexArrayObject + if (openGLversion >= 300 || IsSupported("GL_ARB_vertex_array_object")) + { + try + { + glBindVertexArray = reinterpret_cast(LoadEntry("glBindVertexArray")); + glDeleteVertexArrays = reinterpret_cast(LoadEntry("glDeleteVertexArrays")); + glGenVertexArrays = reinterpret_cast(LoadEntry("glGenVertexArrays")); + + openGLextensions[nzOpenGLExtension_VertexArrayObject] = true; + } + catch (const std::exception& e) + { + NazaraWarning("Failed to load ARB_vertex_array_object: " + NzString(e.what())); + } + } + + // Fonctions de substitut + if (!glGenerateMipmap) + glGenerateMipmap = reinterpret_cast(LoadEntry("glGenerateMipmapEXT", false)); + + /****************************************Contexte de référence****************************************/ + + ///FIXME: Utiliser le contexte de chargement comme référence ? (Vérifier mode debug) + if (!NzContext::Initialize()) + { + NazaraError("Failed to initialize contexts"); + Uninitialize(); + + return false; + } + + NzContextParameters::defaultShareContext = NzContext::GetReference(); + + return true; + } + + bool IsSupported(nzOpenGLExtension extension) + { + return openGLextensions[extension]; + } + + bool IsSupported(const NzString& string) + { + return openGLextensionSet.find(string) != openGLextensionSet.end(); + } + + bool TranslateFormat(nzPixelFormat pixelFormat, Format* format, FormatType type) + { + switch (pixelFormat) + { + case nzPixelFormat_BGR8: + format->dataFormat = GL_BGR; + format->dataType = GL_UNSIGNED_BYTE; + format->internalFormat = GL_RGB8; + return true; + + case nzPixelFormat_BGRA8: + format->dataFormat = GL_BGRA; + format->dataType = GL_UNSIGNED_BYTE; + format->internalFormat = GL_RGBA8; + return true; + + case nzPixelFormat_DXT1: + format->dataFormat = GL_RGB; + format->dataType = GL_UNSIGNED_BYTE; + format->internalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; + return true; + + case nzPixelFormat_DXT3: + format->dataFormat = GL_RGBA; + format->dataType = GL_UNSIGNED_BYTE; + format->internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + return true; + + case nzPixelFormat_DXT5: + format->dataFormat = GL_RGBA; + format->dataType = GL_UNSIGNED_BYTE; + format->internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + return true; + + case nzPixelFormat_L8: + case nzPixelFormat_LA8: + return false; + + case nzPixelFormat_RGB5A1: + format->dataFormat = GL_RGBA; + format->dataType = GL_UNSIGNED_SHORT_5_5_5_1; + format->internalFormat = GL_RGB5_A1; + return true; + + case nzPixelFormat_RGB8: + format->dataFormat = GL_RGB; + format->dataType = GL_UNSIGNED_BYTE; + format->internalFormat = GL_RGB8; + return true; + + case nzPixelFormat_RGBA4: + format->dataFormat = GL_RGBA; + format->dataType = GL_UNSIGNED_SHORT_4_4_4_4; + format->internalFormat = GL_RGBA4; + return true; + + case nzPixelFormat_RGBA8: + format->dataFormat = GL_RGBA; + format->dataType = GL_UNSIGNED_BYTE; + format->internalFormat = GL_RGBA8; + return true; + + case nzPixelFormat_Depth16: + format->dataFormat = GL_DEPTH_COMPONENT; + format->dataType = GL_UNSIGNED_BYTE; + format->internalFormat = GL_DEPTH_COMPONENT16; + return true; + + case nzPixelFormat_Depth24: + format->dataFormat = GL_DEPTH_COMPONENT; + format->dataType = GL_UNSIGNED_BYTE; + format->internalFormat = GL_DEPTH_COMPONENT24; + return true; + + case nzPixelFormat_Depth24Stencil8: + format->dataFormat = GL_DEPTH_STENCIL; + format->dataType = GL_UNSIGNED_BYTE; + format->internalFormat = GL_DEPTH24_STENCIL8; + return true; + + case nzPixelFormat_Depth32: + format->dataFormat = GL_DEPTH_COMPONENT; + format->dataType = GL_UNSIGNED_BYTE; + format->internalFormat = GL_DEPTH_COMPONENT32; + return true; + + case nzPixelFormat_Stencil1: + if (type == FormatType_Texture) // Les formats de stencil ne sont pas supportés par les textures + return false; + else + { + format->dataFormat = GL_NONE; + format->dataType = GL_NONE; + format->internalFormat = GL_STENCIL_INDEX1; + return true; + } + + case nzPixelFormat_Stencil4: + if (type == FormatType_Texture) + return false; + else + { + format->dataFormat = GL_NONE; + format->dataType = GL_NONE; + format->internalFormat = GL_STENCIL_INDEX4; + return true; + } + + case nzPixelFormat_Stencil8: + if (type == FormatType_Texture) + return false; + else + { + format->dataFormat = GL_NONE; + format->dataType = GL_NONE; + format->internalFormat = GL_STENCIL_INDEX8; + return true; + } + + case nzPixelFormat_Stencil16: + if (type == FormatType_Texture) + return false; + else + { + format->dataFormat = GL_NONE; + format->dataType = GL_NONE; + format->internalFormat = GL_STENCIL_INDEX16; + return true; + } + + case nzPixelFormat_Undefined: + break; + } + + NazaraError("Invalid pixel format"); + return false; + } + + void Uninitialize() + { + NzContext::Uninitialize(); + + for (bool& ext : openGLextensions) + ext = false; + + openGLextensionSet.clear(); + openGLversion = 0; + + UnloadLibrary(); + } + + GLenum Attachment[nzAttachmentPoint_Max+1] = + { + GL_COLOR_ATTACHMENT0, // nzAttachmentPoint_Color + GL_DEPTH_ATTACHMENT, // nzAttachmentPoint_Depth + GL_DEPTH_STENCIL_ATTACHMENT, // nzAttachmentPoint_DepthStencil + GL_STENCIL_ATTACHMENT // nzAttachmentPoint_Stencil + }; + + nzUInt8 AttributeIndex[nzElementUsage_Max+1] = + { + 2, // nzElementUsage_Diffuse + 1, // nzElementUsage_Normal + 0, // nzElementUsage_Position + 3, // nzElementUsage_Tangent + + 4 // nzElementUsage_TexCoord (Doit être le dernier de la liste car extensible) + }; + + GLenum BlendFunc[nzBlendFunc_Max+1] = + { + GL_DST_ALPHA, // nzBlendFunc_DestAlpha + GL_DST_COLOR, // nzBlendFunc_DestColor + GL_SRC_ALPHA, // nzBlendFunc_SrcAlpha + GL_SRC_COLOR, // nzBlendFunc_SrcColor + GL_ONE_MINUS_DST_ALPHA, // nzBlendFunc_InvDestAlpha + GL_ONE_MINUS_DST_COLOR, // nzBlendFunc_InvDestColor + GL_ONE_MINUS_SRC_ALPHA, // nzBlendFunc_InvSrcAlpha + GL_ONE_MINUS_SRC_COLOR, // nzBlendFunc_InvSrcColor + GL_ONE, // nzBlendFunc_One + GL_ZERO // nzBlendFunc_Zero + }; + + GLenum BufferLock[nzBufferAccess_Max+1] = + { + GL_WRITE_ONLY, // nzBufferAccess_DiscardAndWrite + GL_READ_ONLY, // nzBufferAccess_ReadOnly + GL_READ_WRITE, // nzBufferAccess_ReadWrite + GL_WRITE_ONLY // nzBufferAccess_WriteOnly + }; + + GLenum BufferLockRange[nzBufferAccess_Max+1] = + { + GL_MAP_INVALIDATE_BUFFER_BIT | GL_MAP_WRITE_BIT, // nzBufferAccess_DiscardAndWrite + GL_MAP_READ_BIT, // nzBufferAccess_ReadOnly + GL_MAP_READ_BIT | GL_MAP_WRITE_BIT, // nzBufferAccess_ReadWrite + GL_MAP_WRITE_BIT // nzBufferAccess_WriteOnly + }; + + GLenum BufferTarget[nzBufferType_Max+1] = + { + GL_ELEMENT_ARRAY_BUFFER, // nzBufferType_Index, + GL_ARRAY_BUFFER, // nzBufferType_Vertex + }; + + GLenum BufferTargetBinding[nzBufferType_Max+1] = + { + GL_ELEMENT_ARRAY_BUFFER_BINDING, // nzBufferType_Index, + GL_ARRAY_BUFFER_BINDING, // nzBufferType_Vertex + }; + + GLenum BufferUsage[nzBufferUsage_Max+1] = + { + // J'ai choisi DYNAMIC à la place de STREAM car DYNAMIC semble plus adapté au profil "une mise à jour pour quelques rendus" + // Ce qui est je pense le scénario qui arrivera le plus souvent (Prévoir une option pour permettre d'utiliser le STREAM_DRAW ?) + // Source: http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=160839 + GL_DYNAMIC_DRAW, // nzBufferUsage_Dynamic + GL_STATIC_DRAW // nzBufferUsage_Static + }; + + GLenum CubemapFace[6] = + { + GL_TEXTURE_CUBE_MAP_POSITIVE_X, // nzCubemapFace_PositiveX + GL_TEXTURE_CUBE_MAP_NEGATIVE_X, // nzCubemapFace_NegativeX + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, // nzCubemapFace_PositiveY + GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, // nzCubemapFace_NegativeY + GL_TEXTURE_CUBE_MAP_POSITIVE_Z, // nzCubemapFace_PositiveZ + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z // nzCubemapFace_NegativeZ + }; + + GLenum ElementType[nzElementType_Max+1] = + { + GL_UNSIGNED_BYTE, // nzElementType_Color + GL_DOUBLE, // nzElementType_Double1 + GL_DOUBLE, // nzElementType_Double2 + GL_DOUBLE, // nzElementType_Double3 + GL_DOUBLE, // nzElementType_Double4 + GL_FLOAT, // nzElementType_Float1 + GL_FLOAT, // nzElementType_Float2 + GL_FLOAT, // nzElementType_Float3 + GL_FLOAT // nzElementType_Float4 + }; + + GLenum FaceCulling[nzFaceCulling_Max+1] = + { + GL_BACK, // nzFaceCulling_Back + GL_FRONT, // nzFaceCulling_Front + GL_FRONT_AND_BACK // nzFaceCulling_FrontAndBack + }; + + GLenum FaceFilling[nzFaceFilling_Max+1] = + { + GL_POINT, // nzFaceFilling_Point + GL_LINE, // nzFaceFilling_Line + GL_FILL // nzFaceFilling_Fill + }; + + GLenum PrimitiveType[nzPrimitiveType_Max+1] = + { + GL_LINES, // nzPrimitiveType_LineList, + GL_LINE_STRIP, // nzPrimitiveType_LineStrip, + GL_POINTS, // nzPrimitiveType_PointList, + GL_TRIANGLES, // nzPrimitiveType_TriangleList, + GL_TRIANGLE_STRIP, // nzPrimitiveType_TriangleStrip, + GL_TRIANGLE_FAN // nzPrimitiveType_TriangleFan + }; + + GLenum RendererComparison[nzRendererComparison_Max+1] = + { + GL_ALWAYS, // nzRendererComparison_Always + GL_EQUAL, // nzRendererComparison_Equal + GL_GREATER, // nzRendererComparison_Greater + GL_GEQUAL, // nzRendererComparison_GreaterOrEqual + GL_LESS, // nzRendererComparison_Less + GL_LEQUAL, // nzRendererComparison_LessOrEqual + GL_NEVER // nzRendererComparison_Never + }; + + GLenum RendererParameter[nzRendererParameter_Max+1] = + { + GL_BLEND, // nzRendererParameter_Blend + GL_NONE, // nzRendererParameter_ColorWrite + GL_DEPTH_TEST, // nzRendererParameter_DepthTest + GL_NONE, // nzRendererParameter_DepthWrite + GL_CULL_FACE, // nzRendererParameter_FaceCulling + GL_STENCIL_TEST // nzRendererParameter_Stencil + }; + + GLenum ShaderType[nzShaderType_Max+1] = + { + GL_FRAGMENT_SHADER, // nzShaderType_Fragment + GL_GEOMETRY_SHADER, // nzShaderType_Geometry + GL_VERTEX_SHADER // nzShaderType_Vertex + }; + + GLenum StencilOperation[nzStencilOperation_Max+1] = + { + GL_DECR, // nzStencilOperation_Decrement + GL_DECR_WRAP, // nzStencilOperation_DecrementToSaturation + GL_INCR, // nzStencilOperation_Increment + GL_INCR_WRAP, // nzStencilOperation_IncrementToSaturation + GL_INVERT, // nzStencilOperation_Invert + GL_KEEP, // nzStencilOperation_Keep + GL_REPLACE, // nzStencilOperation_Replace + GL_ZERO // nzStencilOperation_Zero + }; + + GLenum TextureTarget[nzImageType_Max+1] + { + GL_TEXTURE_1D, // nzImageType_1D + GL_TEXTURE_1D_ARRAY, // nzImageType_1D_Array + GL_TEXTURE_2D, // nzImageType_2D + GL_TEXTURE_2D_ARRAY, // nzImageType_2D_Array + GL_TEXTURE_3D, // nzImageType_3D + GL_TEXTURE_CUBE_MAP // nzImageType_Cubemap + }; + + GLenum TextureTargetBinding[nzImageType_Max+1] + { + GL_TEXTURE_BINDING_1D, // nzImageType_1D + GL_TEXTURE_BINDING_1D_ARRAY, // nzImageType_1D_Array + GL_TEXTURE_BINDING_2D, // nzImageType_2D + GL_TEXTURE_BINDING_2D_ARRAY, // nzImageType_2D_Array + GL_TEXTURE_BINDING_3D, // nzImageType_3D + GL_TEXTURE_BINDING_CUBE_MAP // nzImageType_Cubemap + }; + + GLenum TextureTargetProxy[nzImageType_Max+1] + { + GL_PROXY_TEXTURE_1D, // nzImageType_1D + GL_PROXY_TEXTURE_1D_ARRAY, // nzImageType_1D_Array + GL_PROXY_TEXTURE_2D, // nzImageType_2D + GL_PROXY_TEXTURE_2D_ARRAY, // nzImageType_2D_Array + GL_PROXY_TEXTURE_3D, // nzImageType_3D + GL_PROXY_TEXTURE_CUBE_MAP // nzImageType_Cubemap + }; +} + +PFNGLACTIVETEXTUREPROC glActiveTexture = nullptr; +PFNGLATTACHSHADERPROC glAttachShader = nullptr; +PFNGLBEGINQUERYPROC glBeginQuery = nullptr; +PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation = nullptr; +PFNGLBINDBUFFERPROC glBindBuffer = nullptr; +PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer = nullptr; +PFNGLBINDFRAGDATALOCATIONPROC glBindFragDataLocation = nullptr; +PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer = nullptr; +PFNGLBINDTEXTUREPROC glBindTexture = nullptr; +PFNGLBINDVERTEXARRAYPROC glBindVertexArray = nullptr; +PFNGLBLENDFUNCPROC glBlendFunc = nullptr; +PFNGLBUFFERDATAPROC glBufferData = nullptr; +PFNGLBUFFERSUBDATAPROC glBufferSubData = nullptr; +PFNGLCLEARPROC glClear = nullptr; +PFNGLCLEARCOLORPROC glClearColor = nullptr; +PFNGLCLEARDEPTHPROC glClearDepth = nullptr; +PFNGLCLEARSTENCILPROC glClearStencil = nullptr; +PFNGLCREATEPROGRAMPROC glCreateProgram = nullptr; +PFNGLCREATESHADERPROC glCreateShader = nullptr; +PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus = nullptr; +PFNGLCOLORMASKPROC glColorMask = nullptr; +PFNGLCULLFACEPROC glCullFace = nullptr; +PFNGLCOMPILESHADERPROC glCompileShader = nullptr; +PFNGLCOPYTEXSUBIMAGE2DPROC glCopyTexSubImage2D = nullptr; +PFNGLDEBUGMESSAGECALLBACKPROC glDebugMessageCallback = nullptr; +PFNGLDEBUGMESSAGECONTROLPROC glDebugMessageControl = nullptr; +PFNGLDEBUGMESSAGEINSERTPROC glDebugMessageInsert = nullptr; +PFNGLDELETEBUFFERSPROC glDeleteBuffers = nullptr; +PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers = nullptr; +PFNGLDELETEPROGRAMPROC glDeleteProgram = nullptr; +PFNGLDELETEQUERIESPROC glDeleteQueries = nullptr; +PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers = nullptr; +PFNGLDELETESHADERPROC glDeleteShader = nullptr; +PFNGLDELETETEXTURESPROC glDeleteTextures = nullptr; +PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays = nullptr; +PFNGLDEPTHFUNCPROC glDepthFunc = nullptr; +PFNGLDEPTHMASKPROC glDepthMask = nullptr; +PFNGLDISABLEPROC glDisable = nullptr; +PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray = nullptr; +PFNGLDRAWARRAYSPROC glDrawArrays = nullptr; +PFNGLDRAWBUFFERPROC glDrawBuffer = nullptr; +PFNGLDRAWBUFFERSPROC glDrawBuffers = nullptr; +PFNGLDRAWELEMENTSPROC glDrawElements = nullptr; +PFNGLENDQUERYPROC glEndQuery = nullptr; +PFNGLFLUSHPROC glFlush = nullptr; +PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer = nullptr; +PFNGLFRAMEBUFFERTEXTUREPROC glFramebufferTexture = nullptr; +PFNGLFRAMEBUFFERTEXTURE1DPROC glFramebufferTexture1D = nullptr; +PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D = nullptr; +PFNGLFRAMEBUFFERTEXTURE3DPROC glFramebufferTexture3D = nullptr; +PFNGLFRAMEBUFFERTEXTURELAYERPROC glFramebufferTextureLayer = nullptr; +PFNGLENABLEPROC glEnable = nullptr; +PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray = nullptr; +PFNGLGENERATEMIPMAPPROC glGenerateMipmap = nullptr; +PFNGLGENBUFFERSPROC glGenBuffers = nullptr; +PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers = nullptr; +PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers = nullptr; +PFNGLGENQUERIESPROC glGenQueries = nullptr; +PFNGLGENTEXTURESPROC glGenTextures = nullptr; +PFNGLGENVERTEXARRAYSPROC glGenVertexArrays = nullptr; +PFNGLGETBUFFERPARAMETERIVPROC glGetBufferParameteriv = nullptr; +PFNGLGETDEBUGMESSAGELOGPROC glGetDebugMessageLog = nullptr; +PFNGLGETERRORPROC glGetError = nullptr; +PFNGLGETINTEGERVPROC glGetIntegerv = nullptr; +PFNGLGETPROGRAMIVPROC glGetProgramiv = nullptr; +PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog = nullptr; +PFNGLGETQUERYIVPROC glGetQueryiv = nullptr; +PFNGLGETQUERYOBJECTIVPROC glGetQueryObjectiv = nullptr; +PFNGLGETQUERYOBJECTUIVPROC glGetQueryObjectuiv = nullptr; +PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog = nullptr; +PFNGLGETSHADERIVPROC glGetShaderiv = nullptr; +PFNGLGETSHADERSOURCEPROC glGetShaderSource = nullptr; +PFNGLGETSTRINGPROC glGetString = nullptr; +PFNGLGETSTRINGIPROC glGetStringi = nullptr; +PFNGLGETTEXIMAGEPROC glGetTexImage = nullptr; +PFNGLGETTEXLEVELPARAMETERFVPROC glGetTexLevelParameterfv = nullptr; +PFNGLGETTEXLEVELPARAMETERIVPROC glGetTexLevelParameteriv = nullptr; +PFNGLGETTEXPARAMETERFVPROC glGetTexParameterfv = nullptr; +PFNGLGETTEXPARAMETERIVPROC glGetTexParameteriv = nullptr; +PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation = nullptr; +PFNGLLINKPROGRAMPROC glLinkProgram = nullptr; +PFNGLMAPBUFFERPROC glMapBuffer = nullptr; +PFNGLMAPBUFFERRANGEPROC glMapBufferRange = nullptr; +PFNGLPIXELSTOREIPROC glPixelStorei = nullptr; +PFNGLPOLYGONMODEPROC glPolygonMode = nullptr; +PFNGLPROGRAMUNIFORM1DPROC glProgramUniform1d = nullptr; +PFNGLPROGRAMUNIFORM1FPROC glProgramUniform1f = nullptr; +PFNGLPROGRAMUNIFORM1IPROC glProgramUniform1i = nullptr; +PFNGLPROGRAMUNIFORM2DVPROC glProgramUniform2dv = nullptr; +PFNGLPROGRAMUNIFORM2FVPROC glProgramUniform2fv = nullptr; +PFNGLPROGRAMUNIFORM3DVPROC glProgramUniform3dv = nullptr; +PFNGLPROGRAMUNIFORM3FVPROC glProgramUniform3fv = nullptr; +PFNGLPROGRAMUNIFORM4DVPROC glProgramUniform4dv = nullptr; +PFNGLPROGRAMUNIFORM4FVPROC glProgramUniform4fv = nullptr; +PFNGLPROGRAMUNIFORMMATRIX4DVPROC glProgramUniformMatrix4dv = nullptr; +PFNGLPROGRAMUNIFORMMATRIX4FVPROC glProgramUniformMatrix4fv = nullptr; +PFNGLREADPIXELSPROC glReadPixels = nullptr; +PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage = nullptr; +PFNGLSCISSORPROC glScissor = nullptr; +PFNGLSHADERSOURCEPROC glShaderSource = nullptr; +PFNGLSTENCILFUNCPROC glStencilFunc = nullptr; +PFNGLSTENCILOPPROC glStencilOp = nullptr; +PFNGLTEXIMAGE1DPROC glTexImage1D = nullptr; +PFNGLTEXIMAGE2DPROC glTexImage2D = nullptr; +PFNGLTEXIMAGE3DPROC glTexImage3D = nullptr; +PFNGLTEXPARAMETERFPROC glTexParameterf = nullptr; +PFNGLTEXPARAMETERIPROC glTexParameteri = nullptr; +PFNGLTEXSTORAGE1DPROC glTexStorage1D = nullptr; +PFNGLTEXSTORAGE2DPROC glTexStorage2D = nullptr; +PFNGLTEXSTORAGE3DPROC glTexStorage3D = nullptr; +PFNGLTEXSUBIMAGE1DPROC glTexSubImage1D = nullptr; +PFNGLTEXSUBIMAGE2DPROC glTexSubImage2D = nullptr; +PFNGLTEXSUBIMAGE3DPROC glTexSubImage3D = nullptr; +PFNGLUNIFORM1DPROC glUniform1d = nullptr; +PFNGLUNIFORM1FPROC glUniform1f = nullptr; +PFNGLUNIFORM1IPROC glUniform1i = nullptr; +PFNGLUNIFORM2DVPROC glUniform2dv = nullptr; +PFNGLUNIFORM2FVPROC glUniform2fv = nullptr; +PFNGLUNIFORM3DVPROC glUniform3dv = nullptr; +PFNGLUNIFORM3FVPROC glUniform3fv = nullptr; +PFNGLUNIFORM4DVPROC glUniform4dv = nullptr; +PFNGLUNIFORM4FVPROC glUniform4fv = nullptr; +PFNGLUNIFORMMATRIX4DVPROC glUniformMatrix4dv = nullptr; +PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv = nullptr; +PFNGLUNMAPBUFFERPROC glUnmapBuffer = nullptr; +PFNGLUSEPROGRAMPROC glUseProgram = nullptr; +PFNGLVERTEXATTRIB4FPROC glVertexAttrib4f = nullptr; +PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer = nullptr; +PFNGLVIEWPORTPROC glViewport = nullptr; +#if defined(NAZARA_PLATFORM_WINDOWS) +PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormat = nullptr; +PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribs = nullptr; +PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = nullptr; +PFNWGLGETEXTENSIONSSTRINGEXTPROC wglGetExtensionsStringEXT = nullptr; +PFNWGLSWAPINTERVALEXTPROC wglSwapInterval = nullptr; +#elif defined(NAZARA_PLATFORM_LINUX) +PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribs = nullptr; +PFNGLXSWAPINTERVALSGIPROC glXSwapInterval = nullptr; +#endif diff --git a/src/Nazara/Renderer/RenderTexture.cpp b/src/Nazara/Renderer/RenderTexture.cpp new file mode 100644 index 000000000..8d5bf552f --- /dev/null +++ b/src/Nazara/Renderer/RenderTexture.cpp @@ -0,0 +1,728 @@ +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + struct Attachment + { + union + { + GLuint buffer; + NzTexture* texture; + }; + + bool isBuffer; + bool isUsed = false; + }; + + unsigned int attachmentIndex[nzAttachmentPoint_Max+1] = + { + 2, // nzAttachmentPoint_Color + 0, // nzAttachmentPoint_Depth + 0, // nzAttachmentPoint_DepthStencil + 1 // nzAttachmentPoint_Stencil + }; + + nzAttachmentPoint formatTypeToAttachment[nzPixelFormatType_Max+1] = + { + nzAttachmentPoint_Color, // nzPixelFormatType_Color + nzAttachmentPoint_Depth, // nzPixelFormatType_Depth + nzAttachmentPoint_DepthStencil, // nzPixelFormatType_DepthStencil + nzAttachmentPoint_Stencil // nzPixelFormatType_Stencil + }; + + GLuint lockedPrevious = 0; + nzUInt8 lockedLevel = 0; +} + +struct NzRenderTextureImpl +{ + GLuint fbo; + std::vector attachements; + std::vector drawBuffers; + NzContext* context; + bool checked = false; + bool complete = false; + bool drawBuffersUpdated = true; + unsigned int height; + unsigned int width; +}; + +NzRenderTexture::~NzRenderTexture() +{ + Destroy(); +} + +bool NzRenderTexture::AttachBuffer(nzAttachmentPoint attachmentPoint, nzUInt8 index, nzPixelFormat format) +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Render texture not created"); + return false; + } + + if (attachmentPoint != nzAttachmentPoint_Color && index > 0) + { + NazaraError("Index must be 0 for non-color attachments"); + return false; + } + + unsigned int depthStencilIndex = attachmentIndex[nzAttachmentPoint_DepthStencil]; + if (attachmentPoint == nzAttachmentPoint_Stencil && m_impl->attachements.size() > depthStencilIndex && + m_impl->attachements[depthStencilIndex].isUsed) + { + NazaraError("Stencil target already attached by DepthStencil attachment"); + return false; + } + + if (formatTypeToAttachment[NzPixelFormat::GetType(format)] != attachmentPoint) + { + NazaraError("Pixel format type does not match attachment point type"); + return false; + } + #endif + + NzOpenGL::Format openglFormat; + if (!NzOpenGL::TranslateFormat(format, &openglFormat, NzOpenGL::FormatType_RenderBuffer)) + { + NazaraError("Failed to translate pixel format into OpenGL format"); + return false; + } + + if (!Lock()) + { + NazaraError("Failed to lock render texture"); + return false; + } + + // Détachement de l'attache précédente (Si il y a) + Detach(attachmentPoint, index); + + GLuint renderBuffer = 0; + + glGenRenderbuffers(1, &renderBuffer); + if (!renderBuffer) + { + NazaraError("Failed to create renderbuffer"); + return false; + } + + GLint previous; + glGetIntegerv(GL_RENDERBUFFER_BINDING, &previous); + + glBindRenderbuffer(GL_RENDERBUFFER, renderBuffer); + glRenderbufferStorage(GL_RENDERBUFFER, openglFormat.internalFormat, m_impl->width, m_impl->height); + + if (previous != 0) + glBindRenderbuffer(GL_RENDERBUFFER, previous); + + glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, NzOpenGL::Attachment[attachmentPoint]+index, GL_RENDERBUFFER, renderBuffer); + Unlock(); + + unsigned int minSize = attachmentIndex[attachmentPoint]+index+1; + if (m_impl->attachements.size() < minSize) + m_impl->attachements.resize(minSize); + + Attachment& attachment = m_impl->attachements[minSize-1]; + attachment.isBuffer = true; + attachment.isUsed = true; + attachment.buffer = renderBuffer; + + m_impl->checked = false; + m_impl->drawBuffersUpdated = false; + + return true; +} + +bool NzRenderTexture::AttachTexture(nzAttachmentPoint attachmentPoint, nzUInt8 index, NzTexture* texture) +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Render texture not created"); + return false; + } + + if (attachmentPoint != nzAttachmentPoint_Color && index > 0) + { + NazaraError("Index must be 0 for non-color attachments"); + return false; + } + + unsigned int depthStencilIndex = attachmentIndex[nzAttachmentPoint_DepthStencil]; + if (attachmentPoint == nzAttachmentPoint_Stencil && m_impl->attachements.size() > depthStencilIndex && + m_impl->attachements[depthStencilIndex].isUsed) + { + NazaraError("Stencil target already attached by DepthStencil attachment"); + return false; + } + + if (!texture || !texture->IsValid()) + { + NazaraError("Invalid texture"); + return false; + } + + if (texture->GetWidth() < m_impl->width || texture->GetHeight() < m_impl->height) + { + NazaraError("Texture cannot be smaller than render texture"); + return false; + } + + if (texture->GetRenderTexture() != nullptr) + { + NazaraError("Texture already used by another render texture"); + return false; + } + + if (formatTypeToAttachment[NzPixelFormat::GetType(texture->GetFormat())] != attachmentPoint) + { + NazaraError("Pixel format type does not match attachment point type"); + return false; + } + #endif + + if (!Lock()) + { + NazaraError("Failed to lock render texture"); + return false; + } + + // Détachement de l'attache précédente (Si il y a) + Detach(attachmentPoint, index); + + glFramebufferTexture(GL_DRAW_FRAMEBUFFER, NzOpenGL::Attachment[attachmentPoint]+index, texture->GetOpenGLID(), 0); + Unlock(); + + unsigned int minSize = attachmentIndex[attachmentPoint]+index+1; + if (m_impl->attachements.size() < minSize) + m_impl->attachements.resize(minSize); + + Attachment& attachment = m_impl->attachements[minSize-1]; + attachment.isBuffer = true; + attachment.isUsed = true; + attachment.texture = texture; + + texture->AddResourceListener(this, minSize-1); + texture->SetRenderTexture(this); + + m_impl->checked = false; + m_impl->drawBuffersUpdated = false; + + return true; +} + +bool NzRenderTexture::AttachTexture(nzAttachmentPoint attachmentPoint, nzUInt8 index, NzTexture* texture, unsigned int z) +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Render texture not created"); + return false; + } + + if (attachmentPoint != nzAttachmentPoint_Color && index > 0) + { + NazaraError("Index must be 0 for non-color attachments"); + return false; + } + + if (attachmentPoint == nzAttachmentPoint_Stencil) + { + NazaraError("Targeting stencil-only textures is not supported"); + return false; + } + + unsigned int depthStencilIndex = attachmentIndex[nzAttachmentPoint_DepthStencil]; + if (attachmentPoint == nzAttachmentPoint_Stencil && m_impl->attachements.size() > depthStencilIndex && + m_impl->attachements[depthStencilIndex].isUsed) + { + NazaraError("Stencil target already attached by DepthStencil attachment"); + return false; + } + + if (!texture || !texture->IsValid()) + { + NazaraError("Invalid texture"); + return false; + } + + if (texture->GetWidth() < m_impl->width || texture->GetHeight() < m_impl->height) + { + NazaraError("Texture cannot be smaller than render texture"); + return false; + } + + unsigned int depth = (texture->GetType() == nzImageType_Cubemap) ? 6 : texture->GetDepth(); + if (z >= depth) + { + NazaraError("Z value exceeds depth (" + NzString::Number(z) + " >= (" + NzString::Number(depth) + ')'); + return false; + } + + if (texture->GetRenderTexture() != nullptr) + { + NazaraError("Texture already used by another render texture"); + return false; + } + + if (formatTypeToAttachment[NzPixelFormat::GetType(texture->GetFormat())] != attachmentPoint) + { + NazaraError("Pixel format type does not match attachment point type"); + return false; + } + #endif + + if (!Lock()) + { + NazaraError("Failed to lock render texture"); + return false; + } + + // Détachement de l'attache précédente (Si il y a) + Detach(attachmentPoint, index); + + switch (texture->GetType()) + { + case nzImageType_1D: + glFramebufferTexture1D(GL_DRAW_FRAMEBUFFER, NzOpenGL::Attachment[attachmentPoint]+index, GL_TEXTURE_1D, texture->GetOpenGLID(), 0); + break; + + case nzImageType_1D_Array: + case nzImageType_2D_Array: + glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, NzOpenGL::Attachment[attachmentPoint]+index, texture->GetOpenGLID(), 0, z); + break; + + case nzImageType_2D: + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, NzOpenGL::Attachment[attachmentPoint]+index, NzOpenGL::TextureTarget[texture->GetType()], texture->GetOpenGLID(), 0); + break; + + case nzImageType_3D: + glFramebufferTexture3D(GL_DRAW_FRAMEBUFFER, NzOpenGL::Attachment[attachmentPoint]+index, GL_TEXTURE_3D, texture->GetOpenGLID(), 0, z); + break; + + case nzImageType_Cubemap: + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, NzOpenGL::Attachment[attachmentPoint]+index, NzOpenGL::CubemapFace[z], texture->GetOpenGLID(), 0); + break; + } + Unlock(); + + unsigned int minSize = attachmentIndex[attachmentPoint]+index+1; + if (m_impl->attachements.size() < minSize) + m_impl->attachements.resize(minSize); + + Attachment& attachment = m_impl->attachements[minSize-1]; + attachment.isBuffer = true; + attachment.isUsed = true; + attachment.texture = texture; + + texture->AddResourceListener(this); + texture->SetRenderTexture(this); + + m_impl->checked = false; + m_impl->drawBuffersUpdated = false; + + return true; +} + +bool NzRenderTexture::Create(unsigned int width, unsigned int height, bool lock) +{ + if (!IsSupported()) + { + NazaraError("Render textures not supported"); + return false; + } + + Destroy(); + + #if NAZARA_RENDERER_SAFE + if (width == 0) + { + NazaraError("Width must be at least 1 (0)"); + return false; + } + + if (height == 0) + { + NazaraError("Height must be at least 1 (0)"); + return false; + } + + if (NzContext::GetCurrent() == nullptr) + { + NazaraError("No active context"); + return false; + } + #endif + + NzRenderTextureImpl* impl = new NzRenderTextureImpl; + impl->width = width; + impl->height = height; + + impl->context = NzContext::GetCurrent(); + impl->context->AddResourceListener(this); + + impl->fbo = 0; + glGenFramebuffers(1, &impl->fbo); + + if (!impl->fbo) + { + NazaraError("Failed to create framebuffer"); + delete impl; + + return false; + } + + m_impl = impl; + + if (lock && !Lock()) + { + NazaraError("Failed to lock render texture"); + delete impl; + m_impl = nullptr; + + return false; + } + + return true; +} + +void NzRenderTexture::Destroy() +{ + if (m_impl) + { + #if NAZARA_RENDERER_SAFE + if (NzContext::GetCurrent() != m_impl->context) + { + NazaraError("RenderTexture can only be used with it's creation context"); + return; + } + #endif + + m_impl->context->RemoveResourceListener(this); + + for (Attachment& attachment : m_impl->attachements) + { + if (attachment.isUsed) + { + if (attachment.isBuffer) + glDeleteRenderbuffers(1, &attachment.buffer); + else + { + attachment.texture->RemoveResourceListener(this); + attachment.texture->SetRenderTexture(nullptr); + } + } + } + + glDeleteFramebuffers(1, &m_impl->fbo); + + delete m_impl; + m_impl = nullptr; + } +} + +void NzRenderTexture::Detach(nzAttachmentPoint attachmentPoint, nzUInt8 index) +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Render texture not created"); + return; + } + + if (attachmentPoint != nzAttachmentPoint_Color && index > 0) + { + NazaraError("Index must be 0 for non-color attachments"); + return; + } + #endif + + unsigned int attachIndex = attachmentIndex[attachmentPoint]+index; + if (attachIndex >= m_impl->attachements.size()) + return; + + Attachment& attachement = m_impl->attachements[attachIndex]; + if (!attachement.isUsed) + return; + + if (!Lock()) + { + NazaraError("Failed to lock render texture"); + return; + } + + attachement.isUsed = false; + + if (attachement.isBuffer) + { + glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, NzOpenGL::Attachment[attachmentPoint]+index, GL_RENDERBUFFER, 0); + glDeleteRenderbuffers(1, &attachement.buffer); + } + else + { + glFramebufferTexture(GL_DRAW_FRAMEBUFFER, NzOpenGL::Attachment[attachmentPoint]+index, 0, 0); + attachement.texture->RemoveResourceListener(this); + attachement.texture->SetRenderTexture(nullptr); + } + + Unlock(); + + m_impl->checked = false; + m_impl->drawBuffersUpdated = false; + +} + +unsigned int NzRenderTexture::GetHeight() const +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Render texture not created"); + return 0; + } + #endif + + return m_impl->height; +} + +NzRenderTargetParameters NzRenderTexture::GetParameters() const +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Render texture not created"); + return NzRenderTargetParameters(); + } + #endif + + return NzRenderTargetParameters(); +} + +unsigned int NzRenderTexture::GetWidth() const +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Render texture not created"); + return 0; + } + #endif + + return m_impl->width; +} + +bool NzRenderTexture::IsComplete() const +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Render texture not created"); + return false; + } + #endif + + if (!m_impl->checked) + { + if (!Lock()) + { + NazaraError("Failed to lock render texture"); + return false; + } + + GLenum status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER); + Unlock(); + + m_impl->complete = false; + + switch (status) + { + case GL_FRAMEBUFFER_COMPLETE: + m_impl->complete = true; + break; + + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + NazaraError("Incomplete attachment"); + break; + + case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: + NazaraInternalError("Incomplete draw buffer"); + break; + + case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: + NazaraInternalError("Incomplete read buffer"); + break; + + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + NazaraError("Incomplete missing attachment"); + break; + + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: + NazaraError("Incomplete multisample"); + break; + + case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: + NazaraError("Incomplete layer targets"); + break; + + case GL_FRAMEBUFFER_UNSUPPORTED: + NazaraError("Render texture has unsupported attachments"); + break; + + default: + NazaraInternalError("Unknown error"); + } + + m_impl->checked = true; + } + + return m_impl->complete; +} + +bool NzRenderTexture::IsRenderable() const +{ + return IsComplete(); +} + +bool NzRenderTexture::IsValid() const +{ + return m_impl != nullptr; +} + +bool NzRenderTexture::Lock() const +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Render texture not created"); + return false; + } + + if (NzContext::GetCurrent() != m_impl->context) + { + NazaraError("RenderTexture cannot be used with this context"); + return false; + } + #endif + + if (lockedLevel++ == 0) + { + GLint previous; + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previous); + + lockedPrevious = previous; + + if (lockedPrevious != m_impl->fbo) + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_impl->fbo); + } + + return true; +} + +void NzRenderTexture::Unlock() const +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Render texture not created"); + return; + } + + if (NzContext::GetCurrent() != m_impl->context) + { + NazaraError("RenderTexture cannot be used with this context"); + return; + } + + if (lockedLevel == 0) + { + NazaraWarning("Unlock called on non-locked texture"); + return; + } + #endif + + if (--lockedLevel == 0 && lockedPrevious != m_impl->fbo) // Ici, il est important qu'un FBO soit débindé si l'ancien était 0 + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, lockedPrevious); +} + +bool NzRenderTexture::HasContext() const +{ + return false; +} + +bool NzRenderTexture::IsSupported() +{ + return NzRenderer::HasCapability(nzRendererCap_RenderTexture); +} + +bool NzRenderTexture::Activate() +{ + #if NAZARA_RENDERER_SAFE + if (NzContext::GetCurrent() != m_impl->context) + { + NazaraError("RenderTexture cannot be used with this context"); + return false; + } + #endif + + if (!m_impl->drawBuffersUpdated) + { + m_impl->drawBuffers.clear(); + m_impl->drawBuffers.reserve(m_impl->attachements.size()); + for (unsigned int i = attachmentIndex[nzAttachmentPoint_Color]; i < m_impl->attachements.size(); ++i) + { + if (m_impl->attachements[i].isUsed) + m_impl->drawBuffers.push_back(GL_COLOR_ATTACHMENT0 + i - attachmentIndex[nzAttachmentPoint_Color]); + } + + m_impl->drawBuffersUpdated = true; + } + + glDrawBuffers(m_impl->drawBuffers.size(), &m_impl->drawBuffers[0]); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_impl->fbo); + + return true; +} + +void NzRenderTexture::Desactivate() +{ + #if NAZARA_RENDERER_SAFE + if (NzContext::GetCurrent() != m_impl->context) + { + NazaraError("RenderTexture cannot be used with this context"); + return; + } + #endif + + glFlush(); + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); +} + +void NzRenderTexture::OnResourceDestroy(const NzResource* resource, int index) +{ + if (resource == m_impl->context) + // Notre context a été détruit, libérons la RenderTexture pour éviter un leak + Destroy(); + else + { + // Sinon, c'est une texture + + // La ressource n'est plus, du coup nous mettons à jour + Attachment& attachement = m_impl->attachements[index]; + attachement.isUsed = false; + + m_impl->checked = false; + m_impl->drawBuffersUpdated = false; + } +} diff --git a/src/Nazara/Renderer/RenderWindow.cpp b/src/Nazara/Renderer/RenderWindow.cpp index ffdbc21cc..a3fb37653 100644 --- a/src/Nazara/Renderer/RenderWindow.cpp +++ b/src/Nazara/Renderer/RenderWindow.cpp @@ -1,278 +1,276 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Renderer module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include -#include -#include - -NzRenderWindow::NzRenderWindow(NzVideoMode mode, const NzString& title, nzUInt32 style, const NzContextParameters& parameters) -{ - Create(mode, title, style, parameters); - - #ifdef NAZARA_DEBUG - if (!m_impl) - { - NazaraError("Failed to create render window"); - throw std::runtime_error("Constructor failed"); - } - #endif -} - -NzRenderWindow::NzRenderWindow(NzWindowHandle handle, const NzContextParameters& parameters) -{ - Create(handle, parameters); - - #ifdef NAZARA_DEBUG - if (!m_impl) - { - NazaraError("Failed to create render window"); - throw std::runtime_error("Constructor failed"); - } - #endif -} - -NzRenderWindow::~NzRenderWindow() -{ - // Nécessaire si NzWindow::Destroy est appelé par son destructeur - OnWindowDestroying(); -} - -bool NzRenderWindow::CopyToImage(NzImage* image) -{ - #if NAZARA_RENDERER_SAFE - if (!m_context) - { - NazaraError("Window has not been created"); - return false; - } - - if (!image) - { - NazaraError("Image must be valid"); - return false; - } - #endif - - if (!m_context->SetActive(true)) - { - NazaraError("Failed to activate context"); - return false; - } - - NzVector2ui size = GetSize(); - - if (!image->Create(nzImageType_2D, nzPixelFormat_RGBA8, size.x, size.y, 1, 1)) - { - NazaraError("Failed to create image"); - return false; - } - - nzUInt8* pixels = image->GetPixels(); - glReadPixels(0, 0, size.x, size.y, GL_RGBA, GL_UNSIGNED_BYTE, pixels); - - image->FlipVertically(); - - return true; -} - -bool NzRenderWindow::CopyToTexture(NzTexture* texture) -{ - #if NAZARA_RENDERER_SAFE - if (!m_context) - { - NazaraError("Window has not been created"); - return false; - } - - if (!texture) - { - NazaraError("Texture must be valid"); - return false; - } - #endif - - if (!m_context->SetActive(true)) - { - NazaraError("Failed to activate context"); - return false; - } - - NzVector2ui size = GetSize(); - - if (!texture->Create(nzImageType_2D, nzPixelFormat_RGBA8, size.x, size.y, 1, 1, true)) - { - NazaraError("Failed to create texture"); - return false; - } - - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, size.x, size.y); - - texture->Unlock(); - - return true; -} - -bool NzRenderWindow::Create(NzVideoMode mode, const NzString& title, nzUInt32 style, const NzContextParameters& parameters) -{ - m_parameters = parameters; - return NzWindow::Create(mode, title, style); -} - -bool NzRenderWindow::Create(NzWindowHandle handle, const NzContextParameters& parameters) -{ - m_parameters = parameters; - return NzWindow::Create(handle); -} - -void NzRenderWindow::Display() -{ - if (m_framerateLimit > 0) - { - int remainingTime = 1000/m_framerateLimit - m_clock.GetMilliseconds(); - if (remainingTime > 0) - NzThread::Sleep(remainingTime); - - m_clock.Restart(); - } - - if (m_context && m_parameters.doubleBuffered) - m_context->SwapBuffers(); -} - -void NzRenderWindow::EnableVerticalSync(bool enabled) -{ - if (m_context) - { - #if defined(NAZARA_PLATFORM_WINDOWS) - if (!m_context->SetActive(true)) - { - NazaraError("Failed to activate context"); - return; - } - - if (wglSwapInterval) - wglSwapInterval(enabled ? 1 : 0); - else - #elif defined(NAZARA_PLATFORM_LINUX) - if (!m_context->SetActive(true)) - { - NazaraError("Failed to activate context"); - return; - } - - if (glXSwapInterval) - glXSwapInterval(enabled ? 1 : 0); - else - #else - #error Vertical Sync is not supported on this platform - #endif - NazaraError("Vertical Sync is not supported on this platform"); - } - else - NazaraError("No context"); -} - -NzContextParameters NzRenderWindow::GetContextParameters() const -{ - if (m_context) - return m_context->GetParameters(); - else - { - NazaraError("Window not created/context not initialized"); - return NzContextParameters(); - } -} - -unsigned int NzRenderWindow::GetHeight() const -{ - return NzWindow::GetHeight(); -} - -NzRenderTargetParameters NzRenderWindow::GetParameters() const -{ - if (m_context) - { - const NzContextParameters& parameters = m_context->GetParameters(); - return NzRenderTargetParameters(parameters.antialiasingLevel, parameters.depthBits, parameters.stencilBits); - } - else - { - NazaraError("Window not created/context not initialized"); - return NzRenderTargetParameters(); - } -} - -unsigned int NzRenderWindow::GetWidth() const -{ - return NzWindow::GetWidth(); -} - -bool NzRenderWindow::HasContext() const -{ - return true; -} - -bool NzRenderWindow::IsValid() const -{ - return m_impl != nullptr && m_context != nullptr; -} - -void NzRenderWindow::SetFramerateLimit(unsigned int limit) -{ - m_framerateLimit = limit; -} - -bool NzRenderWindow::Activate() -{ - if (m_context->SetActive(true)) - { - glDrawBuffer((m_parameters.doubleBuffered) ? GL_BACK : GL_FRONT); - return true; - } - else - { - NazaraError("Failed to activate window's context"); - return false; - } -} - -void NzRenderWindow::OnWindowDestroying() -{ - if (m_context) - { - delete m_context; - m_context = nullptr; - } -} - -bool NzRenderWindow::OnWindowCreated() -{ - m_parameters.doubleBuffered = true; - m_parameters.window = GetHandle(); - - m_context = new NzContext; - if (!m_context->Create(m_parameters)) - { - NazaraError("Failed not create context"); - delete m_context; - - return false; - } - - EnableVerticalSync(false); - - #if NAZARA_RENDERER_ACTIVATE_RENDERWINDOW_ON_CREATION - if (!SetActive(true)) // Les fenêtres s'activent à la création - NazaraWarning("Failed to activate window"); - #endif - - m_clock.Restart(); - - return true; -} +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include +#include + +NzRenderWindow::NzRenderWindow(NzVideoMode mode, const NzString& title, nzUInt32 style, const NzContextParameters& parameters) +{ + Create(mode, title, style, parameters); + + #ifdef NAZARA_DEBUG + if (!m_impl) + { + NazaraError("Failed to create render window"); + throw std::runtime_error("Constructor failed"); + } + #endif +} + +NzRenderWindow::NzRenderWindow(NzWindowHandle handle, const NzContextParameters& parameters) +{ + Create(handle, parameters); + + #ifdef NAZARA_DEBUG + if (!m_impl) + { + NazaraError("Failed to create render window"); + throw std::runtime_error("Constructor failed"); + } + #endif +} + +NzRenderWindow::~NzRenderWindow() +{ + // Nécessaire si NzWindow::Destroy est appelé par son destructeur + OnWindowDestroy(); +} + +bool NzRenderWindow::CopyToImage(NzImage* image) +{ + #if NAZARA_RENDERER_SAFE + if (!m_context) + { + NazaraError("Window has not been created"); + return false; + } + + if (!image) + { + NazaraError("Image must be valid"); + return false; + } + #endif + + if (!m_context->SetActive(true)) + { + NazaraError("Failed to activate context"); + return false; + } + + NzVector2ui size = GetSize(); + + if (!image->Create(nzImageType_2D, nzPixelFormat_RGBA8, size.x, size.y, 1, 1)) + { + NazaraError("Failed to create image"); + return false; + } + + nzUInt8* pixels = image->GetPixels(); + glReadPixels(0, 0, size.x, size.y, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + image->FlipVertically(); + + return true; +} + +bool NzRenderWindow::CopyToTexture(NzTexture* texture) +{ + #if NAZARA_RENDERER_SAFE + if (!m_context) + { + NazaraError("Window has not been created"); + return false; + } + + if (!texture) + { + NazaraError("Texture must be valid"); + return false; + } + #endif + + if (!m_context->SetActive(true)) + { + NazaraError("Failed to activate context"); + return false; + } + + NzVector2ui size = GetSize(); + + if (!texture->Create(nzImageType_2D, nzPixelFormat_RGBA8, size.x, size.y, 1, 1, true)) + { + NazaraError("Failed to create texture"); + return false; + } + + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, size.x, size.y); + + texture->Unlock(); + + return true; +} + +bool NzRenderWindow::Create(NzVideoMode mode, const NzString& title, nzUInt32 style, const NzContextParameters& parameters) +{ + m_parameters = parameters; + return NzWindow::Create(mode, title, style); +} + +bool NzRenderWindow::Create(NzWindowHandle handle, const NzContextParameters& parameters) +{ + m_parameters = parameters; + return NzWindow::Create(handle); +} + +void NzRenderWindow::Display() +{ + if (m_framerateLimit > 0) + { + int remainingTime = 1000/m_framerateLimit - m_clock.GetMilliseconds(); + if (remainingTime > 0) + NzThread::Sleep(remainingTime); + + m_clock.Restart(); + } + + if (m_context && m_parameters.doubleBuffered) + m_context->SwapBuffers(); +} + +void NzRenderWindow::EnableVerticalSync(bool enabled) +{ + if (m_context) + { + #if defined(NAZARA_PLATFORM_WINDOWS) + if (!m_context->SetActive(true)) + { + NazaraError("Failed to activate context"); + return; + } + + if (wglSwapInterval) + wglSwapInterval(enabled ? 1 : 0); + else + #elif defined(NAZARA_PLATFORM_LINUX) + if (!m_context->SetActive(true)) + { + NazaraError("Failed to activate context"); + return; + } + + if (glXSwapInterval) + glXSwapInterval(enabled ? 1 : 0); + else + #else + #error Vertical Sync is not supported on this platform + #endif + NazaraError("Vertical Sync is not supported on this platform"); + } + else + NazaraError("No context"); +} + +unsigned int NzRenderWindow::GetHeight() const +{ + return NzWindow::GetHeight(); +} + +NzRenderTargetParameters NzRenderWindow::GetParameters() const +{ + if (m_context) + { + const NzContextParameters& parameters = m_context->GetParameters(); + return NzRenderTargetParameters(parameters.antialiasingLevel, parameters.depthBits, parameters.stencilBits); + } + else + { + NazaraError("Window not created/context not initialized"); + return NzRenderTargetParameters(); + } +} + +unsigned int NzRenderWindow::GetWidth() const +{ + return NzWindow::GetWidth(); +} + +bool NzRenderWindow::IsRenderable() const +{ + return m_impl != nullptr; // Si m_impl est valide, alors m_context l'est aussi +} + +void NzRenderWindow::SetFramerateLimit(unsigned int limit) +{ + m_framerateLimit = limit; +} + +NzContextParameters NzRenderWindow::GetContextParameters() const +{ + if (m_context) + return m_context->GetParameters(); + else + { + NazaraError("Window not created/context not initialized"); + return NzContextParameters(); + } +} + +bool NzRenderWindow::HasContext() const +{ + return true; +} + +bool NzRenderWindow::Activate() +{ + if (m_context->SetActive(true)) + { + glDrawBuffer((m_parameters.doubleBuffered) ? GL_BACK : GL_FRONT); + return true; + } + else + { + NazaraError("Failed to activate window's context"); + return false; + } +} + +bool NzRenderWindow::OnWindowCreated() +{ + m_parameters.doubleBuffered = true; + m_parameters.window = GetHandle(); + + m_context = new NzContext; + if (!m_context->Create(m_parameters)) + { + NazaraError("Failed not create context"); + delete m_context; + + return false; + } + + EnableVerticalSync(false); + + if (!SetActive(true)) // Les fenêtres s'activent à la création + NazaraWarning("Failed to activate window"); + + m_clock.Restart(); + + return true; +} + +void NzRenderWindow::OnWindowDestroy() +{ + if (m_context) + { + delete m_context; + m_context = nullptr; + } +} diff --git a/src/Nazara/Renderer/Renderer.cpp b/src/Nazara/Renderer/Renderer.cpp index a186fe518..a30725bbd 100644 --- a/src/Nazara/Renderer/Renderer.cpp +++ b/src/Nazara/Renderer/Renderer.cpp @@ -1,1008 +1,902 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Renderer module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include // Pour éviter une redéfinition de WIN32_LEAN_AND_MEAN -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace -{ - const nzUInt8 attribIndex[] = - { - 2, // nzElementUsage_Diffuse - 1, // nzElementUsage_Normal - 0, // nzElementUsage_Position - 3, // nzElementUsage_Tangent - - 4 // nzElementUsage_TexCoord (Doit être le dernier de la liste car extensible) - }; - - - const GLenum blendFunc[] = - { - GL_DST_ALPHA, // nzBlendFunc_DestAlpha - GL_DST_COLOR, // nzBlendFunc_DestColor - GL_SRC_ALPHA, // nzBlendFunc_SrcAlpha - GL_SRC_COLOR, // nzBlendFunc_SrcColor - GL_ONE_MINUS_DST_ALPHA, // nzBlendFunc_InvDestAlpha - GL_ONE_MINUS_DST_COLOR, // nzBlendFunc_InvDestColor - GL_ONE_MINUS_SRC_ALPHA, // nzBlendFunc_InvSrcAlpha - GL_ONE_MINUS_SRC_COLOR, // nzBlendFunc_InvSrcColor - GL_ONE, // nzBlendFunc_One - GL_ZERO // nzBlendFunc_Zero - }; - - const GLenum faceCullingMode[] = - { - GL_BACK, // nzFaceCulling_Back - GL_FRONT, // nzFaceCulling_Front - GL_FRONT_AND_BACK // nzFaceCulling_FrontAndBack - }; - - const GLenum faceFillingMode[] = - { - GL_POINT, // nzFaceFilling_Point - GL_LINE, // nzFaceFilling_Line - GL_FILL // nzFaceFilling_Fill - }; - - const GLenum openglPrimitive[] = - { - GL_LINES, // nzPrimitiveType_LineList, - GL_LINE_STRIP, // nzPrimitiveType_LineStrip, - GL_POINTS, // nzPrimitiveType_PointList, - GL_TRIANGLES, // nzPrimitiveType_TriangleList, - GL_TRIANGLE_STRIP, // nzPrimitiveType_TriangleStrip, - GL_TRIANGLE_FAN // nzPrimitiveType_TriangleFan - }; - - const nzUInt8 openglSize[] = - { - 4, // nzElementType_Color - 1, // nzElementType_Double1 - 2, // nzElementType_Double2 - 3, // nzElementType_Double3 - 4, // nzElementType_Double4 - 1, // nzElementType_Float1 - 2, // nzElementType_Float2 - 3, // nzElementType_Float3 - 4 // nzElementType_Float4 - }; - - const GLenum openglType[] = - { - GL_UNSIGNED_BYTE, // nzElementType_Color - GL_DOUBLE, // nzElementType_Double1 - GL_DOUBLE, // nzElementType_Double2 - GL_DOUBLE, // nzElementType_Double3 - GL_DOUBLE, // nzElementType_Double4 - GL_FLOAT, // nzElementType_Float1 - GL_FLOAT, // nzElementType_Float2 - GL_FLOAT, // nzElementType_Float3 - GL_FLOAT // nzElementType_Float4 - }; - - const GLenum rendererComparison[] = - { - GL_ALWAYS, // nzRendererComparison_Always - GL_EQUAL, // nzRendererComparison_Equal - GL_GREATER, // nzRendererComparison_Greater - GL_GEQUAL, // nzRendererComparison_GreaterOrEqual - GL_LESS, // nzRendererComparison_Less - GL_LEQUAL, // nzRendererComparison_LessOrEqual - GL_NEVER // nzRendererComparison_Never - }; - - const GLenum rendererParameter[] = - { - GL_BLEND, // nzRendererParameter_Blend - GL_NONE, // nzRendererParameter_ColorWrite - GL_DEPTH_TEST, // nzRendererParameter_DepthTest - GL_NONE, // nzRendererParameter_DepthWrite - GL_CULL_FACE, // nzRendererParameter_FaceCulling - GL_STENCIL_TEST // nzRendererParameter_Stencil - }; - - const GLenum stencilOperation[] = - { - GL_DECR, // nzStencilOperation_Decrement - GL_DECR_WRAP, // nzStencilOperation_DecrementToSaturation - GL_INCR, // nzStencilOperation_Increment - GL_INCR_WRAP, // nzStencilOperation_IncrementToSaturation - GL_INVERT, // nzStencilOperation_Invert - GL_KEEP, // nzStencilOperation_Keep - GL_REPLACE, // nzStencilOperation_Replace - GL_ZERO // nzStencilOperation_Zero - }; - - ///FIXME: Solution temporaire pour plus de facilité - enum nzMatrixCombination - { - nzMatrixCombination_ViewProj = nzMatrixType_Max+1, - nzMatrixCombination_WorldView, - nzMatrixCombination_WorldViewProj, - - nzMatrixCombination_Max = nzMatrixCombination_WorldViewProj - }; - - NzBufferImpl* HardwareBufferFunction(NzBuffer* parent, nzBufferType type) - { - return new NzHardwareBuffer(parent, type); - } - - constexpr unsigned int totalMatrixCount = nzMatrixCombination_Max+1; - - using VAO_Key = std::tuple; - - std::map s_vaos; - NzMatrix4f s_matrix[totalMatrixCount]; - int s_matrixLocation[totalMatrixCount]; - bool s_matrixUpdated[totalMatrixCount]; - nzRendererComparison s_stencilCompare; - nzStencilOperation s_stencilFail; - nzStencilOperation s_stencilPass; - nzStencilOperation s_stencilZFail; - nzUInt32 s_stencilMask; - const NzIndexBuffer* s_indexBuffer; - NzRenderTarget* s_target; - NzShader* s_shader; - const NzVertexBuffer* s_vertexBuffer; - const NzVertexDeclaration* s_vertexDeclaration; - bool s_vaoUpdated; - bool s_capabilities[nzRendererCap_Max+1]; - bool s_stencilFuncUpdated; - bool s_stencilOpUpdated; - unsigned int s_maxAnisotropyLevel; - unsigned int s_maxRenderTarget; - unsigned int s_maxTextureUnit; - unsigned int s_stencilReference; -} - -void NzRenderer::Clear(unsigned long flags) -{ - #ifdef NAZARA_DEBUG - if (NzContext::GetCurrent() == nullptr) - { - NazaraError("No active context"); - return; - } - #endif - - if (flags) - { - GLenum mask = 0; - - if (flags & nzRendererClear_Color) - mask |= GL_COLOR_BUFFER_BIT; - - if (flags & nzRendererClear_Depth) - mask |= GL_DEPTH_BUFFER_BIT; - - if (flags & nzRendererClear_Stencil) - mask |= GL_STENCIL_BUFFER_BIT; - - glClear(mask); - } -} - -void NzRenderer::DrawIndexedPrimitives(nzPrimitiveType primitive, unsigned int firstIndex, unsigned int indexCount) -{ - #ifdef NAZARA_DEBUG - if (NzContext::GetCurrent() == nullptr) - { - NazaraError("No active context"); - return; - } - #endif - - #if NAZARA_RENDERER_SAFE - if (!s_indexBuffer) - { - NazaraError("No index buffer"); - return; - } - #endif - - if (!EnsureStateUpdate()) - { - NazaraError("Failed to update states"); - return; - } - - if (s_indexBuffer->IsSequential()) - glDrawArrays(openglPrimitive[primitive], s_indexBuffer->GetStartIndex(), s_indexBuffer->GetIndexCount()); - else - { - nzUInt8 indexSize = s_indexBuffer->GetIndexSize(); - - GLenum type; - switch (indexSize) - { - case 1: - type = GL_UNSIGNED_BYTE; - break; - - case 2: - type = GL_UNSIGNED_SHORT; - break; - - case 4: - type = GL_UNSIGNED_INT; - break; - - default: - NazaraError("Invalid index size (" + NzString::Number(indexSize) + ')'); - return; - } - - glDrawElements(openglPrimitive[primitive], indexCount, type, reinterpret_cast(s_indexBuffer->GetPointer()) + firstIndex*indexSize); - } -} - -void NzRenderer::DrawPrimitives(nzPrimitiveType primitive, unsigned int firstVertex, unsigned int vertexCount) -{ - #ifdef NAZARA_DEBUG - if (NzContext::GetCurrent() == nullptr) - { - NazaraError("No active context"); - return; - } - #endif - - if (!EnsureStateUpdate()) - { - NazaraError("Failed to update states"); - return; - } - - glDrawArrays(openglPrimitive[primitive], firstVertex, vertexCount); -} - -void NzRenderer::Enable(nzRendererParameter parameter, bool enable) -{ - #ifdef NAZARA_DEBUG - if (NzContext::GetCurrent() == nullptr) - { - NazaraError("No active context"); - return; - } - #endif - - switch (parameter) - { - case nzRendererParameter_ColorWrite: - glColorMask(enable, enable, enable, enable); - break; - - case nzRendererParameter_DepthWrite: - glDepthMask(enable); - break; - - default: - if (enable) - glEnable(rendererParameter[parameter]); - else - glDisable(rendererParameter[parameter]); - - break; - } -} -/* -NzMatrix4f NzRenderer::GetMatrix(nzMatrixCombination combination) -{ - switch (combination) - { - case nzMatrixCombination_ViewProj: - if (!s_matrixUpdated[nzMatrixCombination_ViewProj]) - { - s_matrix[nzMatrixCombination_ViewProj] = s_matrix[nzMatrixType_View] * s_matrix[nzMatrixType_Projection]; - s_matrixUpdated[nzMatrixCombination_ViewProj] = true; - } - break; - - case nzMatrixCombination_WorldView: - if (!s_matrixUpdated[nzMatrixCombination_WorldView]) - { - s_matrix[nzMatrixCombination_WorldView] = NzMatrix4f::ConcatenateAffine(s_matrix[nzMatrixType_World], s_matrix[nzMatrixType_View]); - s_matrixUpdated[nzMatrixCombination_WorldView] = true; - } - break; - - case nzMatrixCombination_WorldViewProj: - if (!s_matrixUpdated[nzMatrixCombination_WorldViewProj]) - { - s_matrix[nzMatrixCombination_WorldViewProj] = s_matrix[nzMatrixCombination_WorldView] * s_matrix[nzMatrixType_Projection]; - s_matrixUpdated[nzMatrixCombination_WorldViewProj] = true; - } - break; - } - - return m_matrix[combination]; -} -*/ -NzMatrix4f NzRenderer::GetMatrix(nzMatrixType type) -{ - return s_matrix[type]; -} - -unsigned int NzRenderer::GetMaxAnisotropyLevel() -{ - return s_maxAnisotropyLevel; -} - -unsigned int NzRenderer::GetMaxRenderTargets() -{ - return s_maxRenderTarget; -} - -unsigned int NzRenderer::GetMaxTextureUnits() -{ - return s_maxTextureUnit; -} - -NzShader* NzRenderer::GetShader() -{ - return s_shader; -} - -NzRenderTarget* NzRenderer::GetTarget() -{ - return s_target; -} - -NzRectui NzRenderer::GetViewport() -{ - #ifdef NAZARA_DEBUG - if (NzContext::GetCurrent() == nullptr) - { - NazaraError("No active context"); - return NzRectui(); - } - #endif - - GLint params[4]; - glGetIntegerv(GL_VIEWPORT, ¶ms[0]); - - return NzRectui(params[0], params[1], params[2], params[3]); -} - -bool NzRenderer::HasCapability(nzRendererCap capability) -{ - return s_capabilities[capability]; -} - -bool NzRenderer::Initialize() -{ - if (s_moduleReferenceCouter++ != 0) - return true; // Déjà initialisé - - // Initialisation des dépendances - if (!NzUtility::Initialize()) - { - NazaraError("Failed to initialize utility module"); - return false; - } - - // Initialisation du module - if (!NzOpenGL::Initialize()) - { - NazaraError("Failed to initialize OpenGL"); - return false; - } - - NzContext::EnsureContext(); - - for (unsigned int i = 0; i < totalMatrixCount; ++i) - { - s_matrix[i].MakeIdentity(); - s_matrixLocation[i] = -1; - s_matrixUpdated[i] = false; - } - - s_indexBuffer = nullptr; - s_shader = nullptr; - s_stencilCompare = nzRendererComparison_Always; - s_stencilFail = nzStencilOperation_Keep; - s_stencilFuncUpdated = true; - s_stencilMask = 0xFFFFFFFF; - s_stencilOpUpdated = true; - s_stencilPass = nzStencilOperation_Keep; - s_stencilReference = 0; - s_stencilZFail = nzStencilOperation_Keep; - s_target = nullptr; - s_vaoUpdated = false; - s_vertexBuffer = nullptr; - s_vertexDeclaration = nullptr; - - // Récupération des capacités - s_capabilities[nzRendererCap_AnisotropicFilter] = NzOpenGL::IsSupported(NzOpenGL::AnisotropicFilter); - s_capabilities[nzRendererCap_FP64] = NzOpenGL::IsSupported(NzOpenGL::FP64); - s_capabilities[nzRendererCap_HardwareBuffer] = true; // Natif depuis OpenGL 1.5 - s_capabilities[nzRendererCap_MultipleRenderTargets] = true; // Natif depuis OpenGL 2.0 - s_capabilities[nzRendererCap_OcclusionQuery] = true; // Natif depuis OpenGL 1.5 - s_capabilities[nzRendererCap_PixelBufferObject] = NzOpenGL::IsSupported(NzOpenGL::PixelBufferObject); - s_capabilities[nzRendererCap_Texture3D] = NzOpenGL::IsSupported(NzOpenGL::Texture3D); - s_capabilities[nzRendererCap_TextureCubemap] = true; // Natif depuis OpenGL 1.3 - s_capabilities[nzRendererCap_TextureMulti] = true; // Natif depuis OpenGL 1.3 - s_capabilities[nzRendererCap_TextureNPOT] = true; // Natif depuis OpenGL 2.0 - - if (s_capabilities[nzRendererCap_AnisotropicFilter]) - { - GLint maxAnisotropy; - glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropy); - - s_maxAnisotropyLevel = static_cast(maxAnisotropy); - } - else - s_maxAnisotropyLevel = 1; - - if (s_capabilities[nzRendererCap_MultipleRenderTargets]) - { - GLint maxDrawBuffers; - glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers); - - s_maxRenderTarget = static_cast(maxDrawBuffers); - } - else - s_maxRenderTarget = 1; - - if (s_capabilities[nzRendererCap_TextureMulti]) - { - GLint maxTextureUnits; - glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits); - - GLint maxVertexAttribs; - glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs); - - // Impossible de binder plus de texcoords que d'attributes (en sachant qu'un certain nombre est déjà pris par les autres attributs) - s_maxTextureUnit = static_cast(std::min(maxTextureUnits, maxVertexAttribs-attribIndex[nzElementUsage_TexCoord])); - } - else - s_maxTextureUnit = 1; - - NzBuffer::SetBufferFunction(nzBufferStorage_Hardware, HardwareBufferFunction); - - NazaraNotice("Initialized: Renderer module"); - - return true; -} - -bool NzRenderer::IsInitialized() -{ - return s_moduleReferenceCouter != 0; -} - -void NzRenderer::SetBlendFunc(nzBlendFunc src, nzBlendFunc dest) -{ - #ifdef NAZARA_DEBUG - if (NzContext::GetCurrent() == nullptr) - { - NazaraError("No active context"); - return; - } - #endif - - glBlendFunc(blendFunc[src], blendFunc[dest]); -} - -void NzRenderer::SetClearColor(const NzColor& color) -{ - #ifdef NAZARA_DEBUG - if (NzContext::GetCurrent() == nullptr) - { - NazaraError("No active context"); - return; - } - #endif - - glClearColor(color.r/255.f, color.g/255.f, color.b/255.f, color.a/255.f); -} - -void NzRenderer::SetClearColor(nzUInt8 r, nzUInt8 g, nzUInt8 b, nzUInt8 a) -{ - #ifdef NAZARA_DEBUG - if (NzContext::GetCurrent() == nullptr) - { - NazaraError("No active context"); - return; - } - #endif - - glClearColor(r/255.f, g/255.f, b/255.f, a/255.f); -} - -void NzRenderer::SetClearDepth(double depth) -{ - #ifdef NAZARA_DEBUG - if (NzContext::GetCurrent() == nullptr) - { - NazaraError("No active context"); - return; - } - #endif - - glClearDepth(depth); -} - -void NzRenderer::SetClearStencil(unsigned int value) -{ - #ifdef NAZARA_DEBUG - if (NzContext::GetCurrent() == nullptr) - { - NazaraError("No active context"); - return; - } - #endif - - glClearStencil(value); -} - -void NzRenderer::SetFaceCulling(nzFaceCulling cullingMode) -{ - #ifdef NAZARA_DEBUG - if (NzContext::GetCurrent() == nullptr) - { - NazaraError("No active context"); - return; - } - #endif - - glCullFace(faceCullingMode[cullingMode]); -} - -void NzRenderer::SetFaceFilling(nzFaceFilling fillingMode) -{ - #ifdef NAZARA_DEBUG - if (NzContext::GetCurrent() == nullptr) - { - NazaraError("No active context"); - return; - } - #endif - - glPolygonMode(GL_FRONT_AND_BACK, faceFillingMode[fillingMode]); -} - -bool NzRenderer::SetIndexBuffer(const NzIndexBuffer* indexBuffer) -{ - #if NAZARA_RENDERER_SAFE - if (indexBuffer && !indexBuffer->IsHardware() && !indexBuffer->IsSequential()) - { - NazaraError("Buffer must be hardware"); - return false; - } - #endif - - if (s_indexBuffer != indexBuffer) - { - s_indexBuffer = indexBuffer; - s_vaoUpdated = false; - } - - return true; -} - -void NzRenderer::SetMatrix(nzMatrixType type, const NzMatrix4f& matrix) -{ - s_matrix[type] = matrix; - - // Cas particulier, la matrice projection doit être inversée sur l'axe Y à cause des conventions d'OpenGL - if (type == nzMatrixType_Projection) - s_matrix[type] *= NzMatrix4f::Scale(NzVector3f(1.f, -1.f, 1.f)); - - // Invalidation des combinaisons - switch (type) - { - case nzMatrixType_View: - case nzMatrixType_World: - s_matrixUpdated[nzMatrixCombination_WorldView] = false; - case nzMatrixType_Projection: - s_matrixUpdated[nzMatrixCombination_WorldViewProj] = false; - s_matrixUpdated[nzMatrixCombination_ViewProj] = false; - break; - } -} - -bool NzRenderer::SetShader(NzShader* shader) -{ - if (s_shader == shader) - return true; - - if (s_shader) - s_shader->m_impl->Unbind(); - - if (shader) - { - #if NAZARA_RENDERER_SAFE - if (!shader->IsCompiled()) - { - NazaraError("Shader is not compiled"); - shader = nullptr; - - return false; - } - #endif - - if (!shader->m_impl->Bind()) - { - NazaraError("Failed to bind shader"); - shader = nullptr; - - return false; - } - - // Récupération des indices des variables uniformes (-1 si la variable n'existe pas) - s_matrixLocation[nzMatrixType_Projection] = shader->GetUniformLocation("ProjMatrix"); - s_matrixLocation[nzMatrixType_View] = shader->GetUniformLocation("ViewMatrix"); - s_matrixLocation[nzMatrixType_World] = shader->GetUniformLocation("WorldMatrix"); - - s_matrixLocation[nzMatrixCombination_ViewProj] = shader->GetUniformLocation("ViewProjMatrix"); - s_matrixLocation[nzMatrixCombination_WorldView] = shader->GetUniformLocation("WorldViewMatrix"); - s_matrixLocation[nzMatrixCombination_WorldViewProj] = shader->GetUniformLocation("WorldViewProjMatrix"); - } - - s_shader = shader; - - return true; -} - -void NzRenderer::SetStencilCompareFunction(nzRendererComparison compareFunc) -{ - if (compareFunc != s_stencilCompare) - { - s_stencilCompare = compareFunc; - s_stencilFuncUpdated = false; - } -} - -void NzRenderer::SetStencilFailOperation(nzStencilOperation failOperation) -{ - if (failOperation != s_stencilFail) - { - s_stencilFail = failOperation; - s_stencilOpUpdated = false; - } -} - -void NzRenderer::SetStencilMask(nzUInt32 mask) -{ - if (mask != s_stencilMask) - { - s_stencilMask = mask; - s_stencilFuncUpdated = false; - } -} - -void NzRenderer::SetStencilPassOperation(nzStencilOperation passOperation) -{ - if (passOperation != s_stencilPass) - { - s_stencilPass = passOperation; - s_stencilOpUpdated = false; - } -} - -void NzRenderer::SetStencilReferenceValue(unsigned int refValue) -{ - if (refValue != s_stencilReference) - { - s_stencilReference = refValue; - s_stencilFuncUpdated = false; - } -} - -void NzRenderer::SetStencilZFailOperation(nzStencilOperation zfailOperation) -{ - if (zfailOperation != s_stencilZFail) - { - s_stencilZFail = zfailOperation; - s_stencilOpUpdated = false; - } -} - -bool NzRenderer::SetTarget(NzRenderTarget* target) -{ - if (s_target == target) - return true; - - if (s_target && !target->HasContext()) - s_target->Desactivate(); - - if (target) - { - #if NAZARA_RENDERER_SAFE - if (!target->IsValid()) - { - NazaraError("Target not valid"); - return false; - } - #endif - - if (target->Activate()) - s_target = target; - else - { - NazaraError("Failed to activate target"); - s_target = nullptr; - - return false; - } - } - else - s_target = nullptr; - - return true; -} - -bool NzRenderer::SetVertexBuffer(const NzVertexBuffer* vertexBuffer) -{ - #if NAZARA_RENDERER_SAFE - if (vertexBuffer && !vertexBuffer->IsHardware()) - { - NazaraError("Buffer must be hardware"); - return false; - } - #endif - - if (s_vertexBuffer != vertexBuffer) - { - s_vertexBuffer = vertexBuffer; - s_vaoUpdated = false; - } - - return true; -} - -bool NzRenderer::SetVertexDeclaration(const NzVertexDeclaration* vertexDeclaration) -{ - if (s_vertexDeclaration != vertexDeclaration) - { - s_vertexDeclaration = vertexDeclaration; - s_vaoUpdated = false; - } - - return true; -} - -void NzRenderer::SetViewport(const NzRectui& viewport) -{ - #ifdef NAZARA_DEBUG - if (NzContext::GetCurrent() == nullptr) - { - NazaraError("No active context"); - return; - } - #endif - - unsigned int height = s_target->GetHeight(); - - #if NAZARA_RENDERER_SAFE - if (!s_target) - { - NazaraError("Renderer has no target"); - return; - } - - unsigned int width = s_target->GetWidth(); - if (viewport.x+viewport.width > width || viewport.y+viewport.height > height) - { - NazaraError("Rectangle dimensions are out of bounds"); - return; - } - #endif - - glViewport(viewport.x, height-viewport.height-viewport.y, viewport.width, viewport.height); - glScissor(viewport.x, height-viewport.height-viewport.y, viewport.width, viewport.height); -} - -void NzRenderer::Uninitialize() -{ - if (--s_moduleReferenceCouter != 0) - return; // Encore utilisé - - // Libération du module - NzContext::EnsureContext(); - - // Libération des VAOs - for (auto it = s_vaos.begin(); it != s_vaos.end(); ++it) - { - GLuint vao = static_cast(it->second); - glDeleteVertexArrays(1, &vao); - } - - NzOpenGL::Uninitialize(); - - NazaraNotice("Uninitialized: Renderer module"); - - // Libération des dépendances - NzUtility::Uninitialize(); -} - -bool NzRenderer::EnsureStateUpdate() -{ - #ifdef NAZARA_DEBUG - if (NzContext::GetCurrent() == nullptr) - { - NazaraError("No active context"); - return false; - } - #endif - - #if NAZARA_RENDERER_SAFE - if (!s_shader) - { - NazaraError("No shader"); - return false; - } - #endif - - // Il est plus rapide d'opérer sur l'implémentation du shader directement - NzShaderImpl* shaderImpl = s_shader->m_impl; - - if (!shaderImpl->BindTextures()) - NazaraWarning("Failed to bind textures"); - - for (unsigned int i = 0; i <= nzMatrixType_Max; ++i) - { - if (!s_matrixUpdated[i]) - { - shaderImpl->SendMatrix(s_matrixLocation[i], s_matrix[i]); - s_matrixUpdated[i] = true; - } - } - - // Cas spéciaux car il faut recalculer la matrice - if (!s_matrixUpdated[nzMatrixCombination_ViewProj]) - { - s_matrix[nzMatrixCombination_ViewProj] = s_matrix[nzMatrixType_View] * s_matrix[nzMatrixType_Projection]; - - shaderImpl->SendMatrix(s_matrixLocation[nzMatrixCombination_ViewProj], s_matrix[nzMatrixCombination_ViewProj]); - s_matrixUpdated[nzMatrixCombination_ViewProj] = true; - } - - if (!s_matrixUpdated[nzMatrixCombination_WorldView]) - { - s_matrix[nzMatrixCombination_WorldView] = NzMatrix4f::ConcatenateAffine(s_matrix[nzMatrixType_World], s_matrix[nzMatrixType_View]); - - shaderImpl->SendMatrix(s_matrixLocation[nzMatrixCombination_WorldView], s_matrix[nzMatrixCombination_WorldView]); - s_matrixUpdated[nzMatrixCombination_WorldView] = true; - } - - if (!s_matrixUpdated[nzMatrixCombination_WorldViewProj]) - { - s_matrix[nzMatrixCombination_WorldViewProj] = s_matrix[nzMatrixCombination_WorldView] * s_matrix[nzMatrixType_Projection]; - - shaderImpl->SendMatrix(s_matrixLocation[nzMatrixCombination_WorldViewProj], s_matrix[nzMatrixCombination_WorldViewProj]); - s_matrixUpdated[nzMatrixCombination_WorldViewProj] = true; - } - - if (!s_stencilFuncUpdated) - { - glStencilFunc(rendererComparison[s_stencilCompare], s_stencilReference, s_stencilMask); - s_stencilFuncUpdated = true; - } - - if (!s_stencilOpUpdated) - { - glStencilOp(stencilOperation[s_stencilFail], stencilOperation[s_stencilZFail], stencilOperation[s_stencilPass]); - s_stencilOpUpdated = true; - } - - if (!s_vaoUpdated) - { - #if NAZARA_RENDERER_SAFE - if (!s_vertexBuffer) - { - NazaraError("No vertex buffer"); - return false; - } - - if (!s_vertexDeclaration) - { - NazaraError("No vertex declaration"); - return false; - } - #endif - - static const bool vaoSupported = NzOpenGL::IsSupported(NzOpenGL::VertexArrayObject); - bool update; - GLuint vao; - - // Si les VAOs sont supportés, on entoure nos appels par ceux-ci - if (vaoSupported) - { - // On recherche si un VAO existe déjà avec notre configuration - // Note: Les VAOs ne sont pas partagés entre les contextes, ces derniers font donc partie de notre configuration - - auto key = std::make_tuple(NzContext::GetCurrent(), s_indexBuffer, s_vertexBuffer, s_vertexDeclaration); - auto it = s_vaos.find(key); - if (it == s_vaos.end()) - { - // On créé notre VAO - glGenVertexArrays(1, &vao); - glBindVertexArray(vao); - - // On l'ajoute à notre liste - s_vaos.insert(std::make_pair(key, static_cast(vao))); - - // Et on indique qu'on veut le programmer - update = true; - } - else - { - // Notre VAO existe déjà, il est donc inutile de le reprogrammer - vao = it->second; - - update = false; - } - } - else - update = true; // Fallback si les VAOs ne sont pas supportés - - if (update) - { - NzHardwareBuffer* vertexBufferImpl = static_cast(s_vertexBuffer->GetBuffer()->GetImpl()); - vertexBufferImpl->Bind(); - - const nzUInt8* buffer = reinterpret_cast(s_vertexBuffer->GetPointer()); - - unsigned int stride = s_vertexDeclaration->GetStride(nzElementStream_VertexData); - for (unsigned int i = 0; i <= nzElementUsage_Max; ++i) - { - const NzVertexElement* element = s_vertexDeclaration->GetElement(nzElementStream_VertexData, static_cast(i)); - - if (element) - { - glEnableVertexAttribArray(attribIndex[i]); - glVertexAttribPointer(attribIndex[i], - openglSize[element->type], - openglType[element->type], - (element->type == nzElementType_Color) ? GL_TRUE : GL_FALSE, - stride, - &buffer[element->offset]); - } - else - glDisableVertexAttribArray(attribIndex[i]); - } - - if (s_indexBuffer) - { - NzHardwareBuffer* indexBufferImpl = static_cast(s_indexBuffer->GetBuffer()->GetImpl()); - indexBufferImpl->Bind(); - } - } - - if (vaoSupported) - { - // Si nous venons de définir notre VAO, nous devons le débinder pour indiquer la fin de sa construction - if (update) - glBindVertexArray(0); - - // Nous (re)bindons le VAO pour définir les attributs de vertice - glBindVertexArray(vao); - } - - s_vaoUpdated = true; - } - - return true; -} - -unsigned int NzRenderer::s_moduleReferenceCouter = 0; +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include // Pour éviter une redéfinition de WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + ///FIXME: Solution temporaire pour plus de facilité + enum nzMatrixCombination + { + nzMatrixCombination_ViewProj = nzMatrixType_Max+1, + nzMatrixCombination_WorldView, + nzMatrixCombination_WorldViewProj, + + nzMatrixCombination_Max = nzMatrixCombination_WorldViewProj + }; + + NzBufferImpl* HardwareBufferFunction(NzBuffer* parent, nzBufferType type) + { + return new NzHardwareBuffer(parent, type); + } + + constexpr unsigned int totalMatrixCount = nzMatrixCombination_Max+1; + + using VAO_Key = std::tuple; + + std::map s_vaos; + NzMatrix4f s_matrix[totalMatrixCount]; + int s_matrixLocation[totalMatrixCount]; + bool s_matrixUpdated[totalMatrixCount]; + nzRendererComparison s_stencilCompare; + nzStencilOperation s_stencilFail; + nzStencilOperation s_stencilPass; + nzStencilOperation s_stencilZFail; + nzUInt32 s_stencilMask; + const NzIndexBuffer* s_indexBuffer; + NzRenderTarget* s_target; + NzShader* s_shader; + const NzVertexBuffer* s_vertexBuffer; + const NzVertexDeclaration* s_vertexDeclaration; + bool s_vaoUpdated; + bool s_capabilities[nzRendererCap_Max+1]; + bool s_stencilFuncUpdated; + bool s_stencilOpUpdated; + unsigned int s_maxAnisotropyLevel; + unsigned int s_maxRenderTarget; + unsigned int s_maxTextureUnit; + unsigned int s_stencilReference; +} + +void NzRenderer::Clear(unsigned long flags) +{ + #ifdef NAZARA_DEBUG + if (NzContext::GetCurrent() == nullptr) + { + NazaraError("No active context"); + return; + } + #endif + + if (flags) + { + GLenum mask = 0; + + if (flags & nzRendererClear_Color) + mask |= GL_COLOR_BUFFER_BIT; + + if (flags & nzRendererClear_Depth) + mask |= GL_DEPTH_BUFFER_BIT; + + if (flags & nzRendererClear_Stencil) + mask |= GL_STENCIL_BUFFER_BIT; + + glClear(mask); + } +} + +void NzRenderer::DrawIndexedPrimitives(nzPrimitiveType primitive, unsigned int firstIndex, unsigned int indexCount) +{ + #ifdef NAZARA_DEBUG + if (NzContext::GetCurrent() == nullptr) + { + NazaraError("No active context"); + return; + } + #endif + + #if NAZARA_RENDERER_SAFE + if (!s_indexBuffer) + { + NazaraError("No index buffer"); + return; + } + #endif + + if (!EnsureStateUpdate()) + { + NazaraError("Failed to update states"); + return; + } + + if (s_indexBuffer->IsSequential()) + glDrawArrays(NzOpenGL::PrimitiveType[primitive], s_indexBuffer->GetStartIndex(), s_indexBuffer->GetIndexCount()); + else + { + nzUInt8 indexSize = s_indexBuffer->GetIndexSize(); + + GLenum type; + switch (indexSize) + { + case 1: + type = GL_UNSIGNED_BYTE; + break; + + case 2: + type = GL_UNSIGNED_SHORT; + break; + + case 4: + type = GL_UNSIGNED_INT; + break; + + default: + NazaraError("Invalid index size (" + NzString::Number(indexSize) + ')'); + return; + } + + glDrawElements(NzOpenGL::PrimitiveType[primitive], indexCount, type, reinterpret_cast(s_indexBuffer->GetPointer()) + firstIndex*indexSize); + } +} + +void NzRenderer::DrawPrimitives(nzPrimitiveType primitive, unsigned int firstVertex, unsigned int vertexCount) +{ + #ifdef NAZARA_DEBUG + if (NzContext::GetCurrent() == nullptr) + { + NazaraError("No active context"); + return; + } + #endif + + if (!EnsureStateUpdate()) + { + NazaraError("Failed to update states"); + return; + } + + glDrawArrays(NzOpenGL::PrimitiveType[primitive], firstVertex, vertexCount); +} + +void NzRenderer::Enable(nzRendererParameter parameter, bool enable) +{ + #ifdef NAZARA_DEBUG + if (NzContext::GetCurrent() == nullptr) + { + NazaraError("No active context"); + return; + } + #endif + + switch (parameter) + { + case nzRendererParameter_ColorWrite: + glColorMask(enable, enable, enable, enable); + break; + + case nzRendererParameter_DepthWrite: + glDepthMask(enable); + break; + + default: + if (enable) + glEnable(NzOpenGL::RendererParameter[parameter]); + else + glDisable(NzOpenGL::RendererParameter[parameter]); + + break; + } +} +/* +NzMatrix4f NzRenderer::GetMatrix(nzMatrixCombination combination) +{ + ///FIXME: Duplication + switch (combination) + { + case nzMatrixCombination_ViewProj: + if (!s_matrixUpdated[nzMatrixCombination_ViewProj]) + { + s_matrix[nzMatrixCombination_ViewProj] = s_matrix[nzMatrixType_View] * s_matrix[nzMatrixType_Projection]; + s_matrixUpdated[nzMatrixCombination_ViewProj] = true; + } + break; + + case nzMatrixCombination_WorldView: + if (!s_matrixUpdated[nzMatrixCombination_WorldView]) + { + s_matrix[nzMatrixCombination_WorldView] = NzMatrix4f::ConcatenateAffine(s_matrix[nzMatrixType_World], s_matrix[nzMatrixType_View]); + s_matrixUpdated[nzMatrixCombination_WorldView] = true; + } + break; + + case nzMatrixCombination_WorldViewProj: + if (!s_matrixUpdated[nzMatrixCombination_WorldViewProj]) + { + s_matrix[nzMatrixCombination_WorldViewProj] = s_matrix[nzMatrixCombination_WorldView] * s_matrix[nzMatrixType_Projection]; + s_matrixUpdated[nzMatrixCombination_WorldViewProj] = true; + } + break; + } + + return m_matrix[combination]; +} +*/ +NzMatrix4f NzRenderer::GetMatrix(nzMatrixType type) +{ + return s_matrix[type]; +} + +unsigned int NzRenderer::GetMaxAnisotropyLevel() +{ + return s_maxAnisotropyLevel; +} + +unsigned int NzRenderer::GetMaxRenderTargets() +{ + return s_maxRenderTarget; +} + +unsigned int NzRenderer::GetMaxTextureUnits() +{ + return s_maxTextureUnit; +} + +NzShader* NzRenderer::GetShader() +{ + return s_shader; +} + +NzRenderTarget* NzRenderer::GetTarget() +{ + return s_target; +} + +NzRectui NzRenderer::GetViewport() +{ + #ifdef NAZARA_DEBUG + if (NzContext::GetCurrent() == nullptr) + { + NazaraError("No active context"); + return NzRectui(); + } + #endif + + GLint params[4]; + glGetIntegerv(GL_VIEWPORT, ¶ms[0]); + + return NzRectui(params[0], params[1], params[2], params[3]); +} + +bool NzRenderer::HasCapability(nzRendererCap capability) +{ + return s_capabilities[capability]; +} + +bool NzRenderer::Initialize() +{ + if (s_moduleReferenceCouter++ != 0) + return true; // Déjà initialisé + + // Initialisation des dépendances + if (!NzUtility::Initialize()) + { + NazaraError("Failed to initialize utility module"); + return false; + } + + // Initialisation du module + if (!NzOpenGL::Initialize()) + { + NazaraError("Failed to initialize OpenGL"); + return false; + } + + NzContext::EnsureContext(); + + for (unsigned int i = 0; i < totalMatrixCount; ++i) + { + s_matrix[i].MakeIdentity(); + s_matrixLocation[i] = -1; + s_matrixUpdated[i] = false; + } + + s_indexBuffer = nullptr; + s_shader = nullptr; + s_stencilCompare = nzRendererComparison_Always; + s_stencilFail = nzStencilOperation_Keep; + s_stencilFuncUpdated = true; + s_stencilMask = 0xFFFFFFFF; + s_stencilOpUpdated = true; + s_stencilPass = nzStencilOperation_Keep; + s_stencilReference = 0; + s_stencilZFail = nzStencilOperation_Keep; + s_target = nullptr; + s_vaoUpdated = false; + s_vertexBuffer = nullptr; + s_vertexDeclaration = nullptr; + + // Récupération des capacités d'OpenGL + s_capabilities[nzRendererCap_AnisotropicFilter] = NzOpenGL::IsSupported(nzOpenGLExtension_AnisotropicFilter); + s_capabilities[nzRendererCap_FP64] = NzOpenGL::IsSupported(nzOpenGLExtension_FP64); + s_capabilities[nzRendererCap_HardwareBuffer] = true; // Natif depuis OpenGL 1.5 + // MultipleRenderTargets (Techniquement natif depuis OpenGL 2.0 mais inutile sans glBindFragDataLocation) + s_capabilities[nzRendererCap_MultipleRenderTargets] = (glBindFragDataLocation != nullptr); + s_capabilities[nzRendererCap_OcclusionQuery] = true; // Natif depuis OpenGL 1.5 + s_capabilities[nzRendererCap_PixelBufferObject] = NzOpenGL::IsSupported(nzOpenGLExtension_PixelBufferObject); + s_capabilities[nzRendererCap_RenderTexture] = NzOpenGL::IsSupported(nzOpenGLExtension_FrameBufferObject); + s_capabilities[nzRendererCap_Texture3D] = true; // Natif depuis OpenGL 1.2 + s_capabilities[nzRendererCap_TextureCubemap] = true; // Natif depuis OpenGL 1.3 + s_capabilities[nzRendererCap_TextureMulti] = true; // Natif depuis OpenGL 1.3 + s_capabilities[nzRendererCap_TextureNPOT] = true; // Natif depuis OpenGL 2.0 + + if (s_capabilities[nzRendererCap_AnisotropicFilter]) + { + GLint maxAnisotropy; + glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropy); + + s_maxAnisotropyLevel = static_cast(maxAnisotropy); + } + else + s_maxAnisotropyLevel = 1; + + if (s_capabilities[nzRendererCap_MultipleRenderTargets]) + { + GLint maxDrawBuffers; + glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers); + + GLint maxColorAttachments; + glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxColorAttachments); + + s_maxRenderTarget = static_cast(std::min(maxColorAttachments, maxDrawBuffers)); + } + else + s_maxRenderTarget = 1; + + if (s_capabilities[nzRendererCap_TextureMulti]) + { + GLint maxTextureUnits; + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits); + + GLint maxVertexAttribs; + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs); + + // Impossible de binder plus de texcoords que d'attributes (en sachant qu'un certain nombre est déjà pris par les autres attributs) + s_maxTextureUnit = static_cast(std::min(maxTextureUnits, maxVertexAttribs-NzOpenGL::AttributeIndex[nzElementUsage_TexCoord])); + } + else + s_maxTextureUnit = 1; + + NzBuffer::SetBufferFunction(nzBufferStorage_Hardware, HardwareBufferFunction); + + NazaraNotice("Initialized: Renderer module"); + + return true; +} + +bool NzRenderer::IsInitialized() +{ + return s_moduleReferenceCouter != 0; +} + +void NzRenderer::SetBlendFunc(nzBlendFunc src, nzBlendFunc dest) +{ + #ifdef NAZARA_DEBUG + if (NzContext::GetCurrent() == nullptr) + { + NazaraError("No active context"); + return; + } + #endif + + glBlendFunc(NzOpenGL::BlendFunc[src], NzOpenGL::BlendFunc[dest]); +} + +void NzRenderer::SetClearColor(const NzColor& color) +{ + #ifdef NAZARA_DEBUG + if (NzContext::GetCurrent() == nullptr) + { + NazaraError("No active context"); + return; + } + #endif + + glClearColor(color.r/255.f, color.g/255.f, color.b/255.f, color.a/255.f); +} + +void NzRenderer::SetClearColor(nzUInt8 r, nzUInt8 g, nzUInt8 b, nzUInt8 a) +{ + #ifdef NAZARA_DEBUG + if (NzContext::GetCurrent() == nullptr) + { + NazaraError("No active context"); + return; + } + #endif + + glClearColor(r/255.f, g/255.f, b/255.f, a/255.f); +} + +void NzRenderer::SetClearDepth(double depth) +{ + #ifdef NAZARA_DEBUG + if (NzContext::GetCurrent() == nullptr) + { + NazaraError("No active context"); + return; + } + #endif + + glClearDepth(depth); +} + +void NzRenderer::SetClearStencil(unsigned int value) +{ + #ifdef NAZARA_DEBUG + if (NzContext::GetCurrent() == nullptr) + { + NazaraError("No active context"); + return; + } + #endif + + glClearStencil(value); +} + +void NzRenderer::SetFaceCulling(nzFaceCulling cullingMode) +{ + #ifdef NAZARA_DEBUG + if (NzContext::GetCurrent() == nullptr) + { + NazaraError("No active context"); + return; + } + #endif + + glCullFace(NzOpenGL::FaceCulling[cullingMode]); +} + +void NzRenderer::SetFaceFilling(nzFaceFilling fillingMode) +{ + #ifdef NAZARA_DEBUG + if (NzContext::GetCurrent() == nullptr) + { + NazaraError("No active context"); + return; + } + #endif + + glPolygonMode(GL_FRONT_AND_BACK, NzOpenGL::FaceFilling[fillingMode]); +} + +bool NzRenderer::SetIndexBuffer(const NzIndexBuffer* indexBuffer) +{ + #if NAZARA_RENDERER_SAFE + if (indexBuffer && !indexBuffer->IsHardware() && !indexBuffer->IsSequential()) + { + NazaraError("Buffer must be hardware"); + return false; + } + #endif + + if (s_indexBuffer != indexBuffer) + { + s_indexBuffer = indexBuffer; + s_vaoUpdated = false; + } + + return true; +} + +void NzRenderer::SetMatrix(nzMatrixType type, const NzMatrix4f& matrix) +{ + s_matrix[type] = matrix; + + // Invalidation des combinaisons + switch (type) + { + case nzMatrixType_View: + case nzMatrixType_World: + s_matrixUpdated[nzMatrixCombination_WorldView] = false; + case nzMatrixType_Projection: + s_matrixUpdated[nzMatrixCombination_WorldViewProj] = false; + s_matrixUpdated[nzMatrixCombination_ViewProj] = false; + break; + } +} + +bool NzRenderer::SetShader(NzShader* shader) +{ + if (s_shader == shader) + return true; + + if (s_shader) + s_shader->m_impl->Unbind(); + + if (shader) + { + #if NAZARA_RENDERER_SAFE + if (!shader->IsCompiled()) + { + NazaraError("Shader is not compiled"); + shader = nullptr; + + return false; + } + #endif + + if (!shader->m_impl->Bind()) + { + NazaraError("Failed to bind shader"); + shader = nullptr; + + return false; + } + + // Récupération des indices des variables uniformes (-1 si la variable n'existe pas) + s_matrixLocation[nzMatrixType_Projection] = shader->GetUniformLocation("ProjMatrix"); + s_matrixLocation[nzMatrixType_View] = shader->GetUniformLocation("ViewMatrix"); + s_matrixLocation[nzMatrixType_World] = shader->GetUniformLocation("WorldMatrix"); + + s_matrixLocation[nzMatrixCombination_ViewProj] = shader->GetUniformLocation("ViewProjMatrix"); + s_matrixLocation[nzMatrixCombination_WorldView] = shader->GetUniformLocation("WorldViewMatrix"); + s_matrixLocation[nzMatrixCombination_WorldViewProj] = shader->GetUniformLocation("WorldViewProjMatrix"); + } + + s_shader = shader; + + return true; +} + +void NzRenderer::SetStencilCompareFunction(nzRendererComparison compareFunc) +{ + if (compareFunc != s_stencilCompare) + { + s_stencilCompare = compareFunc; + s_stencilFuncUpdated = false; + } +} + +void NzRenderer::SetStencilFailOperation(nzStencilOperation failOperation) +{ + if (failOperation != s_stencilFail) + { + s_stencilFail = failOperation; + s_stencilOpUpdated = false; + } +} + +void NzRenderer::SetStencilMask(nzUInt32 mask) +{ + if (mask != s_stencilMask) + { + s_stencilMask = mask; + s_stencilFuncUpdated = false; + } +} + +void NzRenderer::SetStencilPassOperation(nzStencilOperation passOperation) +{ + if (passOperation != s_stencilPass) + { + s_stencilPass = passOperation; + s_stencilOpUpdated = false; + } +} + +void NzRenderer::SetStencilReferenceValue(unsigned int refValue) +{ + if (refValue != s_stencilReference) + { + s_stencilReference = refValue; + s_stencilFuncUpdated = false; + } +} + +void NzRenderer::SetStencilZFailOperation(nzStencilOperation zfailOperation) +{ + if (zfailOperation != s_stencilZFail) + { + s_stencilZFail = zfailOperation; + s_stencilOpUpdated = false; + } +} + +bool NzRenderer::SetTarget(NzRenderTarget* target) +{ + if (s_target == target) + return true; + + if (s_target && !s_target->HasContext()) + s_target->Desactivate(); + + if (target) + { + #if NAZARA_RENDERER_SAFE + if (!target->IsRenderable()) + { + NazaraError("Target not renderable"); + return false; + } + #endif + + if (target->Activate()) + s_target = target; + else + { + NazaraError("Failed to activate target"); + s_target = nullptr; + + return false; + } + } + else + s_target = nullptr; + + return true; +} + +bool NzRenderer::SetVertexBuffer(const NzVertexBuffer* vertexBuffer) +{ + #if NAZARA_RENDERER_SAFE + if (vertexBuffer && !vertexBuffer->IsHardware()) + { + NazaraError("Buffer must be hardware"); + return false; + } + #endif + + if (s_vertexBuffer != vertexBuffer) + { + s_vertexBuffer = vertexBuffer; + s_vaoUpdated = false; + } + + return true; +} + +bool NzRenderer::SetVertexDeclaration(const NzVertexDeclaration* vertexDeclaration) +{ + if (s_vertexDeclaration != vertexDeclaration) + { + s_vertexDeclaration = vertexDeclaration; + s_vaoUpdated = false; + } + + return true; +} + +void NzRenderer::SetViewport(const NzRectui& viewport) +{ + #ifdef NAZARA_DEBUG + if (NzContext::GetCurrent() == nullptr) + { + NazaraError("No active context"); + return; + } + #endif + + unsigned int height = s_target->GetHeight(); + + #if NAZARA_RENDERER_SAFE + if (!s_target) + { + NazaraError("Renderer has no target"); + return; + } + + unsigned int width = s_target->GetWidth(); + if (viewport.x+viewport.width > width || viewport.y+viewport.height > height) + { + NazaraError("Rectangle dimensions are out of bounds"); + return; + } + #endif + + glViewport(viewport.x, height-viewport.height-viewport.y, viewport.width, viewport.height); + glScissor(viewport.x, height-viewport.height-viewport.y, viewport.width, viewport.height); +} + +void NzRenderer::Uninitialize() +{ + if (--s_moduleReferenceCouter != 0) + return; // Encore utilisé + + // Libération du module + NzContext::EnsureContext(); + + // Libération des VAOs + for (auto it = s_vaos.begin(); it != s_vaos.end(); ++it) + { + GLuint vao = static_cast(it->second); + glDeleteVertexArrays(1, &vao); + } + + NzOpenGL::Uninitialize(); + + NazaraNotice("Uninitialized: Renderer module"); + + // Libération des dépendances + NzUtility::Uninitialize(); +} + +bool NzRenderer::EnsureStateUpdate() +{ + #ifdef NAZARA_DEBUG + if (NzContext::GetCurrent() == nullptr) + { + NazaraError("No active context"); + return false; + } + #endif + + #if NAZARA_RENDERER_SAFE + if (!s_shader) + { + NazaraError("No shader"); + return false; + } + #endif + + // Il est plus rapide d'opérer sur l'implémentation du shader directement + NzShaderImpl* shaderImpl = s_shader->m_impl; + + if (!shaderImpl->BindTextures()) + NazaraWarning("Failed to bind textures"); + + for (unsigned int i = 0; i <= nzMatrixType_Max; ++i) + { + if (!s_matrixUpdated[i]) + { + shaderImpl->SendMatrix(s_matrixLocation[i], s_matrix[i]); + s_matrixUpdated[i] = true; + } + } + + // Cas spéciaux car il faut recalculer la matrice + if (!s_matrixUpdated[nzMatrixCombination_ViewProj]) + { + s_matrix[nzMatrixCombination_ViewProj] = s_matrix[nzMatrixType_View] * s_matrix[nzMatrixType_Projection]; + + shaderImpl->SendMatrix(s_matrixLocation[nzMatrixCombination_ViewProj], s_matrix[nzMatrixCombination_ViewProj]); + s_matrixUpdated[nzMatrixCombination_ViewProj] = true; + } + + if (!s_matrixUpdated[nzMatrixCombination_WorldView]) + { + s_matrix[nzMatrixCombination_WorldView] = NzMatrix4f::ConcatenateAffine(s_matrix[nzMatrixType_World], s_matrix[nzMatrixType_View]); + + shaderImpl->SendMatrix(s_matrixLocation[nzMatrixCombination_WorldView], s_matrix[nzMatrixCombination_WorldView]); + s_matrixUpdated[nzMatrixCombination_WorldView] = true; + } + + if (!s_matrixUpdated[nzMatrixCombination_WorldViewProj]) + { + s_matrix[nzMatrixCombination_WorldViewProj] = s_matrix[nzMatrixCombination_WorldView] * s_matrix[nzMatrixType_Projection]; + + shaderImpl->SendMatrix(s_matrixLocation[nzMatrixCombination_WorldViewProj], s_matrix[nzMatrixCombination_WorldViewProj]); + s_matrixUpdated[nzMatrixCombination_WorldViewProj] = true; + } + + if (!s_stencilFuncUpdated) + { + glStencilFunc(NzOpenGL::RendererComparison[s_stencilCompare], s_stencilReference, s_stencilMask); + s_stencilFuncUpdated = true; + } + + if (!s_stencilOpUpdated) + { + glStencilOp(NzOpenGL::StencilOperation[s_stencilFail], NzOpenGL::StencilOperation[s_stencilZFail], NzOpenGL::StencilOperation[s_stencilPass]); + s_stencilOpUpdated = true; + } + + if (!s_vaoUpdated) + { + #if NAZARA_RENDERER_SAFE + if (!s_vertexBuffer) + { + NazaraError("No vertex buffer"); + return false; + } + + if (!s_vertexDeclaration) + { + NazaraError("No vertex declaration"); + return false; + } + #endif + + static const bool vaoSupported = NzOpenGL::IsSupported(nzOpenGLExtension_VertexArrayObject); + bool update; + GLuint vao; + + // Si les VAOs sont supportés, on entoure nos appels par ceux-ci + if (vaoSupported) + { + // On recherche si un VAO existe déjà avec notre configuration + // Note: Les VAOs ne sont pas partagés entre les contextes, ces derniers font donc partie de notre configuration + + auto key = std::make_tuple(NzContext::GetCurrent(), s_indexBuffer, s_vertexBuffer, s_vertexDeclaration); + auto it = s_vaos.find(key); + if (it == s_vaos.end()) + { + // On créé notre VAO + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + + // On l'ajoute à notre liste + s_vaos.insert(std::make_pair(key, static_cast(vao))); + + // Et on indique qu'on veut le programmer + update = true; + } + else + { + // Notre VAO existe déjà, il est donc inutile de le reprogrammer + vao = it->second; + + update = false; + } + } + else + update = true; // Fallback si les VAOs ne sont pas supportés + + if (update) + { + NzHardwareBuffer* vertexBufferImpl = static_cast(s_vertexBuffer->GetBuffer()->GetImpl()); + vertexBufferImpl->Bind(); + + const nzUInt8* buffer = reinterpret_cast(s_vertexBuffer->GetPointer()); + + unsigned int stride = s_vertexDeclaration->GetStride(nzElementStream_VertexData); + for (unsigned int i = 0; i <= nzElementUsage_Max; ++i) + { + const NzVertexElement* element = s_vertexDeclaration->GetElement(nzElementStream_VertexData, static_cast(i)); + + if (element) + { + glEnableVertexAttribArray(NzOpenGL::AttributeIndex[i]); + glVertexAttribPointer(NzOpenGL::AttributeIndex[i], + NzVertexDeclaration::GetElementCount(element->type), + NzOpenGL::ElementType[element->type], + (element->type == nzElementType_Color) ? GL_TRUE : GL_FALSE, + stride, + &buffer[element->offset]); + } + else + glDisableVertexAttribArray(NzOpenGL::AttributeIndex[i]); + } + + if (s_indexBuffer) + { + NzHardwareBuffer* indexBufferImpl = static_cast(s_indexBuffer->GetBuffer()->GetImpl()); + indexBufferImpl->Bind(); + } + } + + if (vaoSupported) + { + // Si nous venons de définir notre VAO, nous devons le débinder pour indiquer la fin de sa construction + if (update) + glBindVertexArray(0); + + // Nous (re)bindons le VAO pour définir les attributs de vertice + glBindVertexArray(vao); + } + + s_vaoUpdated = true; + } + + return true; +} + +unsigned int NzRenderer::s_moduleReferenceCouter = 0; diff --git a/src/Nazara/Renderer/Shader.cpp b/src/Nazara/Renderer/Shader.cpp index fff9f9fd7..a6be23090 100644 --- a/src/Nazara/Renderer/Shader.cpp +++ b/src/Nazara/Renderer/Shader.cpp @@ -1,624 +1,627 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Renderer module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -NzShader::NzShader(nzShaderLanguage language) -{ - Create(language); - - #ifdef NAZARA_DEBUG - if (!m_impl) - { - NazaraError("Failed to create shader"); - throw std::runtime_error("Constructor failed"); - } - #endif -} - -NzShader::~NzShader() -{ - Destroy(); -} - -bool NzShader::Create(nzShaderLanguage language) -{ - Destroy(); - - switch (language) - { - case nzShaderLanguage_Cg: - NazaraError("Cg support is not implemented yet"); - return false; - - case nzShaderLanguage_GLSL: - m_impl = new NzGLSLShader(this); - break; - - default: - NazaraError("Shader language not handled (0x" + NzString::Number(language, 16) + ')'); - return false; - } - - if (!m_impl->Create()) - { - NazaraError("Failed to create shader"); - delete m_impl; - m_impl = nullptr; - - return false; - } - - return true; -} - -bool NzShader::Compile() -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Shader not created"); - return false; - } - #endif - - if (m_impl->Compile()) - { - m_compiled = true; - return true; - } - else - return false; -} - -void NzShader::Destroy() -{ - if (m_impl) - { - m_impl->Destroy(); - delete m_impl; - m_impl = nullptr; - } -} - -NzString NzShader::GetLog() const -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NzString error = "Shader not created"; - NazaraError(error); - - return error; - } - #endif - - return m_impl->GetLog(); -} - -nzShaderLanguage NzShader::GetLanguage() const -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NzString error = "Shader not created"; - NazaraError(error); - - return nzShaderLanguage_Unknown; - } - #endif - - return m_impl->GetLanguage(); -} - -NzString NzShader::GetSourceCode(nzShaderType type) const -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NzString error = "Shader not created"; - NazaraError(error); - - return error; - } - - if (!IsTypeSupported(type)) - { - NzString error = "Shader type not supported"; - NazaraError(error); - - return error; - } - - if (!m_impl->IsLoaded(type)) - { - NzString error = "Shader not loaded"; - NazaraError(error); - - return error; - } - #endif - - return m_impl->GetSourceCode(type); -} - -int NzShader::GetUniformLocation(const NzString& name) const -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Shader not created"); - return false; - } - #endif - - return m_impl->GetUniformLocation(name); -} - -bool NzShader::HasUniform(const NzString& name) const -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Shader not created"); - return false; - } - #endif - - return m_impl->GetUniformLocation(name) != -1; -} - -bool NzShader::IsCompiled() const -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Shader not created"); - return false; - } - #endif - - return m_compiled; -} - -bool NzShader::IsLoaded(nzShaderType type) const -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Shader not created"); - return false; - } - - if (!IsTypeSupported(type)) - { - NazaraError("Shader type not supported"); - return false; - } - #endif - - return m_impl->IsLoaded(type); -} - -bool NzShader::Load(nzShaderType type, const NzString& source) -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Shader not created"); - return false; - } - - if (!IsTypeSupported(type)) - { - NazaraError("Shader type not supported"); - return false; - } - - if (source.IsEmpty()) - { - NazaraError("Empty source code"); - return false; - } - - if (m_impl->IsLoaded(type)) - { - NazaraError("Shader already loaded"); - return false; - } - #endif - - return m_impl->Load(type, source); -} - -bool NzShader::LoadFromFile(nzShaderType type, const NzString& filePath) -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Shader not created"); - return false; - } - - if (!IsTypeSupported(type)) - { - NazaraError("Shader type not supported"); - return false; - } - - if (m_impl->IsLoaded(type)) - { - NazaraError("Shader already loaded"); - return false; - } - #endif - - NzFile file(filePath); - if (!file.Open(NzFile::ReadOnly | NzFile::Text)) - { - NazaraError("Failed to open \"" + filePath + '"'); - return false; - } - - unsigned int length = file.GetSize(); - - NzString source; - source.Resize(length); - - if (file.Read(&source[0], length) != length) - { - NazaraError("Failed to read shader file"); - return false; - } - - file.Close(); - - return m_impl->Load(type, source); -} - -bool NzShader::Lock() -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Shader not created"); - return false; - } - #endif - - return m_impl->Lock(); -} - -bool NzShader::SendBoolean(int location, bool value) -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Shader not created"); - return false; - } - - if (location == -1) - { - NazaraError("Invalid location"); - return false; - } - #endif - - return m_impl->SendBoolean(location, value); -} - -bool NzShader::SendDouble(int location, double value) -{ - #if NAZARA_RENDERER_SAFE - if (!NzRenderer::HasCapability(nzRendererCap_FP64)) - { - NazaraError("FP64 is not supported"); - return false; - } - - if (!m_impl) - { - NazaraError("Shader not created"); - return false; - } - - if (location == -1) - { - NazaraError("Invalid location"); - return false; - } - #endif - - return m_impl->SendDouble(location, value); -} - -bool NzShader::SendFloat(int location, float value) -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Shader not created"); - return false; - } - - if (location == -1) - { - NazaraError("Invalid location"); - return false; - } - #endif - - return m_impl->SendFloat(location, value); -} - -bool NzShader::SendInteger(int location, int value) -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Shader not created"); - return false; - } - - if (location == -1) - { - NazaraError("Invalid location"); - return false; - } - #endif - - return m_impl->SendInteger(location, value); -} - -bool NzShader::SendMatrix(int location, const NzMatrix4d& matrix) -{ - #if NAZARA_RENDERER_SAFE - if (!NzRenderer::HasCapability(nzRendererCap_FP64)) - { - NazaraError("FP64 is not supported"); - return false; - } - - if (!m_impl) - { - NazaraError("Shader not created"); - return false; - } - - if (location == -1) - { - NazaraError("Invalid location"); - return false; - } - #endif - - return m_impl->SendMatrix(location, matrix); -} - -bool NzShader::SendMatrix(int location, const NzMatrix4f& matrix) -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Shader not created"); - return false; - } - - if (location == -1) - { - NazaraError("Invalid location"); - return false; - } - #endif - - return m_impl->SendMatrix(location, matrix); -} - -bool NzShader::SendTexture(int location, const NzTexture* texture) -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Shader not created"); - return false; - } - - if (location == -1) - { - NazaraError("Invalid location"); - return false; - } - #endif - - return m_impl->SendTexture(location, texture); -} - -bool NzShader::SendVector(int location, const NzVector2d& vector) -{ - #if NAZARA_RENDERER_SAFE - if (!NzRenderer::HasCapability(nzRendererCap_FP64)) - { - NazaraError("FP64 is not supported"); - return false; - } - - if (!m_impl) - { - NazaraError("Shader not created"); - return false; - } - - if (location == -1) - { - NazaraError("Invalid location"); - return false; - } - #endif - - return m_impl->SendVector(location, vector); -} - -bool NzShader::SendVector(int location, const NzVector2f& vector) -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Shader not created"); - return false; - } - - if (location == -1) - { - NazaraError("Invalid location"); - return false; - } - #endif - - return m_impl->SendVector(location, vector); -} - -bool NzShader::SendVector(int location, const NzVector3d& vector) -{ - #if NAZARA_RENDERER_SAFE - if (!NzRenderer::HasCapability(nzRendererCap_FP64)) - { - NazaraError("FP64 is not supported"); - return false; - } - - if (!m_impl) - { - NazaraError("Shader not created"); - return false; - } - - if (location == -1) - { - NazaraError("Invalid location"); - return false; - } - #endif - - return m_impl->SendVector(location, vector); -} - -bool NzShader::SendVector(int location, const NzVector3f& vector) -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Shader not created"); - return false; - } - - if (location == -1) - { - NazaraError("Invalid location"); - return false; - } - #endif - - return m_impl->SendVector(location, vector); -} - -bool NzShader::SendVector(int location, const NzVector4d& vector) -{ - #if NAZARA_RENDERER_SAFE - if (!NzRenderer::HasCapability(nzRendererCap_FP64)) - { - NazaraError("FP64 is not supported"); - return false; - } - - if (!m_impl) - { - NazaraError("Shader not created"); - return false; - } - - if (location == -1) - { - NazaraError("Invalid location"); - return false; - } - #endif - - return m_impl->SendVector(location, vector); -} - -bool NzShader::SendVector(int location, const NzVector4f& vector) -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Shader not created"); - return false; - } - - if (location == -1) - { - NazaraError("Invalid location"); - return false; - } - #endif - - return m_impl->SendVector(location, vector); -} - -void NzShader::Unlock() -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Shader not created"); - return; - } - #endif - - return m_impl->Unlock(); -} - -bool NzShader::IsLanguageSupported(nzShaderLanguage language) -{ - switch (language) - { - case nzShaderLanguage_Cg: - return false; // ?? - - case nzShaderLanguage_GLSL: - return true; - - default: - NazaraError("Shader language not handled (0x" + NzString::Number(language, 16) + ')'); - return false; - } -} - -bool NzShader::IsTypeSupported(nzShaderType type) -{ - switch (type) - { - case nzShaderType_Fragment: - case nzShaderType_Vertex: - return true; - - case nzShaderType_Geometry: - return false; // ?? - - default: - NazaraError("Shader type not handled (0x" + NzString::Number(type, 16) + ')'); - return false; - } -} +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +NzShader::NzShader(nzShaderLanguage language) +{ + Create(language); + + #ifdef NAZARA_DEBUG + if (!m_impl) + { + NazaraError("Failed to create shader"); + throw std::runtime_error("Constructor failed"); + } + #endif +} + +NzShader::~NzShader() +{ + Destroy(); +} + +bool NzShader::Create(nzShaderLanguage language) +{ + Destroy(); + + switch (language) + { + case nzShaderLanguage_Cg: + NazaraError("Cg support is not implemented yet"); + return false; + + case nzShaderLanguage_GLSL: + m_impl = new NzGLSLShader(this); + break; + + default: + NazaraError("Shader language not handled (0x" + NzString::Number(language, 16) + ')'); + return false; + } + + if (!m_impl->Create()) + { + NazaraError("Failed to create shader"); + delete m_impl; + m_impl = nullptr; + + return false; + } + + NotifyCreated(); + return true; +} + +bool NzShader::Compile() +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Shader not created"); + return false; + } + #endif + + if (m_impl->Compile()) + { + m_compiled = true; + return true; + } + else + return false; +} + +void NzShader::Destroy() +{ + if (m_impl) + { + NotifyDestroy(); + + m_impl->Destroy(); + delete m_impl; + m_impl = nullptr; + } +} + +NzString NzShader::GetLog() const +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NzString error = "Shader not created"; + NazaraError(error); + + return error; + } + #endif + + return m_impl->GetLog(); +} + +nzShaderLanguage NzShader::GetLanguage() const +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NzString error = "Shader not created"; + NazaraError(error); + + return nzShaderLanguage_Unknown; + } + #endif + + return m_impl->GetLanguage(); +} + +NzString NzShader::GetSourceCode(nzShaderType type) const +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NzString error = "Shader not created"; + NazaraError(error); + + return error; + } + + if (!IsTypeSupported(type)) + { + NzString error = "Shader type not supported"; + NazaraError(error); + + return error; + } + + if (!m_impl->IsLoaded(type)) + { + NzString error = "Shader not loaded"; + NazaraError(error); + + return error; + } + #endif + + return m_impl->GetSourceCode(type); +} + +int NzShader::GetUniformLocation(const NzString& name) const +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Shader not created"); + return false; + } + #endif + + return m_impl->GetUniformLocation(name); +} + +bool NzShader::HasUniform(const NzString& name) const +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Shader not created"); + return false; + } + #endif + + return m_impl->GetUniformLocation(name) != -1; +} + +bool NzShader::IsCompiled() const +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Shader not created"); + return false; + } + #endif + + return m_compiled; +} + +bool NzShader::IsLoaded(nzShaderType type) const +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Shader not created"); + return false; + } + + if (!IsTypeSupported(type)) + { + NazaraError("Shader type not supported"); + return false; + } + #endif + + return m_impl->IsLoaded(type); +} + +bool NzShader::Load(nzShaderType type, const NzString& source) +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Shader not created"); + return false; + } + + if (!IsTypeSupported(type)) + { + NazaraError("Shader type not supported"); + return false; + } + + if (source.IsEmpty()) + { + NazaraError("Empty source code"); + return false; + } + + if (m_impl->IsLoaded(type)) + { + NazaraError("Shader already loaded"); + return false; + } + #endif + + return m_impl->Load(type, source); +} + +bool NzShader::LoadFromFile(nzShaderType type, const NzString& filePath) +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Shader not created"); + return false; + } + + if (!IsTypeSupported(type)) + { + NazaraError("Shader type not supported"); + return false; + } + + if (m_impl->IsLoaded(type)) + { + NazaraError("Shader already loaded"); + return false; + } + #endif + + NzFile file(filePath); + if (!file.Open(NzFile::ReadOnly | NzFile::Text)) + { + NazaraError("Failed to open \"" + filePath + '"'); + return false; + } + + unsigned int length = file.GetSize(); + + NzString source; + source.Resize(length); + + if (file.Read(&source[0], length) != length) + { + NazaraError("Failed to read shader file"); + return false; + } + + file.Close(); + + return m_impl->Load(type, source); +} + +bool NzShader::Lock() +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Shader not created"); + return false; + } + #endif + + return m_impl->Lock(); +} + +bool NzShader::SendBoolean(int location, bool value) +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Shader not created"); + return false; + } + + if (location == -1) + { + NazaraError("Invalid location"); + return false; + } + #endif + + return m_impl->SendBoolean(location, value); +} + +bool NzShader::SendDouble(int location, double value) +{ + #if NAZARA_RENDERER_SAFE + if (!NzRenderer::HasCapability(nzRendererCap_FP64)) + { + NazaraError("FP64 is not supported"); + return false; + } + + if (!m_impl) + { + NazaraError("Shader not created"); + return false; + } + + if (location == -1) + { + NazaraError("Invalid location"); + return false; + } + #endif + + return m_impl->SendDouble(location, value); +} + +bool NzShader::SendFloat(int location, float value) +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Shader not created"); + return false; + } + + if (location == -1) + { + NazaraError("Invalid location"); + return false; + } + #endif + + return m_impl->SendFloat(location, value); +} + +bool NzShader::SendInteger(int location, int value) +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Shader not created"); + return false; + } + + if (location == -1) + { + NazaraError("Invalid location"); + return false; + } + #endif + + return m_impl->SendInteger(location, value); +} + +bool NzShader::SendMatrix(int location, const NzMatrix4d& matrix) +{ + #if NAZARA_RENDERER_SAFE + if (!NzRenderer::HasCapability(nzRendererCap_FP64)) + { + NazaraError("FP64 is not supported"); + return false; + } + + if (!m_impl) + { + NazaraError("Shader not created"); + return false; + } + + if (location == -1) + { + NazaraError("Invalid location"); + return false; + } + #endif + + return m_impl->SendMatrix(location, matrix); +} + +bool NzShader::SendMatrix(int location, const NzMatrix4f& matrix) +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Shader not created"); + return false; + } + + if (location == -1) + { + NazaraError("Invalid location"); + return false; + } + #endif + + return m_impl->SendMatrix(location, matrix); +} + +bool NzShader::SendTexture(int location, const NzTexture* texture) +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Shader not created"); + return false; + } + + if (location == -1) + { + NazaraError("Invalid location"); + return false; + } + #endif + + return m_impl->SendTexture(location, texture); +} + +bool NzShader::SendVector(int location, const NzVector2d& vector) +{ + #if NAZARA_RENDERER_SAFE + if (!NzRenderer::HasCapability(nzRendererCap_FP64)) + { + NazaraError("FP64 is not supported"); + return false; + } + + if (!m_impl) + { + NazaraError("Shader not created"); + return false; + } + + if (location == -1) + { + NazaraError("Invalid location"); + return false; + } + #endif + + return m_impl->SendVector(location, vector); +} + +bool NzShader::SendVector(int location, const NzVector2f& vector) +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Shader not created"); + return false; + } + + if (location == -1) + { + NazaraError("Invalid location"); + return false; + } + #endif + + return m_impl->SendVector(location, vector); +} + +bool NzShader::SendVector(int location, const NzVector3d& vector) +{ + #if NAZARA_RENDERER_SAFE + if (!NzRenderer::HasCapability(nzRendererCap_FP64)) + { + NazaraError("FP64 is not supported"); + return false; + } + + if (!m_impl) + { + NazaraError("Shader not created"); + return false; + } + + if (location == -1) + { + NazaraError("Invalid location"); + return false; + } + #endif + + return m_impl->SendVector(location, vector); +} + +bool NzShader::SendVector(int location, const NzVector3f& vector) +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Shader not created"); + return false; + } + + if (location == -1) + { + NazaraError("Invalid location"); + return false; + } + #endif + + return m_impl->SendVector(location, vector); +} + +bool NzShader::SendVector(int location, const NzVector4d& vector) +{ + #if NAZARA_RENDERER_SAFE + if (!NzRenderer::HasCapability(nzRendererCap_FP64)) + { + NazaraError("FP64 is not supported"); + return false; + } + + if (!m_impl) + { + NazaraError("Shader not created"); + return false; + } + + if (location == -1) + { + NazaraError("Invalid location"); + return false; + } + #endif + + return m_impl->SendVector(location, vector); +} + +bool NzShader::SendVector(int location, const NzVector4f& vector) +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Shader not created"); + return false; + } + + if (location == -1) + { + NazaraError("Invalid location"); + return false; + } + #endif + + return m_impl->SendVector(location, vector); +} + +void NzShader::Unlock() +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Shader not created"); + return; + } + #endif + + return m_impl->Unlock(); +} + +bool NzShader::IsLanguageSupported(nzShaderLanguage language) +{ + switch (language) + { + case nzShaderLanguage_Cg: + return false; //FIXME: ?? + + case nzShaderLanguage_GLSL: + return true; + + default: + NazaraError("Shader language not handled (0x" + NzString::Number(language, 16) + ')'); + return false; + } +} + +bool NzShader::IsTypeSupported(nzShaderType type) +{ + switch (type) + { + case nzShaderType_Fragment: + case nzShaderType_Vertex: + return true; + + case nzShaderType_Geometry: + return false; // ?? + + default: + NazaraError("Shader type not handled (0x" + NzString::Number(type, 16) + ')'); + return false; + } +} diff --git a/src/Nazara/Renderer/Texture.cpp b/src/Nazara/Renderer/Texture.cpp index 7c829ff44..792990f6d 100644 --- a/src/Nazara/Renderer/Texture.cpp +++ b/src/Nazara/Renderer/Texture.cpp @@ -1,1630 +1,1556 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Renderer module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include -#include -#include - -struct NzTextureImpl -{ - GLuint id; - nzImageType type; - nzPixelFormat format; - nzUInt8 levelCount; - bool isTarget = false; - bool mipmapping = false; - bool mipmapsUpdated = true; - unsigned int depth; - unsigned int height; - unsigned int width; -}; - -namespace -{ - GLenum cubemapFace[] = - { - GL_TEXTURE_CUBE_MAP_POSITIVE_X, // nzCubemapFace_PositiveX - GL_TEXTURE_CUBE_MAP_NEGATIVE_X, // nzCubemapFace_NegativeX - GL_TEXTURE_CUBE_MAP_POSITIVE_Y, // nzCubemapFace_PositiveY - GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, // nzCubemapFace_NegativeY - GL_TEXTURE_CUBE_MAP_POSITIVE_Z, // nzCubemapFace_PositiveZ - GL_TEXTURE_CUBE_MAP_NEGATIVE_Z // nzCubemapFace_NegativeZ - }; - - GLenum openglTarget[] = - { - GL_TEXTURE_1D, // nzImageType_1D - GL_TEXTURE_1D_ARRAY, // nzImageType_1D_Array - GL_TEXTURE_2D, // nzImageType_2D - GL_TEXTURE_2D_ARRAY, // nzImageType_2D_Array - GL_TEXTURE_3D, // nzImageType_3D - GL_TEXTURE_CUBE_MAP // nzImageType_Cubemap - }; - - GLenum openglTargetBinding[] = - { - GL_TEXTURE_BINDING_1D, // nzImageType_1D - GL_TEXTURE_BINDING_1D_ARRAY, // nzImageType_1D - GL_TEXTURE_BINDING_2D, // nzImageType_2D - GL_TEXTURE_BINDING_3D, // nzImageType_3D - GL_TEXTURE_BINDING_CUBE_MAP // nzImageType_Cubemap - }; - - GLenum openglTargetProxy[] = - { - GL_PROXY_TEXTURE_1D, // nzImageType_1D - GL_PROXY_TEXTURE_1D_ARRAY, // nzImageType_1D_Array - GL_PROXY_TEXTURE_2D, // nzImageType_2D - GL_PROXY_TEXTURE_2D_ARRAY, // nzImageType_2D_Array - GL_PROXY_TEXTURE_3D, // nzImageType_3D - GL_PROXY_TEXTURE_CUBE_MAP // nzImageType_Cubemap - }; - - struct OpenGLFormat - { - GLint internalFormat; - GLenum dataFormat; - GLenum dataType; - }; - - bool GetOpenGLFormat(nzPixelFormat pixelFormat, OpenGLFormat* format) - { - switch (pixelFormat) - { - case nzPixelFormat_BGR8: - format->dataFormat = GL_BGR; - format->dataType = GL_UNSIGNED_BYTE; - format->internalFormat = GL_RGB8; - return true; - - case nzPixelFormat_BGRA8: - format->dataFormat = GL_BGRA; - format->dataType = GL_UNSIGNED_BYTE; - format->internalFormat = GL_RGBA8; - return true; - - case nzPixelFormat_DXT1: - format->dataFormat = GL_RGB; - format->dataType = GL_UNSIGNED_BYTE; - format->internalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; - return true; - - case nzPixelFormat_DXT3: - format->dataFormat = GL_RGBA; - format->dataType = GL_UNSIGNED_BYTE; - format->internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; - return true; - - case nzPixelFormat_DXT5: - format->dataFormat = GL_RGBA; - format->dataType = GL_UNSIGNED_BYTE; - format->internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; - return true; - - case nzPixelFormat_L8: - case nzPixelFormat_LA8: - NazaraError("Pixel format not supported"); - return false; - - case nzPixelFormat_RGB5A1: - format->dataFormat = GL_RGBA; - format->dataType = GL_UNSIGNED_SHORT_5_5_5_1; - format->internalFormat = GL_RGB5_A1; - return true; - - case nzPixelFormat_RGB8: - format->dataFormat = GL_RGB; - format->dataType = GL_UNSIGNED_BYTE; - format->internalFormat = GL_RGB8; - return true; - - case nzPixelFormat_RGBA4: - format->dataFormat = GL_RGBA; - format->dataType = GL_UNSIGNED_SHORT_4_4_4_4; - format->internalFormat = GL_RGBA4; - return true; - - case nzPixelFormat_RGBA8: - format->dataFormat = GL_RGBA; - format->dataType = GL_UNSIGNED_BYTE; - format->internalFormat = GL_RGBA8; - return true; - - case nzPixelFormat_Undefined: - NazaraInternalError("Invalid pixel format"); - return false; - } - - NazaraError("Pixel format not handled"); - - return false; - } - - bool CreateTexture(NzTextureImpl* impl, bool proxy) - { - OpenGLFormat openGLFormat; - if (!GetOpenGLFormat(impl->format, &openGLFormat)) - { - NazaraError("Failed to get OpenGL format"); - return false; - } - - GLenum target = (proxy) ? openglTargetProxy[impl->type] : openglTarget[impl->type]; - GLint previous; - glGetIntegerv(openglTargetBinding[impl->type], &previous); - switch (impl->type) - { - case nzImageType_1D: - { - if (glTexStorage1D) - glTexStorage1D(target, impl->levelCount, openGLFormat.internalFormat, impl->width); - else - { - unsigned int w = impl->width; - for (nzUInt8 level = 0; level < impl->levelCount; ++level) - { - glTexImage1D(target, level, openGLFormat.internalFormat, w, 0, openGLFormat.dataFormat, openGLFormat.dataType, nullptr); - if (w > 1U) - w >>= 1; - } - } - break; - } - - case nzImageType_1D_Array: - case nzImageType_2D: - { - if (glTexStorage2D) - glTexStorage2D(target, impl->levelCount, openGLFormat.internalFormat, impl->width, impl->height); - else - { - unsigned int w = impl->width; - unsigned int h = impl->height; - for (nzUInt8 level = 0; level < impl->levelCount; ++level) - { - glTexImage2D(target, level, openGLFormat.internalFormat, w, h, 0, openGLFormat.dataFormat, openGLFormat.dataType, nullptr); - if (w > 1U) - w >>= 1; - - if (h > 1U) - h >>= 1; - } - } - break; - } - - case nzImageType_2D_Array: - case nzImageType_3D: - { - if (glTexStorage3D) - glTexStorage3D(target, impl->levelCount, openGLFormat.internalFormat, impl->width, impl->height, impl->depth); - else - { - unsigned int w = impl->width; - unsigned int h = impl->height; - unsigned int d = impl->depth; - for (nzUInt8 level = 0; level < impl->levelCount; ++level) - { - glTexImage3D(target, level, openGLFormat.internalFormat, w, h, d, 0, openGLFormat.dataFormat, openGLFormat.dataType, nullptr); - if (w > 1U) - w >>= 1; - - if (h > 1U) - h >>= 1; - - if (d > 1U) - d >>= 1; - } - } - break; - } - - case nzImageType_Cubemap: - { - if (glTexStorage2D) - glTexStorage2D(target, impl->levelCount, openGLFormat.internalFormat, impl->width, impl->height); - else - { - unsigned int size = impl->width; // Les cubemaps ont une longueur et largeur identique - for (nzUInt8 level = 0; level < impl->levelCount; ++level) - { - for (GLenum face : cubemapFace) - glTexImage2D(face, level, openGLFormat.internalFormat, size, size, 0, openGLFormat.dataFormat, openGLFormat.dataType, nullptr); - - if (size > 1U) - size >>= 1; - } - } - break; - } - } - - if (proxy) - { - GLint internalFormat = 0; - glGetTexLevelParameteriv(target, 0, GL_TEXTURE_INTERNAL_FORMAT, &internalFormat); - if (internalFormat == 0) - return false; - } - - return true; - } - - static unsigned short lockedLevel[nzImageType_Max+1] = {0}; - static GLuint lockedPrevious[nzImageType_Max+1] = {0}; - - void LockTexture(NzTextureImpl* impl) - { - if (lockedLevel[impl->type]++ == 0) - { - NzContext::EnsureContext(); - - GLint previous; - glGetIntegerv(openglTargetBinding[impl->type], &previous); - - lockedPrevious[impl->type] = static_cast(previous); - - if (lockedPrevious[impl->type] != impl->id) - glBindTexture(openglTarget[impl->type], impl->id); - } - } - - inline void SetUnpackAlignement(nzUInt8 bpp) - { - if (bpp % 8 == 0) - glPixelStorei(GL_UNPACK_ALIGNMENT, 8); - else if (bpp % 4 == 0) - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); - else if (bpp % 2 == 0) - glPixelStorei(GL_UNPACK_ALIGNMENT, 2); - else - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - } - - void UnlockTexture(NzTextureImpl* impl) - { - #ifdef NAZARA_DEBUG - if (NzContext::GetCurrent() == nullptr) - { - NazaraError("No active context"); - return; - } - #endif - - #if NAZARA_RENDERER_SAFE - if (lockedLevel[impl->type] == 0) - { - NazaraError("Unlock called on non-locked texture"); - return; - } - #endif - - if (--lockedLevel[impl->type] == 0 && lockedPrevious[impl->type] != impl->id) - glBindTexture(openglTarget[impl->type], lockedPrevious[impl->type]); - } -} - -NzTexture::NzTexture() : -m_impl(nullptr) -{ -} - -NzTexture::NzTexture(const NzImage& image) : -m_impl(nullptr) -{ - LoadFromImage(image); - - #ifdef NAZARA_DEBUG - if (!m_impl) - { - NazaraError("Failed to create texture"); - throw std::runtime_error("Constructor failed"); - } - #endif -} - -NzTexture::~NzTexture() -{ - Destroy(); -} - -bool NzTexture::Bind() const -{ - #if NAZARA_RENDERER_SAFE - if (lockedLevel[m_impl->type] > 0) - { - NazaraError("Cannot bind texture while a texture is locked"); - return false; - } - #endif - - glBindTexture(openglTarget[m_impl->type], m_impl->id); - - if (m_impl->mipmapping && !m_impl->mipmapsUpdated) - { - glGenerateMipmap(openglTarget[m_impl->type]); - m_impl->mipmapsUpdated = true; - } - - return true; -} - -bool NzTexture::Create(nzImageType type, nzPixelFormat format, unsigned int width, unsigned int height, unsigned int depth, nzUInt8 levelCount, bool lock) -{ - #if NAZARA_RENDERER_SAFE - if (m_impl && m_impl->isTarget) - { - NazaraError("Texture is a target, it cannot be recreated"); - return false; - } - #endif - - Destroy(); - - if (width == 0 || height == 0 || depth == 0) - return true; - - #if NAZARA_RENDERER_SAFE - if (!IsTypeSupported(type)) - { - NazaraError("Texture's type not supported"); - return false; - } - - if (!NzPixelFormat::IsValid(format)) - { - NazaraError("Invalid pixel format"); - return false; - } - - if (!IsFormatSupported(format)) - { - NazaraError("Texture's format not supported"); - return false; - } - - switch (type) - { - case nzImageType_1D: - if (height > 1) - { - NazaraError("One dimensional texture's height must be 1"); - return false; - } - - if (depth > 1) - { - NazaraError("1D textures must be 1 depth"); - return false; - } - break; - - case nzImageType_1D_Array: - case nzImageType_2D: - if (depth > 1) - { - NazaraError("2D textures must be 1 depth"); - return false; - } - break; - - case nzImageType_2D_Array: - case nzImageType_3D: - break; - - case nzImageType_Cubemap: - if (depth > 1) - { - NazaraError("Cubemaps must be 1 depth"); - return false; - } - - if (width != height) - { - NazaraError("Cubemaps must have square dimensions"); - return false; - } - break; - } - #endif - - NzContext::EnsureContext(); - - if (IsMipmappingSupported()) - levelCount = std::min(levelCount, NzImage::GetMaxLevel(width, height, depth)); - else if (levelCount > 1) - { - NazaraWarning("Mipmapping not supported, reducing level count to 1"); - levelCount = 1; - } - - NzTextureImpl* impl = new NzTextureImpl; - glGenTextures(1, &impl->id); - - impl->depth = GetValidSize(depth); - impl->format = format; - impl->height = GetValidSize(height); - impl->levelCount = levelCount; - impl->type = type; - impl->width = GetValidSize(width); - - LockTexture(impl); - - // Vérification du support par la carte graphique - /*if (!CreateTexture(impl, true)) - { - NazaraError("Texture's parameters not supported by driver"); - UnlockTexture(impl); - glDeleteTextures(1, &impl->id); - delete impl; - - return false; - }*/ - - // Création de la texture - if (!CreateTexture(impl, false)) - { - NazaraError("Failed to create texture"); - UnlockTexture(impl); - glDeleteTextures(1, &impl->id); - delete impl; - - return false; - } - - m_impl = impl; - - // Paramètres par défaut - SetFilterMode(nzTextureFilter_Nearest); - SetMipmapRange(0, m_impl->levelCount); - SetWrapMode(nzTextureWrap_Repeat); - - if (m_impl->levelCount > 1U) - EnableMipmapping(true); - - if (!lock) - UnlockTexture(impl); - - return true; -} - -void NzTexture::Destroy() -{ - if (m_impl) - { - #if NAZARA_RENDERER_SAFE - if (m_impl->isTarget) - { - NazaraError("Texture is a target, it cannot be destroyed"); - return; - } - #endif - - NzContext::EnsureContext(); - - glDeleteTextures(1, &m_impl->id); - delete m_impl; - m_impl = nullptr; - } -} - -bool NzTexture::Download(NzImage* image) const -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Texture must be valid"); - return false; - } - - if (!image) - { - NazaraError("Image must be valid"); - return false; - } - #endif - - OpenGLFormat format; - if (!GetOpenGLFormat(m_impl->format, &format)) - { - NazaraError("Failed to get OpenGL format"); - return false; - } - - if (!image->Create(m_impl->type, m_impl->format, m_impl->width, m_impl->height, m_impl->depth, m_impl->levelCount)) - { - NazaraError("Failed to create image"); - return false; - } - - LockTexture(m_impl); - - unsigned int width = m_impl->width; - unsigned int height = m_impl->height; - unsigned int depth = m_impl->depth; - - // Téléchargement... - for (nzUInt8 level = 0; level < m_impl->levelCount; ++level) - { - glGetTexImage(openglTarget[m_impl->type], level, format.dataFormat, format.dataType, image->GetPixels(level)); - - if (width > 1) - width >>= 1; - - if (height > 1) - height >>= 1; - - if (depth > 1) - depth >>= 1; - } - - UnlockTexture(m_impl); - - // Inversion de la texture pour le repère d'OpenGL - if (!image->FlipVertically()) - NazaraWarning("Failed to flip image"); - - return true; -} - -bool NzTexture::EnableMipmapping(bool enable) -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Texture must be valid"); - return false; - } - #endif - - if (m_impl->levelCount == 1) - return true; - - if (!IsMipmappingSupported()) - { - NazaraError("Mipmapping not supported"); - return false; - } - - if (!m_impl->mipmapping && enable) - m_impl->mipmapsUpdated = false; - - m_impl->mipmapping = enable; - - return true; -} - -unsigned int NzTexture::GetAnisotropyLevel() const -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Texture must be valid"); - return 0; - } - #endif - - if (!NzOpenGL::IsSupported(NzOpenGL::AnisotropicFilter)) - return 1; - - LockTexture(m_impl); - - GLint anisotropyLevel; - glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropyLevel); - - UnlockTexture(m_impl); - - return anisotropyLevel; -} - -nzUInt8 NzTexture::GetBPP() const -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Texture must be valid"); - return 0; - } - #endif - - return NzPixelFormat::GetBPP(m_impl->format); -} - -unsigned int NzTexture::GetDepth() const -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Texture must be valid"); - return 0; - } - #endif - - return m_impl->depth; -} - -nzTextureFilter NzTexture::GetFilterMode() const -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Texture must be valid"); - return nzTextureFilter_Unknown; - } - #endif - - LockTexture(m_impl); - - GLint value; - glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, &value); - - UnlockTexture(m_impl); - - GLenum filterMode = static_cast(value); - switch (filterMode) - { - case GL_LINEAR: - case GL_LINEAR_MIPMAP_NEAREST: - return nzTextureFilter_Bilinear; - - case GL_NEAREST: - case GL_NEAREST_MIPMAP_NEAREST: - return nzTextureFilter_Nearest; - - case GL_LINEAR_MIPMAP_LINEAR: - return nzTextureFilter_Trilinear; - - default: - NazaraInternalError("OpenGL filter mode not handled (0x" + NzString::Number(filterMode, 16) + ')'); - return nzTextureFilter_Unknown; - } -} - -nzPixelFormat NzTexture::GetFormat() const -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Texture must be valid"); - return nzPixelFormat_Undefined; - } - #endif - - return m_impl->format; -} - -unsigned int NzTexture::GetHeight() const -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Texture must be valid"); - return 0; - } - #endif - - return m_impl->height; -} - -nzImageType NzTexture::GetType() const -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Texture must be valid"); - return nzImageType_2D; - } - #endif - - return m_impl->type; -} - -unsigned int NzTexture::GetWidth() const -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Texture must be valid"); - return 0; - } - #endif - - return m_impl->width; -} - -nzTextureWrap NzTexture::GetWrapMode() const -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Texture must be valid"); - return nzTextureWrap_Unknown; - } - #endif - - LockTexture(m_impl); - - GLint value; - glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, &value); - - UnlockTexture(m_impl); - - GLenum wrapMode = static_cast(value); - switch (wrapMode) - { - case GL_CLAMP_TO_EDGE: - return nzTextureWrap_Clamp; - - case GL_REPEAT: - return nzTextureWrap_Repeat; - - default: - NazaraInternalError("OpenGL wrap mode not handled (0x" + NzString::Number(wrapMode, 16) + ')'); - return nzTextureWrap_Unknown; - } -} - -bool NzTexture::IsCompressed() const -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Texture must be valid"); - return false; - } - #endif - - return NzPixelFormat::IsCompressed(m_impl->format); -} - -bool NzTexture::IsCubemap() const -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Texture must be valid"); - return false; - } - #endif - - return m_impl->type == nzImageType_Cubemap; -} - -bool NzTexture::IsTarget() const -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Texture must be valid"); - return false; - } - #endif - - return m_impl->isTarget; -} - -bool NzTexture::IsValid() const -{ - return m_impl != nullptr; -} - -bool NzTexture::LoadFromFile(const NzString& filePath, const NzImageParams& params) -{ - NzImage image; - if (!image.LoadFromFile(filePath, params)) - { - NazaraError("Failed to load image"); - return false; - } - - return LoadFromImage(image); -} - -bool NzTexture::LoadFromImage(const NzImage& image) -{ - #if NAZARA_RENDERER_SAFE - if (!image.IsValid()) - { - NazaraError("Image must be valid"); - return false; - } - #endif - - // Vive le Copy-On-Write - NzImage newImage(image); - - nzPixelFormat format = newImage.GetFormat(); - if (!IsFormatSupported(format)) - { - nzPixelFormat newFormat = (NzPixelFormat::HasAlpha(format)) ? nzPixelFormat_BGRA8 : nzPixelFormat_BGR8; - NazaraWarning("Format not supported, trying to convert it to " + NzPixelFormat::ToString(newFormat) + "..."); - - if (NzPixelFormat::IsConversionSupported(format, newFormat)) - { - if (newImage.Convert(newFormat)) - { - NazaraWarning("Conversion succeed"); - format = newFormat; - } - else - { - NazaraError("Conversion failed"); - return false; - } - } - else - { - NazaraError("Conversion not supported"); - return false; - } - } - - nzImageType type = newImage.GetType(); - if (!Create(type, format, newImage.GetWidth(), newImage.GetHeight(), newImage.GetDepth(), newImage.GetLevelCount(), true)) - { - NazaraError("Failed to create texture"); - return false; - } - - for (nzUInt8 level = 0; level < m_impl->levelCount; ++level) - { - if (!Update(newImage.GetConstPixels(level), level)) - { - NazaraError("Failed to update texture"); - Destroy(); - - return false; - } - } - - UnlockTexture(m_impl); - - return true; -} - -bool NzTexture::LoadFromMemory(const void* data, std::size_t size, const NzImageParams& params) -{ - NzImage image; - if (!image.LoadFromMemory(data, size, params)) - { - NazaraError("Failed to load image"); - return false; - } - - return LoadFromImage(image); -} - -bool NzTexture::LoadFromStream(NzInputStream& stream, const NzImageParams& params) -{ - NzImage image; - if (!image.LoadFromStream(stream, params)) - { - NazaraError("Failed to load image"); - return false; - } - - return LoadFromImage(image); -} - -bool NzTexture::Lock() -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Texture must be valid"); - return false; - } - #endif - - LockTexture(m_impl); - - return true; -} - -bool NzTexture::SetAnisotropyLevel(unsigned int anistropyLevel) -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Texture must be valid"); - return false; - } - #endif - - if (!NzOpenGL::IsSupported(NzOpenGL::AnisotropicFilter) && anistropyLevel > 1) - { - NazaraError("Anisotropic filter not supported"); - return false; - } - - LockTexture(m_impl); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anistropyLevel); - - UnlockTexture(m_impl); - - return true; -} - -bool NzTexture::SetFilterMode(nzTextureFilter filter) -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Texture must be valid"); - return false; - } - - if (filter == nzTextureFilter_Trilinear && m_impl->levelCount == 1) - { - NazaraError("Trilinear filter set wihout mipmaps"); - return false; - } - #endif - - LockTexture(m_impl); - - GLenum target = openglTarget[m_impl->type]; - switch (filter) - { - case nzTextureFilter_Bilinear: - if (m_impl->mipmapping > 1) - glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); - else - glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - - glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - break; - - case nzTextureFilter_Nearest: - if (m_impl->mipmapping > 1) - glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); - else - glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - - glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - break; - - case nzTextureFilter_Trilinear: - glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - break; - - default: - NazaraError("Texture filter not handled (0x" + NzString::Number(filter, 16) + ')'); - } - - UnlockTexture(m_impl); - - return true; -} - -bool NzTexture::SetMipmapRange(nzUInt8 minLevel, nzUInt8 maxLevel) -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Texture must be valid"); - return false; - } - - if (minLevel >= m_impl->levelCount) - { - NazaraError("Minimum level cannot be greater or equal than level count (" + NzString::Number(minLevel) + " >= " + NzString::Number(m_impl->levelCount) + ')'); - return false; - } - - if (maxLevel < minLevel) - { - NazaraError("Minimum level cannot be greater than maximum level (" + NzString::Number(minLevel) + " < " + NzString::Number(maxLevel) + ')'); - return false; - } - #endif - - LockTexture(m_impl); - glTexParameteri(openglTarget[m_impl->type], GL_TEXTURE_BASE_LEVEL, minLevel); - glTexParameteri(openglTarget[m_impl->type], GL_TEXTURE_MAX_LEVEL, std::min(m_impl->levelCount, maxLevel)); - UnlockTexture(m_impl); - - return true; -} - -bool NzTexture::SetWrapMode(nzTextureWrap wrap) -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Texture must be valid"); - return false; - } - #endif - - GLenum wrapMode; - switch (wrap) - { - case nzTextureWrap_Clamp: - wrapMode = GL_CLAMP_TO_EDGE; - break; - - case nzTextureWrap_Repeat: - wrapMode = GL_REPEAT; - break; - - default: - NazaraError("Texture wrap mode not handled (0x" + NzString::Number(wrap, 16) + ')'); - return false; - } - - LockTexture(m_impl); - - GLenum target = openglTarget[m_impl->type]; - switch (m_impl->type) - { - // Notez l'absence de "break" ici - case nzImageType_3D: - glTexParameteri(target, GL_TEXTURE_WRAP_R, wrapMode); - case nzImageType_2D: - case nzImageType_2D_Array: - case nzImageType_Cubemap: - glTexParameteri(target, GL_TEXTURE_WRAP_T, wrapMode); - case nzImageType_1D: - case nzImageType_1D_Array: - glTexParameteri(target, GL_TEXTURE_WRAP_S, wrapMode); - break; - - default: - break; - } - - UnlockTexture(m_impl); - - return true; -} - -bool NzTexture::Update(const NzImage& image, nzUInt8 level) -{ - #if NAZARA_RENDERER_SAFE - if (!image.IsValid()) - { - NazaraError("Image must be valid"); - return false; - } - - if (image.GetFormat() != m_impl->format) - { - NazaraError("Image format does not match texture format"); - return false; - } - #endif - - return Update(image.GetConstPixels(level), level); -} - -bool NzTexture::Update(const NzImage& image, const NzRectui& rect, unsigned int z, nzUInt8 level) -{ - #if NAZARA_RENDERER_SAFE - if (!image.IsValid()) - { - NazaraError("Image must be valid"); - return false; - } - - if (image.GetFormat() != m_impl->format) - { - NazaraError("Image format does not match texture format"); - return false; - } - #endif - - const nzUInt8* pixels = image.GetConstPixels(level, rect.x, rect.y, z); - if (!pixels) - { - NazaraError("Failed to access image's pixels"); - return false; - } - - glPixelStorei(GL_UNPACK_ROW_LENGTH, image.GetWidth(level)); - - bool success = Update(pixels, rect, z, level); - - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - - return success; -} - -bool NzTexture::Update(const NzImage& image, const NzCubeui& cube, nzUInt8 level) -{ - #if NAZARA_RENDERER_SAFE - if (!image.IsValid()) - { - NazaraError("Image must be valid"); - return false; - } - - if (image.GetFormat() != m_impl->format) - { - NazaraError("Image format does not match texture format"); - return false; - } - #endif - - const nzUInt8* pixels = image.GetConstPixels(level, cube.x, cube.y, cube.z); - if (!pixels) - { - NazaraError("Failed to access image's pixels"); - return false; - } - - glPixelStorei(GL_UNPACK_ROW_LENGTH, image.GetWidth(level)); - glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, image.GetHeight(level)); - - bool success = Update(pixels, cube, level); - - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0); - - return success; -} - -bool NzTexture::Update(const nzUInt8* pixels, nzUInt8 level) -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Texture must be valid"); - return false; - } - #endif - - if (m_impl->type == nzImageType_3D || m_impl->type == nzImageType_2D_Array) - return Update(pixels, NzCubeui(0, 0, 0, std::max(m_impl->width >> level, 1U), std::max(m_impl->height >> level, 1U), std::max(m_impl->depth >> level, 1U)), level); - else - return Update(pixels, NzRectui(0, 0, std::max(m_impl->width >> level, 1U), std::max(m_impl->height >> level, 1U)), 0, level); -} - -bool NzTexture::Update(const nzUInt8* pixels, const NzRectui& rect, unsigned int z, nzUInt8 level) -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Texture must be valid"); - return false; - } - - if (m_impl->isTarget) - { - NazaraError("Texture is a target, it cannot be updated"); - return false; - } - - if (m_impl->type == nzImageType_Cubemap) - { - NazaraError("Update is not designed for cubemaps, use UpdateFace instead"); - return false; - } - - if (!pixels) - { - NazaraError("Invalid pixel source"); - return false; - } - - if (!rect.IsValid()) - { - NazaraError("Invalid rectangle"); - return false; - } - #endif - - unsigned int height = std::max(m_impl->height >> level, 1U); - - #if NAZARA_RENDERER_SAFE - if (rect.x+rect.width > std::max(m_impl->width >> level, 1U) || rect.y+rect.height > height) - { - NazaraError("Rectangle dimensions are out of bounds"); - return false; - } - - if (z >= std::max(m_impl->depth >> level, 1U)) - { - NazaraError("Z value exceeds depth (" + NzString::Number(z) + " >= (" + NzString::Number(m_impl->depth) + ')'); - return false; - } - - if (level >= m_impl->levelCount) - { - NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_impl->levelCount) + ')'); - return false; - } - #endif - - OpenGLFormat format; - if (!GetOpenGLFormat(m_impl->format, &format)) - { - NazaraError("Failed to get OpenGL format"); - return false; - } - - nzUInt8 bpp = NzPixelFormat::GetBPP(m_impl->format); - - // Inversion de la texture pour le repère d'OpenGL - NzImage mirrored; - mirrored.Create(m_impl->type, m_impl->format, rect.width, rect.height); - mirrored.Update(pixels); - - if (!mirrored.FlipVertically()) - NazaraWarning("Failed to flip image"); - - SetUnpackAlignement(bpp); - - LockTexture(m_impl); - switch (m_impl->type) - { - case nzImageType_1D: - glTexSubImage1D(GL_TEXTURE_1D, level, rect.x, rect.width, format.dataFormat, format.dataType, mirrored.GetConstPixels()); - break; - - case nzImageType_1D_Array: - case nzImageType_2D: - glTexSubImage2D(openglTarget[m_impl->type], level, rect.x, height-rect.height-rect.y, rect.width, rect.height, format.dataFormat, format.dataType, mirrored.GetConstPixels()); - break; - - case nzImageType_2D_Array: - case nzImageType_3D: - glTexSubImage3D(openglTarget[m_impl->type], level, rect.x, height-rect.height-rect.y, z, rect.width, rect.height, 1, format.dataFormat, format.dataType, mirrored.GetConstPixels()); - break; - - case nzImageType_Cubemap: - NazaraError("Update used on a cubemap texture, please enable safe mode"); - break; - } - UnlockTexture(m_impl); - - return true; -} - -bool NzTexture::Update(const nzUInt8* pixels, const NzCubeui& cube, nzUInt8 level) -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Texture must be valid"); - return false; - } - - if (m_impl->isTarget) - { - NazaraError("Texture is a target, it cannot be updated"); - return false; - } - - if (m_impl->type == nzImageType_Cubemap) - { - NazaraError("Update is not designed for cubemaps, use UpdateFace instead"); - return false; - } - - if (!pixels) - { - NazaraError("Invalid pixel source"); - return false; - } - - if (!cube.IsValid()) - { - NazaraError("Invalid rectangle"); - return false; - } - #endif - - unsigned int height = std::max(m_impl->height >> level, 1U); - - #if NAZARA_RENDERER_SAFE - if (cube.x+cube.width > std::max(m_impl->width >> level, 1U) || - cube.y+cube.height > height || - cube.z+cube.depth > std::max(m_impl->depth >> level, 1U)) - { - NazaraError("Cube dimensions are out of bounds"); - return false; - } - - if (level >= m_impl->levelCount) - { - NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_impl->levelCount) + ')'); - return false; - } - #endif - - OpenGLFormat format; - if (!GetOpenGLFormat(m_impl->format, &format)) - { - NazaraError("Failed to get OpenGL format"); - return false; - } - - nzUInt8 bpp = NzPixelFormat::GetBPP(m_impl->format); - - // Inversion de la texture pour le repère d'OpenGL - unsigned int size = cube.width*cube.height*cube.depth*bpp; - nzUInt8* mirrored = new nzUInt8[size]; - if (!NzPixelFormat::Flip(nzPixelFlipping_Vertically, m_impl->format, cube.width, cube.height, cube.depth, pixels, mirrored)) - { - NazaraWarning("Failed to flip image"); - std::memcpy(mirrored, pixels, size); - } - - SetUnpackAlignement(bpp); - - LockTexture(m_impl); - switch (m_impl->type) - { - case nzImageType_1D: - glTexSubImage1D(GL_TEXTURE_1D, level, cube.x, cube.width, format.dataFormat, format.dataType, mirrored); - break; - - case nzImageType_1D_Array: - case nzImageType_2D: - glTexSubImage2D(openglTarget[m_impl->type], level, cube.x, height-cube.height-cube.y, cube.width, cube.height, format.dataFormat, format.dataType, mirrored); - break; - - case nzImageType_2D_Array: - case nzImageType_3D: - glTexSubImage3D(openglTarget[m_impl->type], level, cube.x, height-cube.height-cube.y, cube.z, cube.width, cube.height, cube.depth, format.dataFormat, format.dataType, mirrored); - break; - - case nzImageType_Cubemap: - NazaraError("Update used on a cubemap texture, please enable safe mode"); - break; - } - UnlockTexture(m_impl); - - delete[] mirrored; - - return true; -} - -bool NzTexture::UpdateFace(nzCubemapFace face, const NzImage& image, nzUInt8 level) -{ - #if NAZARA_RENDERER_SAFE - if (!image.IsValid()) - { - NazaraError("Image must be valid"); - return false; - } - - if (image.GetFormat() != m_impl->format) - { - NazaraError("Image format does not match texture format"); - return false; - } - #endif - - return UpdateFace(face, image.GetConstPixels(level), NzRectui(0, 0, image.GetWidth(), image.GetHeight()), level); -} - -bool NzTexture::UpdateFace(nzCubemapFace face, const NzImage& image, const NzRectui& rect, nzUInt8 level) -{ - #if NAZARA_RENDERER_SAFE - if (!image.IsValid()) - { - NazaraError("Image must be valid"); - return false; - } - - if (image.GetFormat() != m_impl->format) - { - NazaraError("Image format does not match texture format"); - return false; - } - #endif - - glPixelStorei(GL_UNPACK_ROW_LENGTH, image.GetWidth(level)); - - bool success = UpdateFace(face, image.GetConstPixels(level), rect, level); - - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - - return success; -} - -bool NzTexture::UpdateFace(nzCubemapFace face, const nzUInt8* pixels, nzUInt8 level) -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Texture must be valid"); - return false; - } - #endif - - return UpdateFace(face, pixels, NzRectui(0, 0, m_impl->width, m_impl->height), level); -} - -bool NzTexture::UpdateFace(nzCubemapFace face, const nzUInt8* pixels, const NzRectui& rect, nzUInt8 level) -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Texture must be valid"); - return false; - } - - if (m_impl->isTarget) - { - NazaraError("Texture is a target, it cannot be updated"); - return false; - } - - if (m_impl->type != nzImageType_Cubemap) - { - NazaraError("UpdateFace is designed for cubemaps, use Update instead"); - return false; - } - - if (!pixels) - { - NazaraError("Invalid pixel source"); - return false; - } - - if (!rect.IsValid()) - { - NazaraError("Invalid rectangle"); - return false; - } - #endif - - unsigned int height = std::max(m_impl->height >> level, 1U); - - #if NAZARA_RENDERER_SAFE - if (rect.x+rect.width > std::max(m_impl->width >> level, 1U) || rect.y+rect.height > height) - { - NazaraError("Rectangle dimensions are out of bounds"); - return false; - } - - if (level >= m_impl->levelCount) - { - NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_impl->levelCount) + ')'); - return false; - } - #endif - - OpenGLFormat format; - if (!GetOpenGLFormat(m_impl->format, &format)) - { - NazaraError("Failed to get OpenGL format"); - return false; - } - - nzUInt8 bpp = NzPixelFormat::GetBPP(m_impl->format); - - // Inversion de la texture pour le repère d'OpenGL - unsigned int size = rect.width*rect.height*bpp; - nzUInt8* mirrored = new nzUInt8[size]; - if (!NzPixelFormat::Flip(nzPixelFlipping_Vertically, m_impl->format, rect.width, rect.height, 1, pixels, mirrored)) - { - NazaraWarning("Failed to flip image"); - std::memcpy(mirrored, pixels, size); - } - - SetUnpackAlignement(bpp); - - LockTexture(m_impl); - glTexSubImage2D(cubemapFace[face], level, rect.x, height-rect.height-rect.y, rect.width, rect.height, format.dataFormat, format.dataType, mirrored); - UnlockTexture(m_impl); - - - return true; -} - -void NzTexture::Unlock() -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Texture must be valid"); - return; - } - #endif - - UnlockTexture(m_impl); -} - -unsigned int NzTexture::GetValidSize(unsigned int size) -{ - if (NzRenderer::HasCapability(nzRendererCap_TextureNPOT)) - return size; - else - { - unsigned int pot = 1; - while (pot < size) - pot <<= 1; - - return pot; - } -} - -bool NzTexture::IsFormatSupported(nzPixelFormat format) -{ - switch (format) - { - // Formats de base - case nzPixelFormat_BGR8: - case nzPixelFormat_BGRA8: - case nzPixelFormat_RGB8: - case nzPixelFormat_RGBA8: - return true; - - // Packed formats supportés depuis OpenGL 1.2 - case nzPixelFormat_RGB5A1: - case nzPixelFormat_RGBA4: - return true; - - // Dépréciés depuis OpenGL 3 (FIXME: Il doit bien exister des remplaçants ..) - case nzPixelFormat_L8: - case nzPixelFormat_LA8: - return false; - - case nzPixelFormat_DXT1: - case nzPixelFormat_DXT3: - case nzPixelFormat_DXT5: - { - static const bool supported = NzOpenGL::IsSupported(NzOpenGL::TextureCompression_s3tc); - return supported; - } - - case nzPixelFormat_Undefined: - break; - } - - NazaraError("Invalid pixel format"); - - return false; -} - -bool NzTexture::IsMipmappingSupported() -{ - return glGenerateMipmap != nullptr; -} - -bool NzTexture::IsTypeSupported(nzImageType type) -{ - switch (type) - { - case nzImageType_1D: - case nzImageType_2D: - case nzImageType_3D: - case nzImageType_Cubemap: - return true; // Tous supportés nativement dans OpenGL 2 - - case nzImageType_1D_Array: - case nzImageType_2D_Array: - { - static bool supported = NzOpenGL::IsSupported(NzOpenGL::TextureArray); - return supported; - } - } - - NazaraError("Image type not handled (0x" + NzString::Number(type, 16) + ')'); - return false; -} - -void NzTexture::SetTarget(bool isTarget) -{ - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraInternalError("Texture must be valid"); - return; - } - #endif - - m_impl->isTarget = isTarget; -} +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include +#include + +struct NzTextureImpl +{ + GLuint id; + nzImageType type; + nzPixelFormat format; + nzUInt8 levelCount; + NzRenderTexture* renderTexture = nullptr; + bool mipmapping = false; + bool mipmapsUpdated = true; + unsigned int depth; + unsigned int height; + unsigned int width; +}; + +namespace +{ + bool CreateTexture(NzTextureImpl* impl, bool proxy) + { + NzOpenGL::Format openGLFormat; + if (!NzOpenGL::TranslateFormat(impl->format, &openGLFormat, NzOpenGL::FormatType_Texture)) + { + NazaraError("Format not supported by OpenGL"); + return false; + } + + GLenum target = (proxy) ? NzOpenGL::TextureTargetProxy[impl->type] : NzOpenGL::TextureTarget[impl->type]; + GLint previous; + glGetIntegerv(NzOpenGL::TextureTargetBinding[impl->type], &previous); + switch (impl->type) + { + case nzImageType_1D: + { + if (glTexStorage1D) + glTexStorage1D(target, impl->levelCount, openGLFormat.internalFormat, impl->width); + else + { + unsigned int w = impl->width; + for (nzUInt8 level = 0; level < impl->levelCount; ++level) + { + glTexImage1D(target, level, openGLFormat.internalFormat, w, 0, openGLFormat.dataFormat, openGLFormat.dataType, nullptr); + if (w > 1U) + w >>= 1; + } + } + break; + } + + case nzImageType_1D_Array: + case nzImageType_2D: + { + if (glTexStorage2D) + glTexStorage2D(target, impl->levelCount, openGLFormat.internalFormat, impl->width, impl->height); + else + { + unsigned int w = impl->width; + unsigned int h = impl->height; + for (nzUInt8 level = 0; level < impl->levelCount; ++level) + { + glTexImage2D(target, level, openGLFormat.internalFormat, w, h, 0, openGLFormat.dataFormat, openGLFormat.dataType, nullptr); + if (w > 1U) + w >>= 1; + + if (h > 1U) + h >>= 1; + } + } + break; + } + + case nzImageType_2D_Array: + case nzImageType_3D: + { + if (glTexStorage3D) + glTexStorage3D(target, impl->levelCount, openGLFormat.internalFormat, impl->width, impl->height, impl->depth); + else + { + unsigned int w = impl->width; + unsigned int h = impl->height; + unsigned int d = impl->depth; + for (nzUInt8 level = 0; level < impl->levelCount; ++level) + { + glTexImage3D(target, level, openGLFormat.internalFormat, w, h, d, 0, openGLFormat.dataFormat, openGLFormat.dataType, nullptr); + if (w > 1U) + w >>= 1; + + if (h > 1U) + h >>= 1; + + if (d > 1U) + d >>= 1; + } + } + break; + } + + case nzImageType_Cubemap: + { + if (glTexStorage2D) + glTexStorage2D(target, impl->levelCount, openGLFormat.internalFormat, impl->width, impl->height); + else + { + unsigned int size = impl->width; // Les cubemaps ont une longueur et largeur identique + for (nzUInt8 level = 0; level < impl->levelCount; ++level) + { + for (GLenum face : NzOpenGL::CubemapFace) + glTexImage2D(face, level, openGLFormat.internalFormat, size, size, 0, openGLFormat.dataFormat, openGLFormat.dataType, nullptr); + + if (size > 1U) + size >>= 1; + } + } + break; + } + } + + if (proxy) + { + GLint internalFormat = 0; + glGetTexLevelParameteriv(target, 0, GL_TEXTURE_INTERNAL_FORMAT, &internalFormat); + if (internalFormat == 0) + return false; + } + + return true; + } + + static unsigned short lockedLevel[nzImageType_Max+1] = {0}; + static GLuint lockedPrevious[nzImageType_Max+1] = {0}; + + void LockTexture(NzTextureImpl* impl) + { + if (lockedLevel[impl->type]++ == 0) + { + NzContext::EnsureContext(); + + GLint previous; + glGetIntegerv(NzOpenGL::TextureTargetBinding[impl->type], &previous); + + lockedPrevious[impl->type] = static_cast(previous); + + if (lockedPrevious[impl->type] != impl->id) + glBindTexture(NzOpenGL::TextureTarget[impl->type], impl->id); + } + } + + inline void SetUnpackAlignement(nzUInt8 bpp) + { + if (bpp % 8 == 0) + glPixelStorei(GL_UNPACK_ALIGNMENT, 8); + else if (bpp % 4 == 0) + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + else if (bpp % 2 == 0) + glPixelStorei(GL_UNPACK_ALIGNMENT, 2); + else + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + } + + void UnlockTexture(NzTextureImpl* impl) + { + #ifdef NAZARA_DEBUG + if (NzContext::GetCurrent() == nullptr) + { + NazaraError("No active context"); + return; + } + #endif + + #if NAZARA_RENDERER_SAFE + if (lockedLevel[impl->type] == 0) + { + NazaraError("Unlock called on non-locked texture"); + return; + } + #endif + + if (--lockedLevel[impl->type] == 0 && lockedPrevious[impl->type] != impl->id) + glBindTexture(NzOpenGL::TextureTarget[impl->type], lockedPrevious[impl->type]); + } +} + +NzTexture::NzTexture() : +m_impl(nullptr) +{ +} + +NzTexture::NzTexture(const NzImage& image) : +m_impl(nullptr) +{ + LoadFromImage(image); + + #ifdef NAZARA_DEBUG + if (!m_impl) + { + NazaraError("Failed to create texture"); + throw std::runtime_error("Constructor failed"); + } + #endif +} + +NzTexture::~NzTexture() +{ + Destroy(); +} + +bool NzTexture::Create(nzImageType type, nzPixelFormat format, unsigned int width, unsigned int height, unsigned int depth, nzUInt8 levelCount, bool lock) +{ + Destroy(); + + #if NAZARA_RENDERER_SAFE + if (!IsTypeSupported(type)) + { + NazaraError("Texture's type not supported"); + return false; + } + + if (!NzPixelFormat::IsValid(format)) + { + NazaraError("Invalid pixel format"); + return false; + } + + if (!IsFormatSupported(format)) + { + NazaraError("Texture's format not supported"); + return false; + } + + if (width == 0) + { + NazaraError("Width must be at least 1 (0)"); + return false; + } + + if (height == 0) + { + NazaraError("Height must be at least 1 (0)"); + return false; + } + + if (depth == 0) + { + NazaraError("Depth must be at least 1 (0)"); + return false; + } + + switch (type) + { + case nzImageType_1D: + if (height > 1) + { + NazaraError("One dimensional texture's height must be 1"); + return false; + } + + if (depth > 1) + { + NazaraError("1D textures must be 1 depth"); + return false; + } + break; + + case nzImageType_1D_Array: + case nzImageType_2D: + if (depth > 1) + { + NazaraError("2D textures must be 1 depth"); + return false; + } + break; + + case nzImageType_2D_Array: + case nzImageType_3D: + break; + + case nzImageType_Cubemap: + if (depth > 1) + { + NazaraError("Cubemaps must be 1 depth"); + return false; + } + + if (width != height) + { + NazaraError("Cubemaps must have square dimensions"); + return false; + } + break; + } + #endif + + NzContext::EnsureContext(); + + if (IsMipmappingSupported()) + levelCount = std::min(levelCount, NzImage::GetMaxLevel(width, height, depth)); + else if (levelCount > 1) + { + NazaraWarning("Mipmapping not supported, reducing level count to 1"); + levelCount = 1; + } + + NzTextureImpl* impl = new NzTextureImpl; + glGenTextures(1, &impl->id); + + impl->depth = GetValidSize(depth); + impl->format = format; + impl->height = GetValidSize(height); + impl->levelCount = levelCount; + impl->type = type; + impl->width = GetValidSize(width); + + LockTexture(impl); + + // Vérification du support par la carte graphique + /*if (!CreateTexture(impl, true)) + { + NazaraError("Texture's parameters not supported by driver"); + UnlockTexture(impl); + glDeleteTextures(1, &impl->id); + delete impl; + + return false; + }*/ + + // Création de la texture + if (!CreateTexture(impl, false)) + { + NazaraError("Failed to create texture"); + UnlockTexture(impl); + glDeleteTextures(1, &impl->id); + delete impl; + + return false; + } + + m_impl = impl; + + // Paramètres par défaut + SetFilterMode(nzTextureFilter_Nearest); + SetMipmapRange(0, m_impl->levelCount); + SetWrapMode(nzTextureWrap_Repeat); + + if (m_impl->levelCount > 1U) + EnableMipmapping(true); + + if (!lock) + UnlockTexture(impl); + + NotifyCreated(); + return true; +} + +void NzTexture::Destroy() +{ + if (m_impl) + { + NotifyDestroy(); + + NzContext::EnsureContext(); + + glDeleteTextures(1, &m_impl->id); + delete m_impl; + m_impl = nullptr; + } +} + +bool NzTexture::Download(NzImage* image) const +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Texture must be valid"); + return false; + } + + if (!image) + { + NazaraError("Image must be valid"); + return false; + } + #endif + + NzOpenGL::Format format; + if (!NzOpenGL::TranslateFormat(m_impl->format, &format, NzOpenGL::FormatType_Texture)) + { + NazaraError("Failed to get OpenGL format"); + return false; + } + + if (!image->Create(m_impl->type, m_impl->format, m_impl->width, m_impl->height, m_impl->depth, m_impl->levelCount)) + { + NazaraError("Failed to create image"); + return false; + } + + LockTexture(m_impl); + + unsigned int width = m_impl->width; + unsigned int height = m_impl->height; + unsigned int depth = m_impl->depth; + + // Téléchargement... + for (nzUInt8 level = 0; level < m_impl->levelCount; ++level) + { + glGetTexImage(NzOpenGL::TextureTarget[m_impl->type], level, format.dataFormat, format.dataType, image->GetPixels(level)); + + if (width > 1) + width >>= 1; + + if (height > 1) + height >>= 1; + + if (depth > 1) + depth >>= 1; + } + + UnlockTexture(m_impl); + + // Inversion de la texture pour le repère d'OpenGL + if (!image->FlipVertically()) + NazaraWarning("Failed to flip image"); + + return true; +} + +bool NzTexture::EnableMipmapping(bool enable) +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Texture must be valid"); + return false; + } + #endif + + if (m_impl->levelCount == 1) + return true; + + if (!IsMipmappingSupported()) + { + NazaraError("Mipmapping not supported"); + return false; + } + + if (!m_impl->mipmapping && enable) + m_impl->mipmapsUpdated = false; + + m_impl->mipmapping = enable; + + return true; +} + +unsigned int NzTexture::GetAnisotropyLevel() const +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Texture must be valid"); + return 0; + } + #endif + + if (!NzOpenGL::IsSupported(nzOpenGLExtension_AnisotropicFilter)) + return 1; + + LockTexture(m_impl); + + GLint anisotropyLevel; + glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropyLevel); + + UnlockTexture(m_impl); + + return anisotropyLevel; +} + +nzUInt8 NzTexture::GetBPP() const +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Texture must be valid"); + return 0; + } + #endif + + return NzPixelFormat::GetBPP(m_impl->format); +} + +unsigned int NzTexture::GetDepth() const +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Texture must be valid"); + return 0; + } + #endif + + return m_impl->depth; +} + +nzTextureFilter NzTexture::GetFilterMode() const +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Texture must be valid"); + return nzTextureFilter_Unknown; + } + #endif + + LockTexture(m_impl); + + GLint value; + glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, &value); + + UnlockTexture(m_impl); + + GLenum filterMode = static_cast(value); + switch (filterMode) + { + case GL_LINEAR: + case GL_LINEAR_MIPMAP_NEAREST: + return nzTextureFilter_Bilinear; + + case GL_NEAREST: + case GL_NEAREST_MIPMAP_NEAREST: + return nzTextureFilter_Nearest; + + case GL_LINEAR_MIPMAP_LINEAR: + return nzTextureFilter_Trilinear; + + default: + NazaraInternalError("OpenGL filter mode not handled (0x" + NzString::Number(filterMode, 16) + ')'); + return nzTextureFilter_Unknown; + } +} + +nzPixelFormat NzTexture::GetFormat() const +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Texture must be valid"); + return nzPixelFormat_Undefined; + } + #endif + + return m_impl->format; +} + +unsigned int NzTexture::GetHeight() const +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Texture must be valid"); + return 0; + } + #endif + + return m_impl->height; +} + +nzImageType NzTexture::GetType() const +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Texture must be valid"); + return nzImageType_2D; + } + #endif + + return m_impl->type; +} + +unsigned int NzTexture::GetWidth() const +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Texture must be valid"); + return 0; + } + #endif + + return m_impl->width; +} + +nzTextureWrap NzTexture::GetWrapMode() const +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Texture must be valid"); + return nzTextureWrap_Unknown; + } + #endif + + LockTexture(m_impl); + + GLint value; + glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, &value); + + UnlockTexture(m_impl); + + GLenum wrapMode = static_cast(value); + switch (wrapMode) + { + case GL_CLAMP_TO_EDGE: + return nzTextureWrap_Clamp; + + case GL_REPEAT: + return nzTextureWrap_Repeat; + + default: + NazaraInternalError("OpenGL wrap mode not handled (0x" + NzString::Number(wrapMode, 16) + ')'); + return nzTextureWrap_Unknown; + } +} + +bool NzTexture::IsCompressed() const +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Texture must be valid"); + return false; + } + #endif + + return NzPixelFormat::IsCompressed(m_impl->format); +} + +bool NzTexture::IsCubemap() const +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Texture must be valid"); + return false; + } + #endif + + return m_impl->type == nzImageType_Cubemap; +} + +bool NzTexture::IsTarget() const +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Texture must be valid"); + return false; + } + #endif + + return m_impl->renderTexture != nullptr; +} + +bool NzTexture::IsValid() const +{ + return m_impl != nullptr; +} + +bool NzTexture::LoadFromFile(const NzString& filePath, const NzImageParams& params) +{ + NzImage image; + if (!image.LoadFromFile(filePath, params)) + { + NazaraError("Failed to load image"); + return false; + } + + return LoadFromImage(image); +} + +bool NzTexture::LoadFromImage(const NzImage& image) +{ + #if NAZARA_RENDERER_SAFE + if (!image.IsValid()) + { + NazaraError("Image must be valid"); + return false; + } + #endif + + // Vive le Copy-On-Write + NzImage newImage(image); + + nzPixelFormat format = newImage.GetFormat(); + if (!IsFormatSupported(format)) + { + nzPixelFormat newFormat = (NzPixelFormat::HasAlpha(format)) ? nzPixelFormat_BGRA8 : nzPixelFormat_BGR8; + NazaraWarning("Format not supported, trying to convert it to " + NzPixelFormat::ToString(newFormat) + "..."); + + if (NzPixelFormat::IsConversionSupported(format, newFormat)) + { + if (newImage.Convert(newFormat)) + { + NazaraWarning("Conversion succeed"); + format = newFormat; + } + else + { + NazaraError("Conversion failed"); + return false; + } + } + else + { + NazaraError("Conversion not supported"); + return false; + } + } + + nzImageType type = newImage.GetType(); + if (!Create(type, format, newImage.GetWidth(), newImage.GetHeight(), newImage.GetDepth(), newImage.GetLevelCount(), true)) + { + NazaraError("Failed to create texture"); + return false; + } + + for (nzUInt8 level = 0; level < m_impl->levelCount; ++level) + { + if (!Update(newImage.GetConstPixels(level), level)) + { + NazaraError("Failed to update texture"); + Destroy(); + + return false; + } + } + + UnlockTexture(m_impl); + + return true; +} + +bool NzTexture::LoadFromMemory(const void* data, std::size_t size, const NzImageParams& params) +{ + NzImage image; + if (!image.LoadFromMemory(data, size, params)) + { + NazaraError("Failed to load image"); + return false; + } + + return LoadFromImage(image); +} + +bool NzTexture::LoadFromStream(NzInputStream& stream, const NzImageParams& params) +{ + NzImage image; + if (!image.LoadFromStream(stream, params)) + { + NazaraError("Failed to load image"); + return false; + } + + return LoadFromImage(image); +} + +bool NzTexture::Lock() +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Texture must be valid"); + return false; + } + #endif + + LockTexture(m_impl); + + return true; +} + +bool NzTexture::SetAnisotropyLevel(unsigned int anistropyLevel) +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Texture must be valid"); + return false; + } + #endif + + if (anistropyLevel > 1 && !NzOpenGL::IsSupported(nzOpenGLExtension_AnisotropicFilter)) + { + NazaraError("Anisotropic filter not supported"); + return false; + } + + LockTexture(m_impl); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anistropyLevel); + + UnlockTexture(m_impl); + + return true; +} + +bool NzTexture::SetFilterMode(nzTextureFilter filter) +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Texture must be valid"); + return false; + } + + if (filter == nzTextureFilter_Trilinear && m_impl->levelCount == 1) + { + NazaraError("Trilinear filter set wihout mipmaps"); + return false; + } + #endif + + LockTexture(m_impl); + + GLenum target = NzOpenGL::TextureTarget[m_impl->type]; + switch (filter) + { + case nzTextureFilter_Bilinear: + if (m_impl->mipmapping > 1) + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); + else + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + break; + + case nzTextureFilter_Nearest: + if (m_impl->mipmapping > 1) + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); + else + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + break; + + case nzTextureFilter_Trilinear: + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + break; + + default: + NazaraError("Texture filter not handled (0x" + NzString::Number(filter, 16) + ')'); + } + + UnlockTexture(m_impl); + + return true; +} + +bool NzTexture::SetMipmapRange(nzUInt8 minLevel, nzUInt8 maxLevel) +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Texture must be valid"); + return false; + } + + if (minLevel >= m_impl->levelCount) + { + NazaraError("Minimum level cannot be greater or equal than level count (" + NzString::Number(minLevel) + " >= " + NzString::Number(m_impl->levelCount) + ')'); + return false; + } + + if (maxLevel < minLevel) + { + NazaraError("Minimum level cannot be greater than maximum level (" + NzString::Number(minLevel) + " < " + NzString::Number(maxLevel) + ')'); + return false; + } + #endif + + LockTexture(m_impl); + glTexParameteri(NzOpenGL::TextureTarget[m_impl->type], GL_TEXTURE_BASE_LEVEL, minLevel); + glTexParameteri(NzOpenGL::TextureTarget[m_impl->type], GL_TEXTURE_MAX_LEVEL, std::min(m_impl->levelCount, maxLevel)); + UnlockTexture(m_impl); + + return true; +} + +bool NzTexture::SetWrapMode(nzTextureWrap wrap) +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Texture must be valid"); + return false; + } + #endif + + GLenum wrapMode; + switch (wrap) + { + case nzTextureWrap_Clamp: + wrapMode = GL_CLAMP_TO_EDGE; + break; + + case nzTextureWrap_Repeat: + wrapMode = GL_REPEAT; + break; + + default: + NazaraError("Texture wrap mode not handled (0x" + NzString::Number(wrap, 16) + ')'); + return false; + } + + LockTexture(m_impl); + + GLenum target = NzOpenGL::TextureTarget[m_impl->type]; + switch (m_impl->type) + { + // Notez l'absence de "break" ici + case nzImageType_3D: + glTexParameteri(target, GL_TEXTURE_WRAP_R, wrapMode); + case nzImageType_2D: + case nzImageType_2D_Array: + case nzImageType_Cubemap: + glTexParameteri(target, GL_TEXTURE_WRAP_T, wrapMode); + case nzImageType_1D: + case nzImageType_1D_Array: + glTexParameteri(target, GL_TEXTURE_WRAP_S, wrapMode); + break; + + default: + break; + } + + UnlockTexture(m_impl); + + return true; +} + +bool NzTexture::Update(const NzImage& image, nzUInt8 level) +{ + #if NAZARA_RENDERER_SAFE + if (!image.IsValid()) + { + NazaraError("Image must be valid"); + return false; + } + + if (image.GetFormat() != m_impl->format) + { + NazaraError("Image format does not match texture format"); + return false; + } + #endif + + return Update(image.GetConstPixels(level), level); +} + +bool NzTexture::Update(const NzImage& image, const NzRectui& rect, unsigned int z, nzUInt8 level) +{ + #if NAZARA_RENDERER_SAFE + if (!image.IsValid()) + { + NazaraError("Image must be valid"); + return false; + } + + if (image.GetFormat() != m_impl->format) + { + NazaraError("Image format does not match texture format"); + return false; + } + #endif + + const nzUInt8* pixels = image.GetConstPixels(level, rect.x, rect.y, z); + if (!pixels) + { + NazaraError("Failed to access image's pixels"); + return false; + } + + glPixelStorei(GL_UNPACK_ROW_LENGTH, image.GetWidth(level)); + + bool success = Update(pixels, rect, z, level); + + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + + return success; +} + +bool NzTexture::Update(const NzImage& image, const NzCubeui& cube, nzUInt8 level) +{ + #if NAZARA_RENDERER_SAFE + if (!image.IsValid()) + { + NazaraError("Image must be valid"); + return false; + } + + if (image.GetFormat() != m_impl->format) + { + NazaraError("Image format does not match texture format"); + return false; + } + #endif + + const nzUInt8* pixels = image.GetConstPixels(level, cube.x, cube.y, cube.z); + if (!pixels) + { + NazaraError("Failed to access image's pixels"); + return false; + } + + glPixelStorei(GL_UNPACK_ROW_LENGTH, image.GetWidth(level)); + glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, image.GetHeight(level)); + + bool success = Update(pixels, cube, level); + + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0); + + return success; +} + +bool NzTexture::Update(const nzUInt8* pixels, nzUInt8 level) +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Texture must be valid"); + return false; + } + #endif + + if (m_impl->type == nzImageType_3D || m_impl->type == nzImageType_2D_Array) + return Update(pixels, NzCubeui(0, 0, 0, std::max(m_impl->width >> level, 1U), std::max(m_impl->height >> level, 1U), std::max(m_impl->depth >> level, 1U)), level); + else + return Update(pixels, NzRectui(0, 0, std::max(m_impl->width >> level, 1U), std::max(m_impl->height >> level, 1U)), 0, level); +} + +bool NzTexture::Update(const nzUInt8* pixels, const NzRectui& rect, unsigned int z, nzUInt8 level) +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Texture must be valid"); + return false; + } + + if (m_impl->renderTexture) + { + NazaraError("Texture is a target, it cannot be updated"); + return false; + } + + if (m_impl->type == nzImageType_Cubemap) + { + NazaraError("Update is not designed for cubemaps, use UpdateFace instead"); + return false; + } + + if (!pixels) + { + NazaraError("Invalid pixel source"); + return false; + } + + if (!rect.IsValid()) + { + NazaraError("Invalid rectangle"); + return false; + } + #endif + + unsigned int height = std::max(m_impl->height >> level, 1U); + + #if NAZARA_RENDERER_SAFE + if (rect.x+rect.width > std::max(m_impl->width >> level, 1U) || rect.y+rect.height > height) + { + NazaraError("Rectangle dimensions are out of bounds"); + return false; + } + + if (z >= std::max(m_impl->depth >> level, 1U)) + { + NazaraError("Z value exceeds depth (" + NzString::Number(z) + " >= (" + NzString::Number(m_impl->depth) + ')'); + return false; + } + + if (level >= m_impl->levelCount) + { + NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_impl->levelCount) + ')'); + return false; + } + #endif + + NzOpenGL::Format format; + if (!NzOpenGL::TranslateFormat(m_impl->format, &format, NzOpenGL::FormatType_Texture)) + { + NazaraError("Failed to get OpenGL format"); + return false; + } + + nzUInt8 bpp = NzPixelFormat::GetBPP(m_impl->format); + + // Inversion de la texture pour le repère d'OpenGL + NzImage mirrored; + mirrored.Create(m_impl->type, m_impl->format, rect.width, rect.height); + mirrored.Update(pixels); + + if (!mirrored.FlipVertically()) + NazaraWarning("Failed to flip image"); + + SetUnpackAlignement(bpp); + + LockTexture(m_impl); + switch (m_impl->type) + { + case nzImageType_1D: + glTexSubImage1D(GL_TEXTURE_1D, level, rect.x, rect.width, format.dataFormat, format.dataType, mirrored.GetConstPixels()); + break; + + case nzImageType_1D_Array: + case nzImageType_2D: + glTexSubImage2D(NzOpenGL::TextureTarget[m_impl->type], level, rect.x, height-rect.height-rect.y, rect.width, rect.height, format.dataFormat, format.dataType, mirrored.GetConstPixels()); + break; + + case nzImageType_2D_Array: + case nzImageType_3D: + glTexSubImage3D(NzOpenGL::TextureTarget[m_impl->type], level, rect.x, height-rect.height-rect.y, z, rect.width, rect.height, 1, format.dataFormat, format.dataType, mirrored.GetConstPixels()); + break; + + case nzImageType_Cubemap: + NazaraError("Update used on a cubemap texture, please enable safe mode"); + break; + } + UnlockTexture(m_impl); + + return true; +} + +bool NzTexture::Update(const nzUInt8* pixels, const NzCubeui& cube, nzUInt8 level) +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Texture must be valid"); + return false; + } + + if (m_impl->renderTexture) + { + NazaraError("Texture is a target, it cannot be updated"); + return false; + } + + if (m_impl->type == nzImageType_Cubemap) + { + NazaraError("Update is not designed for cubemaps, use UpdateFace instead"); + return false; + } + + if (!pixels) + { + NazaraError("Invalid pixel source"); + return false; + } + + if (!cube.IsValid()) + { + NazaraError("Invalid rectangle"); + return false; + } + #endif + + unsigned int height = std::max(m_impl->height >> level, 1U); + + #if NAZARA_RENDERER_SAFE + if (cube.x+cube.width > std::max(m_impl->width >> level, 1U) || + cube.y+cube.height > height || + cube.z+cube.depth > std::max(m_impl->depth >> level, 1U)) + { + NazaraError("Cube dimensions are out of bounds"); + return false; + } + + if (level >= m_impl->levelCount) + { + NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_impl->levelCount) + ')'); + return false; + } + #endif + + NzOpenGL::Format format; + if (!NzOpenGL::TranslateFormat(m_impl->format, &format, NzOpenGL::FormatType_Texture)) + { + NazaraError("Failed to get OpenGL format"); + return false; + } + + nzUInt8 bpp = NzPixelFormat::GetBPP(m_impl->format); + + // Inversion de la texture pour le repère d'OpenGL + unsigned int size = cube.width*cube.height*cube.depth*bpp; + nzUInt8* mirrored = new nzUInt8[size]; + if (!NzPixelFormat::Flip(nzPixelFlipping_Vertically, m_impl->format, cube.width, cube.height, cube.depth, pixels, mirrored)) + { + NazaraWarning("Failed to flip image"); + std::memcpy(mirrored, pixels, size); + } + + SetUnpackAlignement(bpp); + + LockTexture(m_impl); + switch (m_impl->type) + { + case nzImageType_1D: + glTexSubImage1D(GL_TEXTURE_1D, level, cube.x, cube.width, format.dataFormat, format.dataType, mirrored); + break; + + case nzImageType_1D_Array: + case nzImageType_2D: + glTexSubImage2D(NzOpenGL::TextureTarget[m_impl->type], level, cube.x, height-cube.height-cube.y, cube.width, cube.height, format.dataFormat, format.dataType, mirrored); + break; + + case nzImageType_2D_Array: + case nzImageType_3D: + glTexSubImage3D(NzOpenGL::TextureTarget[m_impl->type], level, cube.x, height-cube.height-cube.y, cube.z, cube.width, cube.height, cube.depth, format.dataFormat, format.dataType, mirrored); + break; + + case nzImageType_Cubemap: + NazaraError("Update used on a cubemap texture, please enable safe mode"); + break; + } + UnlockTexture(m_impl); + + delete[] mirrored; + + return true; +} + +bool NzTexture::UpdateFace(nzCubemapFace face, const NzImage& image, nzUInt8 level) +{ + #if NAZARA_RENDERER_SAFE + if (!image.IsValid()) + { + NazaraError("Image must be valid"); + return false; + } + + if (image.GetFormat() != m_impl->format) + { + NazaraError("Image format does not match texture format"); + return false; + } + #endif + + return UpdateFace(face, image.GetConstPixels(level), NzRectui(0, 0, image.GetWidth(), image.GetHeight()), level); +} + +bool NzTexture::UpdateFace(nzCubemapFace face, const NzImage& image, const NzRectui& rect, nzUInt8 level) +{ + #if NAZARA_RENDERER_SAFE + if (!image.IsValid()) + { + NazaraError("Image must be valid"); + return false; + } + + if (image.GetFormat() != m_impl->format) + { + NazaraError("Image format does not match texture format"); + return false; + } + #endif + + glPixelStorei(GL_UNPACK_ROW_LENGTH, image.GetWidth(level)); + + bool success = UpdateFace(face, image.GetConstPixels(level), rect, level); + + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + + return success; +} + +bool NzTexture::UpdateFace(nzCubemapFace face, const nzUInt8* pixels, nzUInt8 level) +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Texture must be valid"); + return false; + } + #endif + + return UpdateFace(face, pixels, NzRectui(0, 0, m_impl->width, m_impl->height), level); +} + +bool NzTexture::UpdateFace(nzCubemapFace face, const nzUInt8* pixels, const NzRectui& rect, nzUInt8 level) +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Texture must be valid"); + return false; + } + + if (m_impl->renderTexture) + { + NazaraError("Texture is a target, it cannot be updated"); + return false; + } + + if (m_impl->type != nzImageType_Cubemap) + { + NazaraError("UpdateFace is designed for cubemaps, use Update instead"); + return false; + } + + if (!pixels) + { + NazaraError("Invalid pixel source"); + return false; + } + + if (!rect.IsValid()) + { + NazaraError("Invalid rectangle"); + return false; + } + #endif + + unsigned int height = std::max(m_impl->height >> level, 1U); + + #if NAZARA_RENDERER_SAFE + if (rect.x+rect.width > std::max(m_impl->width >> level, 1U) || rect.y+rect.height > height) + { + NazaraError("Rectangle dimensions are out of bounds"); + return false; + } + + if (level >= m_impl->levelCount) + { + NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_impl->levelCount) + ')'); + return false; + } + #endif + + NzOpenGL::Format format; + if (!NzOpenGL::TranslateFormat(m_impl->format, &format, NzOpenGL::FormatType_Texture)) + { + NazaraError("Failed to get OpenGL format"); + return false; + } + + nzUInt8 bpp = NzPixelFormat::GetBPP(m_impl->format); + + // Inversion de la texture pour le repère d'OpenGL + unsigned int size = rect.width*rect.height*bpp; + nzUInt8* mirrored = new nzUInt8[size]; + if (!NzPixelFormat::Flip(nzPixelFlipping_Vertically, m_impl->format, rect.width, rect.height, 1, pixels, mirrored)) + { + NazaraWarning("Failed to flip image"); + std::memcpy(mirrored, pixels, size); + } + + SetUnpackAlignement(bpp); + + LockTexture(m_impl); + glTexSubImage2D(NzOpenGL::CubemapFace[face], level, rect.x, height-rect.height-rect.y, rect.width, rect.height, format.dataFormat, format.dataType, mirrored); + UnlockTexture(m_impl); + + + return true; +} + +void NzTexture::Unlock() +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Texture must be valid"); + return; + } + #endif + + UnlockTexture(m_impl); +} + +unsigned int NzTexture::GetOpenGLID() const +{ + #if NAZARA_RENDERER_SAFE + if (!m_impl) + { + NazaraError("Texture must be valid"); + return 0; + } + #endif + + return m_impl->id; +} + +bool NzTexture::Prepare() const +{ + #if NAZARA_RENDERER_SAFE + if (lockedLevel[m_impl->type] > 0) + { + NazaraError("Cannot bind texture while a texture is locked"); + return false; + } + #endif + + glBindTexture(NzOpenGL::TextureTarget[m_impl->type], m_impl->id); + + if (m_impl->mipmapping && !m_impl->mipmapsUpdated) + { + glGenerateMipmap(NzOpenGL::TextureTarget[m_impl->type]); + m_impl->mipmapsUpdated = true; + } + + return true; +} + +unsigned int NzTexture::GetValidSize(unsigned int size) +{ + if (NzRenderer::HasCapability(nzRendererCap_TextureNPOT)) + return size; + else + { + unsigned int pot = 1; + while (pot < size) + pot <<= 1; + + return pot; + } +} + +bool NzTexture::IsFormatSupported(nzPixelFormat format) +{ + switch (format) + { + // Formats de base + case nzPixelFormat_BGR8: + case nzPixelFormat_BGRA8: + case nzPixelFormat_RGB8: + case nzPixelFormat_RGBA8: + return true; + + // Packed formats supportés depuis OpenGL 1.2 + case nzPixelFormat_RGB5A1: + case nzPixelFormat_RGBA4: + return true; + + // Formats de profondeur (Supportés avec les FBOs) + case nzPixelFormat_Depth16: + case nzPixelFormat_Depth24: + case nzPixelFormat_Depth32: + case nzPixelFormat_Depth24Stencil8: + { + static const bool supported = NzOpenGL::IsSupported(nzOpenGLExtension_FrameBufferObject); + return supported; + } + + // Formats de stencil (Non supportés pour les textures) + case nzPixelFormat_Stencil1: + case nzPixelFormat_Stencil4: + case nzPixelFormat_Stencil8: + case nzPixelFormat_Stencil16: + return false; + + // Dépréciés depuis OpenGL 3 (FIXME: Il doit bien exister des remplaçants ..) + case nzPixelFormat_L8: + case nzPixelFormat_LA8: + return false; + + case nzPixelFormat_DXT1: + case nzPixelFormat_DXT3: + case nzPixelFormat_DXT5: + { + static const bool supported = NzOpenGL::IsSupported(nzOpenGLExtension_TextureCompression_s3tc); + return supported; + } + + case nzPixelFormat_Undefined: + break; + } + + NazaraError("Invalid pixel format"); + + return false; +} + +bool NzTexture::IsMipmappingSupported() +{ + return glGenerateMipmap != nullptr; +} + +bool NzTexture::IsTypeSupported(nzImageType type) +{ + switch (type) + { + case nzImageType_1D: + case nzImageType_2D: + case nzImageType_3D: + case nzImageType_Cubemap: + return true; // Tous supportés nativement dans OpenGL 2 + + case nzImageType_1D_Array: + case nzImageType_2D_Array: + { + static bool supported = NzOpenGL::IsSupported(nzOpenGLExtension_TextureArray); + return supported; + } + } + + NazaraError("Image type not handled (0x" + NzString::Number(type, 16) + ')'); + return false; +} + +NzRenderTexture* NzTexture::GetRenderTexture() const +{ + #ifdef NAZARA_DEBUG + if (!m_impl) + { + NazaraInternalError("Texture must be valid"); + return nullptr; + } + #endif + + return m_impl->renderTexture; +} + +void NzTexture::SetRenderTexture(NzRenderTexture* renderTexture) +{ + #ifdef NAZARA_DEBUG + if (!m_impl) + { + NazaraInternalError("Texture must be valid"); + return; + } + #endif + + m_impl->renderTexture = renderTexture; +} diff --git a/src/Nazara/Renderer/Win32/ContextImpl.cpp b/src/Nazara/Renderer/Win32/ContextImpl.cpp index 5cb657f48..15fd533d2 100644 --- a/src/Nazara/Renderer/Win32/ContextImpl.cpp +++ b/src/Nazara/Renderer/Win32/ContextImpl.cpp @@ -1,231 +1,234 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Renderer module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -// Code inspiré de NeHe (Lesson1) et de la SFML par Laurent Gomila - -#include -#include -#include -#include -#include -#include -#include -#include - -NzContextImpl::NzContextImpl() -{ -} - -bool NzContextImpl::Activate() -{ - return wglMakeCurrent(m_deviceContext, m_context); -} - -bool NzContextImpl::Create(NzContextParameters& parameters) -{ - if (parameters.window) - { - m_window = static_cast(parameters.window); - m_ownsWindow = false; - } - else - { - m_window = CreateWindowA("STATIC", nullptr, WS_DISABLED | WS_POPUP, 0, 0, 1, 1, nullptr, nullptr, GetModuleHandle(nullptr), nullptr); - if (!m_window) - { - NazaraError("Failed to create window"); - return false; - } - - ShowWindow(m_window, SW_HIDE); - m_ownsWindow = true; - } - - m_deviceContext = GetDC(m_window); - if (!m_deviceContext) - { - NazaraError("Failed to get device context"); - Destroy(); - return false; - } - - int pixelFormat = 0; - if (parameters.antialiasingLevel > 0) - { - if (wglChoosePixelFormat) - { - bool valid; - UINT numFormats; - - int attributes[] = { - WGL_DRAW_TO_WINDOW_ARB, GL_TRUE, - WGL_SUPPORT_OPENGL_ARB, GL_TRUE, - WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB, - WGL_COLOR_BITS_ARB, (parameters.bitsPerPixel == 32) ? 24 : parameters.bitsPerPixel, - WGL_ALPHA_BITS_ARB, (parameters.bitsPerPixel == 32) ? 8 : 0, - WGL_DEPTH_BITS_ARB, parameters.depthBits, - WGL_STENCIL_BITS_ARB, parameters.stencilBits, - WGL_DOUBLE_BUFFER_ARB, (parameters.doubleBuffered) ? GL_TRUE : GL_FALSE, - WGL_SAMPLE_BUFFERS_ARB, GL_TRUE, - WGL_SAMPLES_ARB, parameters.antialiasingLevel, - 0, 0 - }; - - do - { - valid = wglChoosePixelFormat(m_deviceContext, attributes, nullptr, 1, &pixelFormat, &numFormats); - } - while ((!valid || numFormats == 0) && --attributes[19] > 0); - - if (!valid) - { - NazaraWarning("Could not find a format matching requirements, disabling antialiasing..."); - pixelFormat = 0; - } - - parameters.antialiasingLevel = attributes[19]; - } - else - { - NazaraWarning("Antialiasing is not supported"); - parameters.antialiasingLevel = 0; - } - } - - PIXELFORMATDESCRIPTOR descriptor; - ZeroMemory(&descriptor, sizeof(PIXELFORMATDESCRIPTOR)); - descriptor.nSize = sizeof(PIXELFORMATDESCRIPTOR); - descriptor.nVersion = 1; - - if (pixelFormat == 0) - { - descriptor.cColorBits = parameters.bitsPerPixel; - descriptor.cDepthBits = parameters.depthBits; - descriptor.cStencilBits = parameters.stencilBits; - descriptor.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL; - descriptor.iPixelType = PFD_TYPE_RGBA; - - if (parameters.bitsPerPixel == 32) - descriptor.cAlphaBits = 8; - - if (parameters.doubleBuffered) - descriptor.dwFlags |= PFD_DOUBLEBUFFER; - - pixelFormat = ChoosePixelFormat(m_deviceContext, &descriptor); - if (pixelFormat == 0) - { - NazaraError("Failed to choose pixel format"); - Destroy(); - - return false; - } - } - - if (!SetPixelFormat(m_deviceContext, pixelFormat, &descriptor)) - { - NazaraError("Failed to set pixel format"); - Destroy(); - return false; - } - - // Arrivé ici, le format de pixel est choisi, nous récupérons donc les paramètres réels du futur contexte - if (DescribePixelFormat(m_deviceContext, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &descriptor) != 0) - { - parameters.bitsPerPixel = descriptor.cColorBits + descriptor.cAlphaBits; - parameters.depthBits = descriptor.cDepthBits; - parameters.stencilBits = descriptor.cDepthBits; - } - else - NazaraWarning("Failed to get context's parameters"); - - HGLRC shareContext = (parameters.shared) ? static_cast(parameters.shareContext->m_impl)->m_context : nullptr; - - m_context = nullptr; - if (wglCreateContextAttribs) - { - int attributes[4*2+1]; - int* attrib = attributes; - - *attrib++ = WGL_CONTEXT_MAJOR_VERSION_ARB; - *attrib++ = parameters.majorVersion; - - *attrib++ = WGL_CONTEXT_MINOR_VERSION_ARB; - *attrib++ = parameters.minorVersion; - - int flags = 0; - - if (parameters.majorVersion >= 3) - { - *attrib++ = WGL_CONTEXT_PROFILE_MASK_ARB; - if (parameters.compatibilityProfile) - *attrib++ = WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; - else - { - *attrib++ = WGL_CONTEXT_CORE_PROFILE_BIT_ARB; - - flags |= WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; - } - } - - if (parameters.debugMode) - flags |= WGL_CONTEXT_DEBUG_BIT_ARB; - - if (flags) - { - *attrib++ = WGL_CONTEXT_FLAGS_ARB; - *attrib++ = flags; - } - - *attrib++ = 0; - - m_context = wglCreateContextAttribs(m_deviceContext, shareContext, attributes); - } - - if (!m_context) - { - m_context = wglCreateContext(m_deviceContext); - - if (shareContext) - { - // wglShareLists n'est pas thread-safe (source: SFML) - static NzMutex mutex; - NzLockGuard lock(mutex); - - if (!wglShareLists(shareContext, m_context)) - NazaraWarning("Failed to share the context: " + NzGetLastSystemError()); - } - } - - if (!m_context) - { - NazaraError("Failed to create context"); - Destroy(); - return false; - } - - return true; -} - -void NzContextImpl::Destroy() -{ - if (m_context) - wglDeleteContext(m_context); - - if (m_deviceContext) - ReleaseDC(m_window, m_deviceContext); - - if (m_ownsWindow) - DestroyWindow(m_window); -} - -void NzContextImpl::SwapBuffers() -{ - ::SwapBuffers(m_deviceContext); -} - -bool NzContextImpl::Desactivate() -{ - return wglMakeCurrent(nullptr, nullptr); -} - +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +// Code inspiré de NeHe (Lesson1) et de la SFML par Laurent Gomila + +#include +#include +#include +#include +#include +#include +#include +#include + +NzContextImpl::NzContextImpl() +{ +} + +bool NzContextImpl::Activate() +{ + return wglMakeCurrent(m_deviceContext, m_context); +} + +bool NzContextImpl::Create(NzContextParameters& parameters) +{ + if (parameters.window) + { + m_window = static_cast(parameters.window); + m_ownsWindow = false; + } + else + { + m_window = CreateWindowA("STATIC", nullptr, WS_DISABLED | WS_POPUP, 0, 0, 1, 1, nullptr, nullptr, GetModuleHandle(nullptr), nullptr); + if (!m_window) + { + NazaraError("Failed to create window"); + return false; + } + + ShowWindow(m_window, SW_HIDE); + m_ownsWindow = true; + } + + m_deviceContext = GetDC(m_window); + if (!m_deviceContext) + { + NazaraError("Failed to get device context"); + Destroy(); + return false; + } + + int pixelFormat = 0; + if (parameters.antialiasingLevel > 0) + { + if (wglChoosePixelFormat) + { + bool valid; + UINT numFormats; + + int attributes[] = { + WGL_DRAW_TO_WINDOW_ARB, GL_TRUE, + WGL_SUPPORT_OPENGL_ARB, GL_TRUE, + WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB, + WGL_COLOR_BITS_ARB, (parameters.bitsPerPixel == 32) ? 24 : parameters.bitsPerPixel, + WGL_ALPHA_BITS_ARB, (parameters.bitsPerPixel == 32) ? 8 : 0, + WGL_DEPTH_BITS_ARB, parameters.depthBits, + WGL_STENCIL_BITS_ARB, parameters.stencilBits, + WGL_DOUBLE_BUFFER_ARB, (parameters.doubleBuffered) ? GL_TRUE : GL_FALSE, + WGL_SAMPLE_BUFFERS_ARB, GL_TRUE, + WGL_SAMPLES_ARB, parameters.antialiasingLevel, + 0, 0 + }; + + do + { + valid = wglChoosePixelFormat(m_deviceContext, attributes, nullptr, 1, &pixelFormat, &numFormats); + } + while ((!valid || numFormats == 0) && --attributes[19] > 0); + + if (!valid) + { + NazaraWarning("Could not find a format matching requirements, disabling antialiasing..."); + pixelFormat = 0; + } + + parameters.antialiasingLevel = attributes[19]; + } + else + { + NazaraWarning("Antialiasing is not supported"); + parameters.antialiasingLevel = 0; + } + } + + PIXELFORMATDESCRIPTOR descriptor; + ZeroMemory(&descriptor, sizeof(PIXELFORMATDESCRIPTOR)); + descriptor.nSize = sizeof(PIXELFORMATDESCRIPTOR); + descriptor.nVersion = 1; + + if (pixelFormat == 0) + { + descriptor.cColorBits = parameters.bitsPerPixel; + descriptor.cDepthBits = parameters.depthBits; + descriptor.cStencilBits = parameters.stencilBits; + descriptor.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL; + descriptor.iPixelType = PFD_TYPE_RGBA; + + if (parameters.bitsPerPixel == 32) + descriptor.cAlphaBits = 8; + + if (parameters.doubleBuffered) + descriptor.dwFlags |= PFD_DOUBLEBUFFER; + + pixelFormat = ChoosePixelFormat(m_deviceContext, &descriptor); + if (pixelFormat == 0) + { + NazaraError("Failed to choose pixel format"); + Destroy(); + + return false; + } + } + + if (!SetPixelFormat(m_deviceContext, pixelFormat, &descriptor)) + { + NazaraError("Failed to set pixel format"); + Destroy(); + return false; + } + + // Arrivé ici, le format de pixel est choisi, nous récupérons donc les paramètres réels du futur contexte + if (DescribePixelFormat(m_deviceContext, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &descriptor) != 0) + { + parameters.bitsPerPixel = descriptor.cColorBits + descriptor.cAlphaBits; + parameters.depthBits = descriptor.cDepthBits; + parameters.stencilBits = descriptor.cDepthBits; + } + else + NazaraWarning("Failed to get context's parameters"); + + HGLRC shareContext = (parameters.shared) ? static_cast(parameters.shareContext->m_impl)->m_context : nullptr; + + m_context = nullptr; + if (wglCreateContextAttribs) + { + int attributes[4*2+1]; + int* attrib = attributes; + + *attrib++ = WGL_CONTEXT_MAJOR_VERSION_ARB; + *attrib++ = parameters.majorVersion; + + *attrib++ = WGL_CONTEXT_MINOR_VERSION_ARB; + *attrib++ = parameters.minorVersion; + + if (parameters.majorVersion >= 3) + { + *attrib++ = WGL_CONTEXT_PROFILE_MASK_ARB; + *attrib++ = (parameters.compatibilityProfile) ? WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB : WGL_CONTEXT_CORE_PROFILE_BIT_ARB; + } + + if (parameters.debugMode) + { + *attrib++ = WGL_CONTEXT_FLAGS_ARB; + *attrib++ = WGL_CONTEXT_DEBUG_BIT_ARB; + + // Les contextes forward-compatible ne sont plus utilisés pour cette raison : + // http://www.opengl.org/discussion_boards/showthread.php/175052-Forward-compatible-vs-Core-profile + } + + *attrib++ = 0; + + m_context = wglCreateContextAttribs(m_deviceContext, shareContext, attributes); + } + + if (!m_context) + { + m_context = wglCreateContext(m_deviceContext); + + if (shareContext) + { + // wglShareLists n'est pas thread-safe (source: SFML) + static NzMutex mutex; + NzLockGuard lock(mutex); + + if (!wglShareLists(shareContext, m_context)) + NazaraWarning("Failed to share the context: " + NzGetLastSystemError()); + } + } + + if (!m_context) + { + NazaraError("Failed to create context"); + Destroy(); + return false; + } + + return true; +} + +void NzContextImpl::Destroy() +{ + if (m_context) + { + if (wglGetCurrentContext() == m_context) + wglMakeCurrent(nullptr, nullptr); + + wglDeleteContext(m_context); + m_context = nullptr; + } + + if (m_deviceContext) + { + ReleaseDC(m_window, m_deviceContext); + m_deviceContext = nullptr; + } + + if (m_ownsWindow) + { + DestroyWindow(m_window); + m_window = nullptr; + } +} + +void NzContextImpl::SwapBuffers() +{ + ::SwapBuffers(m_deviceContext); +} + +bool NzContextImpl::Desactivate() +{ + return wglMakeCurrent(nullptr, nullptr); +} + diff --git a/src/Nazara/Utility/Animation.cpp b/src/Nazara/Utility/Animation.cpp index 48bad22f2..fe9f1c0b2 100644 --- a/src/Nazara/Utility/Animation.cpp +++ b/src/Nazara/Utility/Animation.cpp @@ -1,317 +1,342 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Utility module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include - -struct NzAnimationImpl -{ - std::map sequenceMap; - std::vector sequences; - nzAnimationType type; - unsigned int frameCount; -}; - -bool NzAnimationParams::IsValid() const -{ - if (startFrame > endFrame) - { - NazaraError("Start frame must be lower than end frame"); - return false; - } - - return true; -} - -NzAnimation::~NzAnimation() -{ - Destroy(); -} - -unsigned int NzAnimation::AddSequence(const NzSequence& sequence) -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Animation not created"); - return 0; - } - #endif - - unsigned int index = m_impl->sequences.size(); - - if (!sequence.name.IsEmpty()) - { - #if NAZARA_UTILITY_SAFE - auto it = m_impl->sequenceMap.find(sequence.name); - if (it != m_impl->sequenceMap.end()) - { - NazaraError("Sequence name \"" + sequence.name + "\" is already used"); - return 0; - } - #endif - - m_impl->sequenceMap[sequence.name] = index; - } - - m_impl->sequences.push_back(sequence); - - return index; -} - -bool NzAnimation::Create(nzAnimationType type, unsigned int frameCount) -{ - Destroy(); - - #if NAZARA_UTILITY_SAFE - if (type == nzAnimationType_Static) - { - NazaraError("Invalid type"); - return false; - } - - if (frameCount == 0) - { - NazaraError("Frame count must be over zero"); - return false; - } - #endif - - m_impl = new NzAnimationImpl; - m_impl->frameCount = frameCount; - m_impl->type = type; - - return true; -} - -void NzAnimation::Destroy() -{ - if (m_impl) - { - delete m_impl; - m_impl = nullptr; - } -} - -unsigned int NzAnimation::GetFrameCount() const -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Animation not created"); - return false; - } - #endif - - return m_impl->frameCount; -} - -NzSequence* NzAnimation::GetSequence(const NzString& sequenceName) -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Animation not created"); - return nullptr; - } - - auto it = m_impl->sequenceMap.find(sequenceName); - if (it == m_impl->sequenceMap.end()) - { - NazaraError("Sequence not found"); - return nullptr; - } - - return &m_impl->sequences[it->second]; - #else - return &m_impl->sequences[m_impl->sequenceMap[sequenceName]]; - #endif -} - -NzSequence* NzAnimation::GetSequence(unsigned int index) -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Animation not created"); - return nullptr; - } - - if (index >= m_impl->sequences.size()) - { - NazaraError("Sequence index out of range (" + NzString::Number(index) + " >= " + NzString::Number(m_impl->sequences.size()) + ')'); - return nullptr; - } - #endif - - return &m_impl->sequences[index]; -} - -const NzSequence* NzAnimation::GetSequence(const NzString& sequenceName) const -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Animation not created"); - return nullptr; - } - - auto it = m_impl->sequenceMap.find(sequenceName); - if (it == m_impl->sequenceMap.end()) - { - NazaraError("Sequence not found"); - return nullptr; - } - - return &m_impl->sequences[it->second]; - #else - return &m_impl->sequences[m_impl->sequenceMap[sequenceName]]; - #endif -} - -const NzSequence* NzAnimation::GetSequence(unsigned int index) const -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Animation not created"); - return nullptr; - } - - if (index >= m_impl->sequences.size()) - { - NazaraError("Sequence index out of range (" + NzString::Number(index) + " >= " + NzString::Number(m_impl->sequences.size()) + ')'); - return nullptr; - } - #endif - - return &m_impl->sequences[index]; -} - -unsigned int NzAnimation::GetSequenceCount() const -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Animation not created"); - return 0; - } - #endif - - return m_impl->sequences.size(); -} - -nzAnimationType NzAnimation::GetType() const -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Animation not created"); - return nzAnimationType_Static; // Ce qui est une valeur invalide pour NzAnimation - } - #endif - - return m_impl->type; -} - -bool NzAnimation::HasSequence(const NzString& sequenceName) const -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Animation not created"); - return false; - } - #endif - - return m_impl->sequenceMap.find(sequenceName) != m_impl->sequenceMap.end(); -} - -bool NzAnimation::HasSequence(unsigned int index) const -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Animation not created"); - return false; - } - #endif - - return index >= m_impl->sequences.size(); -} - -bool NzAnimation::IsValid() const -{ - return m_impl != nullptr; -} - -bool NzAnimation::LoadFromFile(const NzString& filePath, const NzAnimationParams& params) -{ - return NzAnimationLoader::LoadFromFile(this, filePath, params); -} - -bool NzAnimation::LoadFromMemory(const void* data, std::size_t size, const NzAnimationParams& params) -{ - return NzAnimationLoader::LoadFromMemory(this, data, size, params); -} - -bool NzAnimation::LoadFromStream(NzInputStream& stream, const NzAnimationParams& params) -{ - return NzAnimationLoader::LoadFromStream(this, stream, params); -} - -void NzAnimation::RemoveSequence(const NzString& identifier) -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Animation not created"); - return; - } - - auto it = m_impl->sequenceMap.find(identifier); - if (it == m_impl->sequenceMap.end()) - { - NazaraError("SubMesh not found"); - return; - } - - unsigned int index = it->second; - #else - unsigned int index = m_impl->sequenceMap[identifier]; - #endif - - auto it2 = m_impl->sequences.begin(); - std::advance(it2, index); - - m_impl->sequences.erase(it2); -} - -void NzAnimation::RemoveSequence(unsigned int index) -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Animation not created"); - return; - } - - if (index >= m_impl->sequences.size()) - { - NazaraError("Sequence index out of range (" + NzString::Number(index) + " >= " + NzString::Number(m_impl->sequences.size()) + ')'); - return; - } - #endif - - auto it = m_impl->sequences.begin(); - std::advance(it, index); - - m_impl->sequences.erase(it); -} - -NzAnimationLoader::LoaderList NzAnimation::s_loaders; +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include + +struct NzAnimationImpl +{ + std::map sequenceMap; + std::vector sequences; + nzAnimationType type; + unsigned int frameCount; +}; + +bool NzAnimationParams::IsValid() const +{ + if (startFrame > endFrame) + { + NazaraError("Start frame must be lower than end frame"); + return false; + } + + return true; +} + +NzAnimation::~NzAnimation() +{ + Destroy(); +} + +bool NzAnimation::AddSequence(const NzSequence& sequence) +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Animation not created"); + return false; + } + #endif + + unsigned int index = m_impl->sequences.size(); + + if (!sequence.name.IsEmpty()) + { + #if NAZARA_UTILITY_SAFE + auto it = m_impl->sequenceMap.find(sequence.name); + if (it != m_impl->sequenceMap.end()) + { + NazaraError("Sequence name \"" + sequence.name + "\" is already used"); + return false; + } + #endif + + m_impl->sequenceMap[sequence.name] = index; + } + + m_impl->sequences.push_back(sequence); + + return true; +} + +bool NzAnimation::Create(nzAnimationType type, unsigned int frameCount) +{ + Destroy(); + + #if NAZARA_UTILITY_SAFE + if (type == nzAnimationType_Static) + { + NazaraError("Invalid type"); + return false; + } + + if (frameCount == 0) + { + NazaraError("Frame count must be over zero"); + return false; + } + #endif + + m_impl = new NzAnimationImpl; + m_impl->frameCount = frameCount; + m_impl->type = type; + + NotifyCreated(); + return true; +} + +void NzAnimation::Destroy() +{ + if (m_impl) + { + NotifyDestroy(); + + delete m_impl; + m_impl = nullptr; + } +} + +unsigned int NzAnimation::GetFrameCount() const +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Animation not created"); + return false; + } + #endif + + return m_impl->frameCount; +} + +NzSequence* NzAnimation::GetSequence(const NzString& sequenceName) +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Animation not created"); + return nullptr; + } + + auto it = m_impl->sequenceMap.find(sequenceName); + if (it == m_impl->sequenceMap.end()) + { + NazaraError("Sequence not found"); + return nullptr; + } + + return &m_impl->sequences[it->second]; + #else + return &m_impl->sequences[m_impl->sequenceMap[sequenceName]]; + #endif +} + +NzSequence* NzAnimation::GetSequence(unsigned int index) +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Animation not created"); + return nullptr; + } + + if (index >= m_impl->sequences.size()) + { + NazaraError("Sequence index out of range (" + NzString::Number(index) + " >= " + NzString::Number(m_impl->sequences.size()) + ')'); + return nullptr; + } + #endif + + return &m_impl->sequences[index]; +} + +const NzSequence* NzAnimation::GetSequence(const NzString& sequenceName) const +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Animation not created"); + return nullptr; + } + + auto it = m_impl->sequenceMap.find(sequenceName); + if (it == m_impl->sequenceMap.end()) + { + NazaraError("Sequence not found"); + return nullptr; + } + + return &m_impl->sequences[it->second]; + #else + return &m_impl->sequences[m_impl->sequenceMap[sequenceName]]; + #endif +} + +const NzSequence* NzAnimation::GetSequence(unsigned int index) const +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Animation not created"); + return nullptr; + } + + if (index >= m_impl->sequences.size()) + { + NazaraError("Sequence index out of range (" + NzString::Number(index) + " >= " + NzString::Number(m_impl->sequences.size()) + ')'); + return nullptr; + } + #endif + + return &m_impl->sequences[index]; +} + +unsigned int NzAnimation::GetSequenceCount() const +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Animation not created"); + return 0; + } + #endif + + return m_impl->sequences.size(); +} + +int NzAnimation::GetSequenceIndex(const NzString& sequenceName) const +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Animation not created"); + return -1; + } + + auto it = m_impl->sequenceMap.find(sequenceName); + if (it == m_impl->sequenceMap.end()) + { + NazaraError("Sequence not found"); + return -1; + } + + return it->second; + #else + return m_impl->sequenceMap[sequenceName]; + #endif +} + +nzAnimationType NzAnimation::GetType() const +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Animation not created"); + return nzAnimationType_Static; // Ce qui est une valeur invalide pour NzAnimation + } + #endif + + return m_impl->type; +} + +bool NzAnimation::HasSequence(const NzString& sequenceName) const +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Animation not created"); + return false; + } + #endif + + return m_impl->sequenceMap.find(sequenceName) != m_impl->sequenceMap.end(); +} + +bool NzAnimation::HasSequence(unsigned int index) const +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Animation not created"); + return false; + } + #endif + + return index >= m_impl->sequences.size(); +} + +bool NzAnimation::IsValid() const +{ + return m_impl != nullptr; +} + +bool NzAnimation::LoadFromFile(const NzString& filePath, const NzAnimationParams& params) +{ + return NzAnimationLoader::LoadFromFile(this, filePath, params); +} + +bool NzAnimation::LoadFromMemory(const void* data, std::size_t size, const NzAnimationParams& params) +{ + return NzAnimationLoader::LoadFromMemory(this, data, size, params); +} + +bool NzAnimation::LoadFromStream(NzInputStream& stream, const NzAnimationParams& params) +{ + return NzAnimationLoader::LoadFromStream(this, stream, params); +} + +void NzAnimation::RemoveSequence(const NzString& identifier) +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Animation not created"); + return; + } + + auto it = m_impl->sequenceMap.find(identifier); + if (it == m_impl->sequenceMap.end()) + { + NazaraError("Sequence not found"); + return; + } + + int index = it->second; + #else + int index = m_impl->sequenceMap[identifier]; + #endif + + auto it2 = m_impl->sequences.begin(); + std::advance(it2, index); + + m_impl->sequences.erase(it2); +} + +void NzAnimation::RemoveSequence(unsigned int index) +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Animation not created"); + return; + } + + if (index >= m_impl->sequences.size()) + { + NazaraError("Sequence index out of range (" + NzString::Number(index) + " >= " + NzString::Number(m_impl->sequences.size()) + ')'); + return; + } + #endif + + auto it = m_impl->sequences.begin(); + std::advance(it, index); + + m_impl->sequences.erase(it); +} + +NzAnimationLoader::LoaderList NzAnimation::s_loaders; diff --git a/src/Nazara/Utility/Buffer.cpp b/src/Nazara/Utility/Buffer.cpp index cebc3e28a..4969b3129 100644 --- a/src/Nazara/Utility/Buffer.cpp +++ b/src/Nazara/Utility/Buffer.cpp @@ -1,337 +1,339 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Utility module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace -{ - NzBufferImpl* SoftwareBufferFunction(NzBuffer* parent, nzBufferType type) - { - return new NzSoftwareBuffer(parent, type); - } -} - -NzBuffer::NzBuffer(nzBufferType type) : -m_type(type), -m_typeSize(0), -m_impl(nullptr), -m_length(0) -{ -} - -NzBuffer::NzBuffer(nzBufferType type, unsigned int length, nzUInt8 typeSize, nzBufferStorage storage, nzBufferUsage usage) : -m_type(type), -m_impl(nullptr) -{ - Create(length, typeSize, storage, usage); - - #ifdef NAZARA_DEBUG - if (!m_impl) - { - NazaraError("Failed to create buffer"); - throw std::runtime_error("Constructor failed"); - } - #endif -} - -NzBuffer::~NzBuffer() -{ - Destroy(); -} - -bool NzBuffer::CopyContent(NzBuffer& buffer) -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Buffer must be valid"); - return false; - } - - if (!buffer.IsValid()) - { - NazaraError("Source buffer must be valid"); - return false; - } - - if (!buffer.GetTypeSize() != m_typeSize) - { - NazaraError("Source buffer type size does not match buffer type size"); - return false; - } - #endif - - void* ptr = buffer.Map(nzBufferAccess_ReadOnly); - if (!ptr) - { - NazaraError("Failed to map source buffer"); - return false; - } - - bool r = Fill(ptr, 0, buffer.GetLength()); - - if (!buffer.Unmap()) - NazaraWarning("Failed to unmap source buffer"); - - return r; -} - -bool NzBuffer::Create(unsigned int length, nzUInt8 typeSize, nzBufferStorage storage, nzBufferUsage usage) -{ - Destroy(); - - // On tente d'abord de faire un buffer hardware, si supporté - if (s_bufferFunctions[storage]) - { - NzBufferImpl* impl = s_bufferFunctions[storage](this, m_type); - if (!impl->Create(length*typeSize, usage)) - { - NazaraError("Failed to create buffer"); - delete impl; - - return false; - } - - m_impl = impl; - } - else - { - NazaraError("Buffer storage not supported"); - return false; - } - - m_length = length; - m_typeSize = typeSize; - m_storage = storage; - m_usage = usage; - - // Si on arrive ici c'est que tout s'est bien passé. - return true; -} - -void NzBuffer::Destroy() -{ - if (m_impl) - { - m_impl->Destroy(); - delete m_impl; - m_impl = nullptr; - } -} - -bool NzBuffer::Fill(const void* data, unsigned int offset, unsigned int length) -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Buffer not valid"); - return false; - } - - if (offset+length > m_length) - { - NazaraError("Exceeding buffer size"); - return false; - } - #endif - - return m_impl->Fill(data, offset*m_typeSize, ((length == 0) ? m_length-offset : length)*m_typeSize); -} - -NzBufferImpl* NzBuffer::GetImpl() const -{ - return m_impl; -} - -unsigned int NzBuffer::GetLength() const -{ - return m_length; -} - -void* NzBuffer::GetPointer() -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Buffer not valid"); - return nullptr; - } - #endif - - return m_impl->GetPointer(); -} - -const void* NzBuffer::GetPointer() const -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Buffer not valid"); - return nullptr; - } - #endif - - return m_impl->GetPointer(); -} - -unsigned int NzBuffer::GetSize() const -{ - return m_length*m_typeSize; -} - -nzBufferStorage NzBuffer::GetStorage() const -{ - return m_storage; -} - -nzBufferType NzBuffer::GetType() const -{ - return m_type; -} - -nzUInt8 NzBuffer::GetTypeSize() const -{ - return m_typeSize; -} - -nzBufferUsage NzBuffer::GetUsage() const -{ - return m_usage; -} - -bool NzBuffer::IsHardware() const -{ - return m_storage == nzBufferStorage_Hardware; -} - -bool NzBuffer::IsValid() const -{ - return m_impl != nullptr; -} - -void* NzBuffer::Map(nzBufferAccess access, unsigned int offset, unsigned int length) -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Buffer not valid"); - return nullptr; - } - - if (offset+length > m_length) - { - NazaraError("Exceeding buffer size"); - return nullptr; - } - #endif - - return m_impl->Map(access, offset*m_typeSize, ((length == 0) ? m_length-offset : length)*m_typeSize); -} - -bool NzBuffer::SetStorage(nzBufferStorage storage) -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Buffer not valid"); - return false; - } - #endif - - if (m_storage == storage) - return true; - - #if NAZARA_UTILITY_SAFE - if (!IsSupported(storage)) - { - NazaraError("Storage not supported"); - return false; - } - #endif - - void* ptr = m_impl->Map(nzBufferAccess_ReadOnly, 0, m_length*m_typeSize); - if (!ptr) - { - NazaraError("Failed to map buffer"); - return false; - } - - NzBufferImpl* impl = s_bufferFunctions[storage](this, m_type); - if (!impl->Create(m_length*m_typeSize, m_usage)) - { - NazaraError("Failed to create buffer"); - delete impl; - - if (!m_impl->Unmap()) - NazaraWarning("Failed to unmap buffer"); - - return false; - } - - if (!impl->Fill(ptr, 0, m_length*m_typeSize)) - { - NazaraError("Failed to fill buffer"); - impl->Destroy(); - delete impl; - - if (!m_impl->Unmap()) - NazaraWarning("Failed to unmap buffer"); - - return false; - } - - m_impl->Unmap(); - m_impl->Destroy(); - - delete m_impl; - m_impl = impl; - - m_storage = storage; - - return true; -} - -bool NzBuffer::Unmap() -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Buffer not valid"); - return false; - } - #endif - - return m_impl->Unmap(); -} - -bool NzBuffer::IsSupported(nzBufferStorage storage) -{ - return s_bufferFunctions[storage] != nullptr; -} - -void NzBuffer::SetBufferFunction(nzBufferStorage storage, BufferFunction func) -{ - s_bufferFunctions[storage] = func; -} - -bool NzBuffer::Initialize() -{ - s_bufferFunctions[nzBufferStorage_Software] = SoftwareBufferFunction; - - return true; -} - -void NzBuffer::Uninitialize() -{ - std::memset(s_bufferFunctions, 0, (nzBufferStorage_Max+1)*sizeof(NzBuffer::BufferFunction)); -} - -NzBuffer::BufferFunction NzBuffer::s_bufferFunctions[nzBufferStorage_Max+1] = {0}; +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + NzBufferImpl* SoftwareBufferFunction(NzBuffer* parent, nzBufferType type) + { + return new NzSoftwareBuffer(parent, type); + } +} + +NzBuffer::NzBuffer(nzBufferType type) : +m_type(type), +m_typeSize(0), +m_impl(nullptr), +m_length(0) +{ +} + +NzBuffer::NzBuffer(nzBufferType type, unsigned int length, nzUInt8 typeSize, nzBufferStorage storage, nzBufferUsage usage) : +m_type(type), +m_impl(nullptr) +{ + Create(length, typeSize, storage, usage); + + #ifdef NAZARA_DEBUG + if (!m_impl) + { + NazaraError("Failed to create buffer"); + throw std::runtime_error("Constructor failed"); + } + #endif +} + +NzBuffer::~NzBuffer() +{ + Destroy(); +} + +bool NzBuffer::CopyContent(NzBuffer& buffer) +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Buffer must be valid"); + return false; + } + + if (!buffer.IsValid()) + { + NazaraError("Source buffer must be valid"); + return false; + } + + if (!buffer.GetTypeSize() != m_typeSize) + { + NazaraError("Source buffer type size does not match buffer type size"); + return false; + } + #endif + + void* ptr = buffer.Map(nzBufferAccess_ReadOnly); + if (!ptr) + { + NazaraError("Failed to map source buffer"); + return false; + } + + bool r = Fill(ptr, 0, buffer.GetLength()); + + if (!buffer.Unmap()) + NazaraWarning("Failed to unmap source buffer"); + + return r; +} + +bool NzBuffer::Create(unsigned int length, nzUInt8 typeSize, nzBufferStorage storage, nzBufferUsage usage) +{ + Destroy(); + + // On tente d'abord de faire un buffer hardware, si supporté + if (s_bufferFunctions[storage]) + { + NzBufferImpl* impl = s_bufferFunctions[storage](this, m_type); + if (!impl->Create(length*typeSize, usage)) + { + NazaraError("Failed to create buffer"); + delete impl; + + return false; + } + + m_impl = impl; + } + else + { + NazaraError("Buffer storage not supported"); + return false; + } + + m_length = length; + m_typeSize = typeSize; + m_storage = storage; + m_usage = usage; + + NotifyCreated(); + return true; // Si on arrive ici c'est que tout s'est bien passé. +} + +void NzBuffer::Destroy() +{ + if (m_impl) + { + NotifyDestroy(); + + m_impl->Destroy(); + delete m_impl; + m_impl = nullptr; + } +} + +bool NzBuffer::Fill(const void* data, unsigned int offset, unsigned int length) +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Buffer not valid"); + return false; + } + + if (offset+length > m_length) + { + NazaraError("Exceeding buffer size"); + return false; + } + #endif + + return m_impl->Fill(data, offset*m_typeSize, ((length == 0) ? m_length-offset : length)*m_typeSize); +} + +NzBufferImpl* NzBuffer::GetImpl() const +{ + return m_impl; +} + +unsigned int NzBuffer::GetLength() const +{ + return m_length; +} + +void* NzBuffer::GetPointer() +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Buffer not valid"); + return nullptr; + } + #endif + + return m_impl->GetPointer(); +} + +const void* NzBuffer::GetPointer() const +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Buffer not valid"); + return nullptr; + } + #endif + + return m_impl->GetPointer(); +} + +unsigned int NzBuffer::GetSize() const +{ + return m_length*m_typeSize; +} + +nzBufferStorage NzBuffer::GetStorage() const +{ + return m_storage; +} + +nzBufferType NzBuffer::GetType() const +{ + return m_type; +} + +nzUInt8 NzBuffer::GetTypeSize() const +{ + return m_typeSize; +} + +nzBufferUsage NzBuffer::GetUsage() const +{ + return m_usage; +} + +bool NzBuffer::IsHardware() const +{ + return m_storage == nzBufferStorage_Hardware; +} + +bool NzBuffer::IsValid() const +{ + return m_impl != nullptr; +} + +void* NzBuffer::Map(nzBufferAccess access, unsigned int offset, unsigned int length) +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Buffer not valid"); + return nullptr; + } + + if (offset+length > m_length) + { + NazaraError("Exceeding buffer size"); + return nullptr; + } + #endif + + return m_impl->Map(access, offset*m_typeSize, ((length == 0) ? m_length-offset : length)*m_typeSize); +} + +bool NzBuffer::SetStorage(nzBufferStorage storage) +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Buffer not valid"); + return false; + } + #endif + + if (m_storage == storage) + return true; + + #if NAZARA_UTILITY_SAFE + if (!IsSupported(storage)) + { + NazaraError("Storage not supported"); + return false; + } + #endif + + void* ptr = m_impl->Map(nzBufferAccess_ReadOnly, 0, m_length*m_typeSize); + if (!ptr) + { + NazaraError("Failed to map buffer"); + return false; + } + + NzBufferImpl* impl = s_bufferFunctions[storage](this, m_type); + if (!impl->Create(m_length*m_typeSize, m_usage)) + { + NazaraError("Failed to create buffer"); + delete impl; + + if (!m_impl->Unmap()) + NazaraWarning("Failed to unmap buffer"); + + return false; + } + + if (!impl->Fill(ptr, 0, m_length*m_typeSize)) + { + NazaraError("Failed to fill buffer"); + impl->Destroy(); + delete impl; + + if (!m_impl->Unmap()) + NazaraWarning("Failed to unmap buffer"); + + return false; + } + + m_impl->Unmap(); + m_impl->Destroy(); + + delete m_impl; + m_impl = impl; + + m_storage = storage; + + return true; +} + +bool NzBuffer::Unmap() +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Buffer not valid"); + return false; + } + #endif + + return m_impl->Unmap(); +} + +bool NzBuffer::IsSupported(nzBufferStorage storage) +{ + return s_bufferFunctions[storage] != nullptr; +} + +void NzBuffer::SetBufferFunction(nzBufferStorage storage, BufferFunction func) +{ + s_bufferFunctions[storage] = func; +} + +bool NzBuffer::Initialize() +{ + s_bufferFunctions[nzBufferStorage_Software] = SoftwareBufferFunction; + + return true; +} + +void NzBuffer::Uninitialize() +{ + std::memset(s_bufferFunctions, 0, (nzBufferStorage_Max+1)*sizeof(NzBuffer::BufferFunction)); +} + +NzBuffer::BufferFunction NzBuffer::s_bufferFunctions[nzBufferStorage_Max+1] = {0}; diff --git a/src/Nazara/Utility/Image.cpp b/src/Nazara/Utility/Image.cpp index ebcfa716a..75b66ca2a 100644 --- a/src/Nazara/Utility/Image.cpp +++ b/src/Nazara/Utility/Image.cpp @@ -1,1170 +1,1187 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Utility module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include - -namespace -{ - inline unsigned int GetLevelSize(unsigned int size, nzUInt8 level) - { - return std::max(size >> level, 1U); - } - - inline nzUInt8* GetPixelPtr(nzUInt8* base, nzUInt8 bpp, unsigned int x, unsigned int y, unsigned int z, unsigned int width, unsigned int height) - { - return &base[(width*(height*z + y) + x)*bpp]; - } -} - -bool NzImageParams::IsValid() const -{ - return true; -} - -NzImage::NzImage() : -m_sharedImage(&emptyImage) -{ -} - -NzImage::NzImage(nzImageType type, nzPixelFormat format, unsigned int width, unsigned int height, unsigned int depth, nzUInt8 levelCount) : -m_sharedImage(&emptyImage) -{ - Create(type, format, width, height, depth, levelCount); - - #ifdef NAZARA_DEBUG - if (!m_sharedImage) - { - NazaraError("Failed to create image"); - throw std::runtime_error("Constructor failed"); - } - #endif -} - -NzImage::NzImage(const NzImage& image) : -NzResource(image), -m_sharedImage(image.m_sharedImage) -{ - if (m_sharedImage != &emptyImage) - { - NazaraMutexLock(m_sharedImage->mutex); - m_sharedImage->refCount++; - NazaraMutexUnlock(m_sharedImage->mutex); - } -} - -NzImage::NzImage(NzImage&& image) noexcept : -m_sharedImage(image.m_sharedImage) -{ - image.m_sharedImage = &emptyImage; -} - -NzImage::~NzImage() -{ - ReleaseImage(); -} - -bool NzImage::Convert(nzPixelFormat format) -{ - #if NAZARA_UTILITY_SAFE - if (!m_sharedImage) - { - NazaraError("Image must be valid"); - return false; - } - - if (!NzPixelFormat::IsValid(format)) - { - NazaraError("Invalid pixel format"); - return false; - } - - if (!NzPixelFormat::IsConversionSupported(m_sharedImage->format, format)) - { - NazaraError("Conversion from " + NzPixelFormat::ToString(m_sharedImage->format) + " to " + NzPixelFormat::ToString(format) + " is not supported"); - return false; - } - #endif - - if (format == m_sharedImage->format) - return true; - - nzUInt8** levels = new nzUInt8*[m_sharedImage->levelCount]; - - unsigned int width = m_sharedImage->width; - unsigned int height = m_sharedImage->height; - - // Les images 3D et cubemaps sont stockés de la même façon - unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : m_sharedImage->depth; - - for (unsigned int i = 0; i < m_sharedImage->levelCount; ++i) - { - unsigned int pixelsPerFace = width*height; - nzUInt8* ptr = new nzUInt8[pixelsPerFace*depth*NzPixelFormat::GetBPP(format)]; - nzUInt8* pixels = m_sharedImage->pixels[i]; - unsigned int srcStride = pixelsPerFace * NzPixelFormat::GetBPP(m_sharedImage->format); - unsigned int dstStride = pixelsPerFace * NzPixelFormat::GetBPP(format); - - for (unsigned int d = 0; d < depth; ++d) - { - if (!NzPixelFormat::Convert(m_sharedImage->format, format, pixels, &pixels[srcStride], ptr)) - { - NazaraError("Failed to convert image"); - - // Nettoyage de la mémoire - delete[] ptr; // Permet une optimisation de boucle (GCC) - for (unsigned int j = 0; j < i; ++j) - delete[] levels[j]; - - delete[] levels; - - return false; - } - - pixels += srcStride; - ptr += dstStride; - } - - levels[i] = ptr; - - if (width > 1) - width >>= 1; - - if (height > 1) - height >>= 1; - - if (depth > 1 && m_sharedImage->type != nzImageType_Cubemap) - depth >>= 1; - } - - SharedImage* newImage = new SharedImage(1, m_sharedImage->type, format, m_sharedImage->levelCount, levels, m_sharedImage->width, m_sharedImage->height, m_sharedImage->depth); - - ReleaseImage(); - m_sharedImage = newImage; - - return true; -} - -bool NzImage::Copy(const NzImage& source, const NzCubeui& srcCube, const NzVector3ui& dstPos) -{ - #if NAZARA_UTILITY_SAFE - if (!source.IsValid()) - { - NazaraError("Source image must be valid"); - return false; - } - - if (source.GetFormat() != m_sharedImage->format) - { - NazaraError("Source image format does not match destination image format"); - return false; - } - #endif - - const nzUInt8* pixels = source.GetConstPixels(0, srcCube.x, srcCube.y, srcCube.z); - if (!pixels) - { - NazaraError("Failed to access pixels"); - return false; - } - - /* - Correctif temporaire : Update veut de la mémoire contigüe - Il est donc nécessaire de prendre la partie de la texture que nous voulons mettre à jour - - ///FIXME: Trouver une interface pour gérer ce genre de problème (Façon OpenGL?) - (Appliquer l'interface à NzTexture également) - */ - nzUInt8 bpp = NzPixelFormat::GetBPP(m_sharedImage->format); - unsigned int dstLineStride = srcCube.width*bpp; - unsigned int dstFaceStride = dstLineStride*srcCube.height; - unsigned int srcLineStride = m_sharedImage->width*bpp; - unsigned int srcFaceStride = srcLineStride*m_sharedImage->height; - - nzUInt8* cube = new nzUInt8[dstFaceStride*srcCube.depth]; - nzUInt8* ptr = cube; - - for (unsigned int z = 0; z < srcCube.depth; ++z) - { - nzUInt8* facePixels = ptr; - for (unsigned int y = 0; y < srcCube.height; ++y) - { - std::memcpy(facePixels, pixels, dstLineStride); - - facePixels += dstLineStride; - pixels += srcLineStride; - } - - ptr += dstFaceStride; - pixels += srcFaceStride; - } - - bool success = Update(cube, NzCubeui(dstPos.x, dstPos.y, dstPos.z, srcCube.width, srcCube.height, srcCube.depth)); - - delete[] cube; - - return success; -} - -bool NzImage::Create(nzImageType type, nzPixelFormat format, unsigned int width, unsigned int height, unsigned int depth, nzUInt8 levelCount) -{ - ReleaseImage(); - - if (width == 0 || height == 0 || depth == 0) - return true; - - #if NAZARA_UTILITY_SAFE - if (!NzPixelFormat::IsValid(format)) - { - NazaraError("Invalid pixel format"); - return false; - } - - switch (type) - { - case nzImageType_1D: - if (height > 1) - { - NazaraError("1D textures must be 1 height"); - return false; - } - - if (depth > 1) - { - NazaraError("1D textures must be 1 depth"); - return false; - } - break; - - case nzImageType_1D_Array: - case nzImageType_2D: - if (depth > 1) - { - NazaraError("2D textures must be 1 depth"); - return false; - } - break; - - case nzImageType_2D_Array: - case nzImageType_3D: - break; - - case nzImageType_Cubemap: - if (depth > 1) - { - NazaraError("Cubemaps must be 1 depth"); - return false; - } - - if (width != height) - { - NazaraError("Cubemaps must have square dimensions"); - return false; - } - break; - - default: - NazaraInternalError("Image type not handled"); - return false; - } - #endif - - levelCount = std::min(levelCount, GetMaxLevel(width, height, depth)); - - nzUInt8** levels = new nzUInt8*[levelCount]; - - unsigned int w = width; - unsigned int h = height; - unsigned int d = (type == nzImageType_Cubemap) ? 6 : depth; - - for (unsigned int i = 0; i < levelCount; ++i) - { - // Cette allocation est protégée car sa taille dépend directement de paramètres utilisateurs - try - { - levels[i] = new nzUInt8[w * h * d * NzPixelFormat::GetBPP(format)]; - - if (w > 1) - w >>= 1; - - if (h > 1) - h >>= 1; - - if (d > 1 && type != nzImageType_Cubemap) - d >>= 1; - } - catch (const std::exception& e) - { - NazaraError("Failed to allocate image's level " + NzString::Number(i) + " (" + NzString(e.what()) + ')'); - - // Nettoyage - delete[] levels[i]; // Permet une optimisation de boucle (GCC) - for (unsigned int j = 0; j < i; ++j) - delete[] levels[j]; - - delete[] levels; - - return false; - } - } - - m_sharedImage = new SharedImage(1, type, format, levelCount, levels, width, height, depth); - - return true; -} - -void NzImage::Destroy() -{ - ReleaseImage(); -} - -bool NzImage::Fill(const NzColor& color) -{ - #if NAZARA_UTILITY_SAFE - if (!m_sharedImage) - { - NazaraError("Image must be valid"); - return false; - } - - if (NzPixelFormat::IsCompressed(m_sharedImage->format)) - { - NazaraError("Cannot access pixels from compressed image"); - return false; - } - #endif - - EnsureOwnership(); - - nzUInt8 bpp = NzPixelFormat::GetBPP(m_sharedImage->format); - nzUInt8* pixels = new nzUInt8[bpp]; - if (!NzPixelFormat::Convert(nzPixelFormat_RGBA8, m_sharedImage->format, &color.r, pixels)) - { - NazaraError("Failed to convert RGBA8 to " + NzPixelFormat::ToString(m_sharedImage->format)); - delete[] pixels; - - return false; - } - - unsigned int width = m_sharedImage->width; - unsigned int height = m_sharedImage->height; - unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : m_sharedImage->depth; - - for (unsigned int level = 0; level < m_sharedImage->levelCount; ++level) - { - nzUInt8* ptr = &m_sharedImage->pixels[level][0]; - nzUInt8* end = &m_sharedImage->pixels[level][width*height*depth*bpp]; - - while (ptr < end) - { - std::memcpy(ptr, pixels, bpp); - ptr += bpp; - } - - if (width > 1U) - width >>= 1; - - if (height > 1U) - height >>= 1; - - if (depth > 1U && m_sharedImage->type != nzImageType_Cubemap) - depth >>= 1; - } - - delete[] pixels; - - return true; -} - -bool NzImage::Fill(const NzColor& color, const NzRectui& rect, unsigned int z) -{ - #if NAZARA_UTILITY_SAFE - if (!m_sharedImage) - { - NazaraError("Image must be valid"); - return false; - } - - if (!rect.IsValid()) - { - NazaraError("Invalid rectangle"); - return false; - } - - if (rect.x+rect.width > m_sharedImage->width || rect.y+rect.height > m_sharedImage->height) - { - NazaraError("Rectangle dimensions are out of bounds"); - return false; - } - - unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : m_sharedImage->depth; - if (z >= depth) - { - NazaraError("Z value exceeds depth (" + NzString::Number(z) + " >= (" + NzString::Number(depth) + ')'); - return false; - } - #endif - - EnsureOwnership(); - - nzUInt8 bpp = NzPixelFormat::GetBPP(m_sharedImage->format); - nzUInt8* pixels = new nzUInt8[bpp]; - if (!NzPixelFormat::Convert(nzPixelFormat_RGBA8, m_sharedImage->format, &color.r, pixels)) - { - NazaraError("Failed to convert RGBA8 to " + NzPixelFormat::ToString(m_sharedImage->format)); - delete[] pixels; - - return false; - } - - ///FIXME: L'algorithme a du mal avec un bpp non multiple de 2 - nzUInt8* dstPixels = GetPixelPtr(m_sharedImage->pixels[0], bpp, rect.x, rect.y, z, m_sharedImage->width, m_sharedImage->height); - unsigned int srcStride = rect.width * bpp; - unsigned int dstStride = m_sharedImage->width * bpp; - for (unsigned int y = 0; y < rect.height; ++y) - { - nzUInt8* start = dstPixels; - nzUInt8* end = dstPixels + srcStride; - while (start < end) - { - std::memcpy(start, pixels, bpp); - start += bpp; - } - - dstPixels += dstStride; - } - - delete[] pixels; - - return true; -} - -bool NzImage::Fill(const NzColor& color, const NzCubeui& cube) -{ - #if NAZARA_UTILITY_SAFE - if (!m_sharedImage) - { - NazaraError("Image must be valid"); - return false; - } - - if (!cube.IsValid()) - { - NazaraError("Invalid rectangle"); - return false; - } - - if (cube.x+cube.width > m_sharedImage->width || cube.y+cube.height > m_sharedImage->height || cube.z+cube.depth > m_sharedImage->depth) - { - NazaraError("Cube dimensions are out of bounds"); - return false; - } - #endif - - EnsureOwnership(); - - nzUInt8 bpp = NzPixelFormat::GetBPP(m_sharedImage->format); - nzUInt8* pixels = new nzUInt8[bpp]; - if (!NzPixelFormat::Convert(nzPixelFormat_RGBA8, m_sharedImage->format, &color.r, pixels)) - { - NazaraError("Failed to convert RGBA8 to " + NzPixelFormat::ToString(m_sharedImage->format)); - delete[] pixels; - - return false; - } - - ///FIXME: L'algorithme a du mal avec un bpp non multiple de 2 - nzUInt8* dstPixels = GetPixelPtr(m_sharedImage->pixels[0], bpp, cube.x, cube.y, cube.z, m_sharedImage->width, m_sharedImage->height); - unsigned int srcStride = cube.width * bpp; - unsigned int dstStride = m_sharedImage->width * bpp; - unsigned int faceSize = dstStride * m_sharedImage->height; - for (unsigned int z = 0; z < cube.depth; ++z) - { - nzUInt8* facePixels = dstPixels; - for (unsigned int y = 0; y < cube.height; ++y) - { - nzUInt8* start = facePixels; - nzUInt8* end = facePixels + srcStride; - while (start < end) - { - std::memcpy(start, pixels, bpp); - start += bpp; - } - - facePixels += dstStride; - } - - dstPixels += faceSize; - } - - delete[] pixels; - - return true; -} - -bool NzImage::FlipHorizontally() -{ - #if NAZARA_UTILITY_SAFE - if (!m_sharedImage) - { - NazaraError("Image must be valid"); - return false; - } - - if (NzPixelFormat::IsCompressed(m_sharedImage->format)) - { - NazaraError("Cannot flip compressed image"); - return false; - } - #endif - - EnsureOwnership(); - - unsigned int width = m_sharedImage->width; - unsigned int height = m_sharedImage->height; - unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : m_sharedImage->depth; - for (unsigned int level = 0; level < m_sharedImage->levelCount; ++level) - { - NzPixelFormat::Flip(nzPixelFlipping_Horizontally, m_sharedImage->format, width, height, depth, m_sharedImage->pixels[level], m_sharedImage->pixels[level]); - - if (width > 1U) - width >>= 1; - - if (height > 1U) - height >>= 1; - - if (depth > 1U && m_sharedImage->type != nzImageType_Cubemap) - depth >>= 1; - } - - return true; -} - - -bool NzImage::FlipVertically() -{ - #if NAZARA_UTILITY_SAFE - if (!m_sharedImage) - { - NazaraError("Image must be valid"); - return false; - } - - if (NzPixelFormat::IsCompressed(m_sharedImage->format)) - { - NazaraError("Cannot flip compressed image"); - return false; - } - #endif - - EnsureOwnership(); - - unsigned int width = m_sharedImage->width; - unsigned int height = m_sharedImage->height; - unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : m_sharedImage->depth; - for (unsigned int level = 0; level < m_sharedImage->levelCount; ++level) - { - NzPixelFormat::Flip(nzPixelFlipping_Vertically, m_sharedImage->format, width, height, depth, m_sharedImage->pixels[level], m_sharedImage->pixels[level]); - - if (width > 1U) - width >>= 1; - - if (height > 1U) - height >>= 1; - - if (depth > 1U && m_sharedImage->type != nzImageType_Cubemap) - depth >>= 1; - } - - return true; -} - -nzUInt8 NzImage::GetBPP() const -{ - return NzPixelFormat::GetBPP(m_sharedImage->format); -} - -const nzUInt8* NzImage::GetConstPixels(unsigned int x, unsigned int y, unsigned int z, nzUInt8 level) const -{ - #if NAZARA_UTILITY_SAFE - if (!m_sharedImage) - { - NazaraError("Image must be valid"); - return nullptr; - } - - if (x >= m_sharedImage->width) - { - NazaraError("X value exceeds width (" + NzString::Number(x) + " >= (" + NzString::Number(m_sharedImage->width) + ')'); - return nullptr; - } - - if (y >= m_sharedImage->height) - { - NazaraError("Y value exceeds width (" + NzString::Number(y) + " >= (" + NzString::Number(m_sharedImage->height) + ')'); - return nullptr; - } - - unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : m_sharedImage->depth; - if (z >= depth) - { - NazaraError("Z value exceeds depth (" + NzString::Number(z) + " >= (" + NzString::Number(depth) + ')'); - return nullptr; - } - - if (level >= m_sharedImage->levelCount) - { - NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_sharedImage->levelCount) + ')'); - return nullptr; - } - #endif - - return GetPixelPtr(m_sharedImage->pixels[level], NzPixelFormat::GetBPP(m_sharedImage->format), x, y, z, m_sharedImage->width, m_sharedImage->height); -} - -unsigned int NzImage::GetDepth(nzUInt8 level) const -{ - #if NAZARA_UTILITY_SAFE - if (level >= m_sharedImage->levelCount) - { - NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_sharedImage->levelCount) + ')'); - return 0; - } - #endif - - return GetLevelSize(m_sharedImage->depth, level); -} - -nzPixelFormat NzImage::GetFormat() const -{ - return m_sharedImage->format; -} - -unsigned int NzImage::GetHeight(nzUInt8 level) const -{ - #if NAZARA_UTILITY_SAFE - if (level >= m_sharedImage->levelCount) - { - NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_sharedImage->levelCount) + ')'); - return 0; - } - #endif - - return GetLevelSize(m_sharedImage->height, level); -} - -nzUInt8 NzImage::GetLevelCount() const -{ - return m_sharedImage->levelCount; -} - -nzUInt8 NzImage::GetMaxLevel() const -{ - return GetMaxLevel(m_sharedImage->width, m_sharedImage->height, m_sharedImage->depth); -} - -NzColor NzImage::GetPixelColor(unsigned int x, unsigned int y, unsigned int z) const -{ - #if NAZARA_UTILITY_SAFE - if (!m_sharedImage) - { - NazaraError("Image must be valid"); - return NzColor(); - } - - if (NzPixelFormat::IsCompressed(m_sharedImage->format)) - { - NazaraError("Cannot access pixels from compressed image"); - return NzColor(); - } - - if (x >= m_sharedImage->width) - { - NazaraError("X value exceeds width (" + NzString::Number(x) + " >= (" + NzString::Number(m_sharedImage->width) + ')'); - return NzColor(); - } - - if (y >= m_sharedImage->height) - { - NazaraError("Y value exceeds width (" + NzString::Number(y) + " >= (" + NzString::Number(m_sharedImage->height) + ')'); - return NzColor(); - } - - unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : m_sharedImage->depth; - if (z >= depth) - { - NazaraError("Z value exceeds depth (" + NzString::Number(z) + " >= (" + NzString::Number(depth) + ')'); - return NzColor(); - } - #endif - - const nzUInt8* pixel = GetPixelPtr(m_sharedImage->pixels[0], NzPixelFormat::GetBPP(m_sharedImage->format), x, y, z, m_sharedImage->width, m_sharedImage->height); - - NzColor color; - if (!NzPixelFormat::Convert(m_sharedImage->format, nzPixelFormat_RGBA8, pixel, &color.r)) - NazaraError("Failed to convert image's format to RGBA8"); - - return color; -} - -nzUInt8* NzImage::GetPixels(unsigned int x, unsigned int y, unsigned int z, nzUInt8 level) -{ - #if NAZARA_UTILITY_SAFE - if (!m_sharedImage) - { - NazaraError("Image must be valid"); - return nullptr; - } - - if (x >= m_sharedImage->width) - { - NazaraError("X value exceeds width (" + NzString::Number(x) + " >= (" + NzString::Number(m_sharedImage->width) + ')'); - return nullptr; - } - - if (y >= m_sharedImage->height) - { - NazaraError("Y value exceeds width (" + NzString::Number(y) + " >= (" + NzString::Number(m_sharedImage->height) + ')'); - return nullptr; - } - - unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : m_sharedImage->depth; - if (z >= depth) - { - NazaraError("Z value exceeds depth (" + NzString::Number(z) + " >= (" + NzString::Number(depth) + ')'); - return nullptr; - } - - - if (level >= m_sharedImage->levelCount) - { - NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_sharedImage->levelCount) + ')'); - return nullptr; - } - #endif - - EnsureOwnership(); - - return GetPixelPtr(m_sharedImage->pixels[level], NzPixelFormat::GetBPP(m_sharedImage->format), x, y, z, m_sharedImage->width, m_sharedImage->height); -} - -unsigned int NzImage::GetSize() const -{ - unsigned int width = m_sharedImage->width; - unsigned int height = m_sharedImage->height; - unsigned int depth = m_sharedImage->depth; - - unsigned int size = 0; - for (unsigned int i = 0; i < m_sharedImage->levelCount; ++i) - { - size += width * height * depth; - - if (width > 1) - width >>= 1; - - if (height > 1) - height >>= 1; - - if (depth > 1) - depth >>= 1; - } - - if (m_sharedImage->type == nzImageType_Cubemap) - size *= 6; - - return size * NzPixelFormat::GetBPP(m_sharedImage->format); -} - -unsigned int NzImage::GetSize(nzUInt8 level) const -{ - #if NAZARA_UTILITY_SAFE - if (level >= m_sharedImage->levelCount) - { - NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_sharedImage->levelCount) + ')'); - return 0; - } - #endif - - return (GetLevelSize(m_sharedImage->width, level)) * - (GetLevelSize(m_sharedImage->height, level)) * - ((m_sharedImage->type == nzImageType_Cubemap) ? 6 : GetLevelSize(m_sharedImage->depth, level)) * - NzPixelFormat::GetBPP(m_sharedImage->format); -} - -nzImageType NzImage::GetType() const -{ - return m_sharedImage->type; -} - -unsigned int NzImage::GetWidth(nzUInt8 level) const -{ - #if NAZARA_UTILITY_SAFE - if (level >= m_sharedImage->levelCount) - { - NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_sharedImage->levelCount) + ')'); - return 0; - } - #endif - - return GetLevelSize(m_sharedImage->width, level); -} - -bool NzImage::IsCompressed() const -{ - return NzPixelFormat::IsCompressed(m_sharedImage->format); -} - -bool NzImage::IsCubemap() const -{ - return m_sharedImage->type == nzImageType_Cubemap; -} - -bool NzImage::IsValid() const -{ - return m_sharedImage != &emptyImage; -} - -bool NzImage::LoadFromFile(const NzString& filePath, const NzImageParams& params) -{ - return NzImageLoader::LoadFromFile(this, filePath, params); -} - -bool NzImage::LoadFromMemory(const void* data, std::size_t size, const NzImageParams& params) -{ - return NzImageLoader::LoadFromMemory(this, data, size, params); -} - -bool NzImage::LoadFromStream(NzInputStream& stream, const NzImageParams& params) -{ - return NzImageLoader::LoadFromStream(this, stream, params); -} - -bool NzImage::SetLevelCount(nzUInt8 levelCount) -{ - #if NAZARA_UTILITY_SAFE - if (!m_sharedImage) - { - NazaraError("Image must be valid"); - return false; - } - - if (levelCount == 0) - { - NazaraError("Level count must be positive"); - return false; - } - #endif - - levelCount = std::min(levelCount, GetMaxLevel(m_sharedImage->width, m_sharedImage->height, m_sharedImage->depth)); - - if (m_sharedImage->levelCount == levelCount) - return true; - - EnsureOwnership(); - - nzUInt8 oldLevelCount = m_sharedImage->levelCount; - nzUInt8 maxLevelCount = std::max(levelCount, oldLevelCount); - m_sharedImage->levelCount = levelCount; // Pour faire fonctionner GetSize - - nzUInt8** pixels = new nzUInt8*[levelCount]; - for (unsigned int i = 0; i < maxLevelCount; ++i) - { - if (i < oldLevelCount) - pixels[i] = m_sharedImage->pixels[i]; - else if (i < levelCount) - pixels[i] = new nzUInt8[GetSize(i)]; - else - delete[] m_sharedImage->pixels[i]; - } - - delete[] m_sharedImage->pixels; - - m_sharedImage->pixels = pixels; - - return true; -} - -bool NzImage::SetPixelColor(const NzColor& color, unsigned int x, unsigned int y, unsigned int z) -{ - #if NAZARA_UTILITY_SAFE - if (!m_sharedImage) - { - NazaraError("Image must be valid"); - return false; - } - - if (NzPixelFormat::IsCompressed(m_sharedImage->format)) - { - NazaraError("Cannot access pixels from compressed image"); - return false; - } - - if (x >= m_sharedImage->width) - { - NazaraError("X value exceeds width (" + NzString::Number(x) + " >= (" + NzString::Number(m_sharedImage->width) + ')'); - return false; - } - - if (y >= m_sharedImage->height) - { - NazaraError("Y value exceeds width (" + NzString::Number(y) + " >= (" + NzString::Number(m_sharedImage->height) + ')'); - return false; - } - - unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : m_sharedImage->depth; - if (z >= depth) - { - NazaraError("Z value exceeds depth (" + NzString::Number(z) + " >= (" + NzString::Number(depth) + ')'); - return false; - } - #endif - - nzUInt8* pixel = GetPixelPtr(m_sharedImage->pixels[0], NzPixelFormat::GetBPP(m_sharedImage->format), x, y, z, m_sharedImage->width, m_sharedImage->height); - - if (!NzPixelFormat::Convert(nzPixelFormat_RGBA8, m_sharedImage->format, &color.r, pixel)) - { - NazaraError("Failed to convert RGBA8 to image's format"); - return false; - } - - return true; -} - -bool NzImage::Update(const nzUInt8* pixels, nzUInt8 level) -{ - #if NAZARA_UTILITY_SAFE - if (!m_sharedImage) - { - NazaraError("Image must be valid"); - return false; - } - - if (!pixels) - { - NazaraError("Invalid pixel source"); - return false; - } - - if (level >= m_sharedImage->levelCount) - { - NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_sharedImage->levelCount) + ')'); - return false; - } - #endif - - EnsureOwnership(); - - std::memcpy(m_sharedImage->pixels[level], pixels, GetSize(level)); - - return true; -} - -bool NzImage::Update(const nzUInt8* pixels, const NzRectui& rect, unsigned int z, nzUInt8 level) -{ - #if NAZARA_UTILITY_SAFE - if (!m_sharedImage) - { - NazaraError("Image must be valid"); - return false; - } - - if (!pixels) - { - NazaraError("Invalid pixel source"); - return false; - } - - if (level >= m_sharedImage->levelCount) - { - NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_sharedImage->levelCount) + ')'); - return false; - } - - if (!rect.IsValid()) - { - NazaraError("Invalid rectangle"); - return false; - } - #endif - - unsigned int width = GetLevelSize(m_sharedImage->width, level); - unsigned int height = GetLevelSize(m_sharedImage->height, level); - - #if NAZARA_UTILITY_SAFE - if (rect.x+rect.width > width || rect.y+rect.height > height) - { - NazaraError("Rectangle dimensions are out of bounds"); - return false; - } - - unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : GetLevelSize(m_sharedImage->depth, level); - if (z >= depth) - { - NazaraError("Z value exceeds depth (" + NzString::Number(z) + " >= " + NzString::Number(depth) + ')'); - return false; - } - #endif - - EnsureOwnership(); - - nzUInt8 bpp = NzPixelFormat::GetBPP(m_sharedImage->format); - nzUInt8* dstPixels = GetPixelPtr(m_sharedImage->pixels[level], bpp, rect.x, rect.y, z, width, height); - unsigned int srcStride = rect.width * bpp; - unsigned int dstStride = m_sharedImage->width * bpp; - for (unsigned int y = 0; y < rect.height; ++y) - { - std::memcpy(dstPixels, pixels, srcStride); - pixels += srcStride; - dstPixels += dstStride; - } - - return true; -} - -bool NzImage::Update(const nzUInt8* pixels, const NzCubeui& cube, nzUInt8 level) -{ - ///FIXME: Vérifier que ça fonctionne correctement - #if NAZARA_UTILITY_SAFE - if (!m_sharedImage) - { - NazaraError("Image must be valid"); - return false; - } - - if (!pixels) - { - NazaraError("Invalid pixel source"); - return false; - } - - if (level >= m_sharedImage->levelCount) - { - NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_sharedImage->levelCount) + ')'); - return false; - } - #endif - - unsigned int width = GetLevelSize(m_sharedImage->width, level); - unsigned int height = GetLevelSize(m_sharedImage->height, level); - unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : GetLevelSize(m_sharedImage->height, level); - - #if NAZARA_UTILITY_SAFE - if (!cube.IsValid()) - { - NazaraError("Invalid cube"); - return false; - } - - if (cube.x+cube.width > width || cube.y+cube.height > height || cube.z+cube.depth > depth) - { - NazaraError("Cube dimensions are out of bounds"); - return false; - } - #endif - - EnsureOwnership(); - - nzUInt8 bpp = NzPixelFormat::GetBPP(m_sharedImage->format); - nzUInt8* dstPixels = GetPixelPtr(m_sharedImage->pixels[level], bpp, cube.x, cube.y, cube.z, width, height); - unsigned int srcStride = cube.width * bpp; - unsigned int dstStride = width * bpp; - unsigned int faceSize = dstStride * height; - for (unsigned int z = 0; z < cube.depth; ++z) - { - nzUInt8* facePixels = dstPixels; - for (unsigned int y = 0; y < cube.height; ++y) - { - std::memcpy(facePixels, pixels, srcStride); - pixels += srcStride; - facePixels += dstStride; - } - - dstPixels += faceSize; - } - - return true; -} - -NzImage& NzImage::operator=(const NzImage& image) -{ - ReleaseImage(); - - m_sharedImage = image.m_sharedImage; - if (m_sharedImage != &emptyImage) - { - NazaraMutexLock(m_sharedImage->mutex); - m_sharedImage->refCount++; - NazaraMutexUnlock(m_sharedImage->mutex); - } - - return *this; -} - -NzImage& NzImage::operator=(NzImage&& image) noexcept -{ - std::swap(m_sharedImage, image.m_sharedImage); - - return *this; -} - -nzUInt8 NzImage::GetMaxLevel(unsigned int width, unsigned int height, unsigned int depth) -{ - static const float l2 = std::log(2.f); - - unsigned int widthLevel = std::log(static_cast(width))/l2; - unsigned int heightLevel = std::log(static_cast(height))/l2; - unsigned int depthLevel = std::log(static_cast(depth))/l2; - - return std::max(std::max(std::max(widthLevel, heightLevel), depthLevel), 1U); -} - -void NzImage::EnsureOwnership() -{ - if (m_sharedImage == &emptyImage) - return; - - NazaraLock(m_sharedImage->mutex); - if (m_sharedImage->refCount > 1) - { - m_sharedImage->refCount--; - - nzUInt8** pixels = new nzUInt8*[m_sharedImage->levelCount]; - for (unsigned int i = 0; i < m_sharedImage->levelCount; ++i) - { - unsigned int size = GetSize(i); - pixels[i] = new nzUInt8[size]; - std::memcpy(pixels[i], m_sharedImage->pixels[i], size); - } - - m_sharedImage = new SharedImage(1, m_sharedImage->type, m_sharedImage->format, m_sharedImage->levelCount, pixels, m_sharedImage->width, m_sharedImage->height, m_sharedImage->depth); - } -} - -void NzImage::ReleaseImage() -{ - if (m_sharedImage == &emptyImage) - return; - - NazaraMutexLock(m_sharedImage->mutex); - bool freeSharedImage = (--m_sharedImage->refCount == 0); - NazaraMutexUnlock(m_sharedImage->mutex); - - if (freeSharedImage) - { - for (unsigned int i = 0; i < m_sharedImage->levelCount; ++i) - delete[] m_sharedImage->pixels[i]; - - delete[] m_sharedImage->pixels; - delete m_sharedImage; - } - - m_sharedImage = &emptyImage; -} - -NzImage::SharedImage NzImage::emptyImage(0, nzImageType_2D, nzPixelFormat_Undefined, 1, nullptr, 0, 0, 0); -NzImageLoader::LoaderList NzImage::s_loaders; +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include + +namespace +{ + inline unsigned int GetLevelSize(unsigned int size, nzUInt8 level) + { + return std::max(size >> level, 1U); + } + + inline nzUInt8* GetPixelPtr(nzUInt8* base, nzUInt8 bpp, unsigned int x, unsigned int y, unsigned int z, unsigned int width, unsigned int height) + { + return &base[(width*(height*z + y) + x)*bpp]; + } +} + +bool NzImageParams::IsValid() const +{ + return true; +} + +NzImage::NzImage() : +m_sharedImage(&emptyImage) +{ +} + +NzImage::NzImage(nzImageType type, nzPixelFormat format, unsigned int width, unsigned int height, unsigned int depth, nzUInt8 levelCount) : +m_sharedImage(&emptyImage) +{ + Create(type, format, width, height, depth, levelCount); + + #ifdef NAZARA_DEBUG + if (!m_sharedImage) + { + NazaraError("Failed to create image"); + throw std::runtime_error("Constructor failed"); + } + #endif +} + +NzImage::NzImage(const NzImage& image) : +NzResource(image), +m_sharedImage(image.m_sharedImage) +{ + if (m_sharedImage != &emptyImage) + { + NazaraMutexLock(m_sharedImage->mutex); + m_sharedImage->refCount++; + NazaraMutexUnlock(m_sharedImage->mutex); + } +} + +NzImage::NzImage(NzImage&& image) noexcept : +m_sharedImage(image.m_sharedImage) +{ + image.m_sharedImage = &emptyImage; +} + +NzImage::~NzImage() +{ + ReleaseImage(); +} + +bool NzImage::Convert(nzPixelFormat format) +{ + #if NAZARA_UTILITY_SAFE + if (!m_sharedImage) + { + NazaraError("Image must be valid"); + return false; + } + + if (!NzPixelFormat::IsValid(format)) + { + NazaraError("Invalid pixel format"); + return false; + } + + if (!NzPixelFormat::IsConversionSupported(m_sharedImage->format, format)) + { + NazaraError("Conversion from " + NzPixelFormat::ToString(m_sharedImage->format) + " to " + NzPixelFormat::ToString(format) + " is not supported"); + return false; + } + #endif + + if (format == m_sharedImage->format) + return true; + + nzUInt8** levels = new nzUInt8*[m_sharedImage->levelCount]; + + unsigned int width = m_sharedImage->width; + unsigned int height = m_sharedImage->height; + + // Les images 3D et cubemaps sont stockés de la même façon + unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : m_sharedImage->depth; + + for (unsigned int i = 0; i < m_sharedImage->levelCount; ++i) + { + unsigned int pixelsPerFace = width*height; + nzUInt8* ptr = new nzUInt8[pixelsPerFace*depth*NzPixelFormat::GetBPP(format)]; + nzUInt8* pixels = m_sharedImage->pixels[i]; + unsigned int srcStride = pixelsPerFace * NzPixelFormat::GetBPP(m_sharedImage->format); + unsigned int dstStride = pixelsPerFace * NzPixelFormat::GetBPP(format); + + for (unsigned int d = 0; d < depth; ++d) + { + if (!NzPixelFormat::Convert(m_sharedImage->format, format, pixels, &pixels[srcStride], ptr)) + { + NazaraError("Failed to convert image"); + + // Nettoyage de la mémoire + delete[] ptr; // Permet une optimisation de boucle (GCC) + for (unsigned int j = 0; j < i; ++j) + delete[] levels[j]; + + delete[] levels; + + return false; + } + + pixels += srcStride; + ptr += dstStride; + } + + levels[i] = ptr; + + if (width > 1) + width >>= 1; + + if (height > 1) + height >>= 1; + + if (depth > 1 && m_sharedImage->type != nzImageType_Cubemap) + depth >>= 1; + } + + SharedImage* newImage = new SharedImage(1, m_sharedImage->type, format, m_sharedImage->levelCount, levels, m_sharedImage->width, m_sharedImage->height, m_sharedImage->depth); + + ReleaseImage(); + m_sharedImage = newImage; + + return true; +} + +bool NzImage::Copy(const NzImage& source, const NzCubeui& srcCube, const NzVector3ui& dstPos) +{ + #if NAZARA_UTILITY_SAFE + if (!source.IsValid()) + { + NazaraError("Source image must be valid"); + return false; + } + + if (source.GetFormat() != m_sharedImage->format) + { + NazaraError("Source image format does not match destination image format"); + return false; + } + #endif + + const nzUInt8* pixels = source.GetConstPixels(0, srcCube.x, srcCube.y, srcCube.z); + if (!pixels) + { + NazaraError("Failed to access pixels"); + return false; + } + + /* + Correctif temporaire : Update veut de la mémoire contigüe + Il est donc nécessaire de prendre la partie de la texture que nous voulons mettre à jour + + ///FIXME: Trouver une interface pour gérer ce genre de problème (Façon OpenGL?) + (Appliquer l'interface à NzTexture également) + */ + nzUInt8 bpp = NzPixelFormat::GetBPP(m_sharedImage->format); + unsigned int dstLineStride = srcCube.width*bpp; + unsigned int dstFaceStride = dstLineStride*srcCube.height; + unsigned int srcLineStride = m_sharedImage->width*bpp; + unsigned int srcFaceStride = srcLineStride*m_sharedImage->height; + + nzUInt8* cube = new nzUInt8[dstFaceStride*srcCube.depth]; + nzUInt8* ptr = cube; + + for (unsigned int z = 0; z < srcCube.depth; ++z) + { + nzUInt8* facePixels = ptr; + for (unsigned int y = 0; y < srcCube.height; ++y) + { + std::memcpy(facePixels, pixels, dstLineStride); + + facePixels += dstLineStride; + pixels += srcLineStride; + } + + ptr += dstFaceStride; + pixels += srcFaceStride; + } + + bool success = Update(cube, NzCubeui(dstPos.x, dstPos.y, dstPos.z, srcCube.width, srcCube.height, srcCube.depth)); + + delete[] cube; + + return success; +} + +bool NzImage::Create(nzImageType type, nzPixelFormat format, unsigned int width, unsigned int height, unsigned int depth, nzUInt8 levelCount) +{ + ReleaseImage(); + + #if NAZARA_UTILITY_SAFE + if (!NzPixelFormat::IsValid(format)) + { + NazaraError("Invalid pixel format"); + return false; + } + + if (width == 0) + { + NazaraError("Width must be at least 1 (0)"); + return false; + } + + if (height == 0) + { + NazaraError("Height must be at least 1 (0)"); + return false; + } + + if (depth == 0) + { + NazaraError("Depth must be at least 1 (0)"); + return false; + } + + switch (type) + { + case nzImageType_1D: + if (height > 1) + { + NazaraError("1D textures must be 1 height"); + return false; + } + + if (depth > 1) + { + NazaraError("1D textures must be 1 depth"); + return false; + } + break; + + case nzImageType_1D_Array: + case nzImageType_2D: + if (depth > 1) + { + NazaraError("2D textures must be 1 depth"); + return false; + } + break; + + case nzImageType_2D_Array: + case nzImageType_3D: + break; + + case nzImageType_Cubemap: + if (depth > 1) + { + NazaraError("Cubemaps must be 1 depth"); + return false; + } + + if (width != height) + { + NazaraError("Cubemaps must have square dimensions"); + return false; + } + break; + + default: + NazaraInternalError("Image type not handled"); + return false; + } + #endif + + levelCount = std::min(levelCount, GetMaxLevel(width, height, depth)); + + nzUInt8** levels = new nzUInt8*[levelCount]; + + unsigned int w = width; + unsigned int h = height; + unsigned int d = (type == nzImageType_Cubemap) ? 6 : depth; + + for (unsigned int i = 0; i < levelCount; ++i) + { + // Cette allocation est protégée car sa taille dépend directement de paramètres utilisateurs + try + { + levels[i] = new nzUInt8[w * h * d * NzPixelFormat::GetBPP(format)]; + + if (w > 1) + w >>= 1; + + if (h > 1) + h >>= 1; + + if (d > 1 && type != nzImageType_Cubemap) + d >>= 1; + } + catch (const std::exception& e) + { + NazaraError("Failed to allocate image's level " + NzString::Number(i) + " (" + NzString(e.what()) + ')'); + + // Nettoyage + delete[] levels[i]; // Permet une optimisation de boucle (GCC) + for (unsigned int j = 0; j < i; ++j) + delete[] levels[j]; + + delete[] levels; + + return false; + } + } + + m_sharedImage = new SharedImage(1, type, format, levelCount, levels, width, height, depth); + + NotifyCreated(); + return true; +} + +void NzImage::Destroy() +{ + NotifyDestroy(); + ReleaseImage(); +} + +bool NzImage::Fill(const NzColor& color) +{ + #if NAZARA_UTILITY_SAFE + if (!m_sharedImage) + { + NazaraError("Image must be valid"); + return false; + } + + if (NzPixelFormat::IsCompressed(m_sharedImage->format)) + { + NazaraError("Cannot access pixels from compressed image"); + return false; + } + #endif + + EnsureOwnership(); + + nzUInt8 bpp = NzPixelFormat::GetBPP(m_sharedImage->format); + nzUInt8* pixels = new nzUInt8[bpp]; + if (!NzPixelFormat::Convert(nzPixelFormat_RGBA8, m_sharedImage->format, &color.r, pixels)) + { + NazaraError("Failed to convert RGBA8 to " + NzPixelFormat::ToString(m_sharedImage->format)); + delete[] pixels; + + return false; + } + + unsigned int width = m_sharedImage->width; + unsigned int height = m_sharedImage->height; + unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : m_sharedImage->depth; + + for (unsigned int level = 0; level < m_sharedImage->levelCount; ++level) + { + nzUInt8* ptr = &m_sharedImage->pixels[level][0]; + nzUInt8* end = &m_sharedImage->pixels[level][width*height*depth*bpp]; + + while (ptr < end) + { + std::memcpy(ptr, pixels, bpp); + ptr += bpp; + } + + if (width > 1U) + width >>= 1; + + if (height > 1U) + height >>= 1; + + if (depth > 1U && m_sharedImage->type != nzImageType_Cubemap) + depth >>= 1; + } + + delete[] pixels; + + return true; +} + +bool NzImage::Fill(const NzColor& color, const NzRectui& rect, unsigned int z) +{ + #if NAZARA_UTILITY_SAFE + if (!m_sharedImage) + { + NazaraError("Image must be valid"); + return false; + } + + if (!rect.IsValid()) + { + NazaraError("Invalid rectangle"); + return false; + } + + if (rect.x+rect.width > m_sharedImage->width || rect.y+rect.height > m_sharedImage->height) + { + NazaraError("Rectangle dimensions are out of bounds"); + return false; + } + + unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : m_sharedImage->depth; + if (z >= depth) + { + NazaraError("Z value exceeds depth (" + NzString::Number(z) + " >= (" + NzString::Number(depth) + ')'); + return false; + } + #endif + + EnsureOwnership(); + + nzUInt8 bpp = NzPixelFormat::GetBPP(m_sharedImage->format); + nzUInt8* pixels = new nzUInt8[bpp]; + if (!NzPixelFormat::Convert(nzPixelFormat_RGBA8, m_sharedImage->format, &color.r, pixels)) + { + NazaraError("Failed to convert RGBA8 to " + NzPixelFormat::ToString(m_sharedImage->format)); + delete[] pixels; + + return false; + } + + ///FIXME: L'algorithme a du mal avec un bpp non multiple de 2 + nzUInt8* dstPixels = GetPixelPtr(m_sharedImage->pixels[0], bpp, rect.x, rect.y, z, m_sharedImage->width, m_sharedImage->height); + unsigned int srcStride = rect.width * bpp; + unsigned int dstStride = m_sharedImage->width * bpp; + for (unsigned int y = 0; y < rect.height; ++y) + { + nzUInt8* start = dstPixels; + nzUInt8* end = dstPixels + srcStride; + while (start < end) + { + std::memcpy(start, pixels, bpp); + start += bpp; + } + + dstPixels += dstStride; + } + + delete[] pixels; + + return true; +} + +bool NzImage::Fill(const NzColor& color, const NzCubeui& cube) +{ + #if NAZARA_UTILITY_SAFE + if (!m_sharedImage) + { + NazaraError("Image must be valid"); + return false; + } + + if (!cube.IsValid()) + { + NazaraError("Invalid rectangle"); + return false; + } + + if (cube.x+cube.width > m_sharedImage->width || cube.y+cube.height > m_sharedImage->height || cube.z+cube.depth > m_sharedImage->depth) + { + NazaraError("Cube dimensions are out of bounds"); + return false; + } + #endif + + EnsureOwnership(); + + nzUInt8 bpp = NzPixelFormat::GetBPP(m_sharedImage->format); + nzUInt8* pixels = new nzUInt8[bpp]; + if (!NzPixelFormat::Convert(nzPixelFormat_RGBA8, m_sharedImage->format, &color.r, pixels)) + { + NazaraError("Failed to convert RGBA8 to " + NzPixelFormat::ToString(m_sharedImage->format)); + delete[] pixels; + + return false; + } + + ///FIXME: L'algorithme a du mal avec un bpp non multiple de 2 + nzUInt8* dstPixels = GetPixelPtr(m_sharedImage->pixels[0], bpp, cube.x, cube.y, cube.z, m_sharedImage->width, m_sharedImage->height); + unsigned int srcStride = cube.width * bpp; + unsigned int dstStride = m_sharedImage->width * bpp; + unsigned int faceSize = dstStride * m_sharedImage->height; + for (unsigned int z = 0; z < cube.depth; ++z) + { + nzUInt8* facePixels = dstPixels; + for (unsigned int y = 0; y < cube.height; ++y) + { + nzUInt8* start = facePixels; + nzUInt8* end = facePixels + srcStride; + while (start < end) + { + std::memcpy(start, pixels, bpp); + start += bpp; + } + + facePixels += dstStride; + } + + dstPixels += faceSize; + } + + delete[] pixels; + + return true; +} + +bool NzImage::FlipHorizontally() +{ + #if NAZARA_UTILITY_SAFE + if (!m_sharedImage) + { + NazaraError("Image must be valid"); + return false; + } + + if (NzPixelFormat::IsCompressed(m_sharedImage->format)) + { + NazaraError("Cannot flip compressed image"); + return false; + } + #endif + + EnsureOwnership(); + + unsigned int width = m_sharedImage->width; + unsigned int height = m_sharedImage->height; + unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : m_sharedImage->depth; + for (unsigned int level = 0; level < m_sharedImage->levelCount; ++level) + { + NzPixelFormat::Flip(nzPixelFlipping_Horizontally, m_sharedImage->format, width, height, depth, m_sharedImage->pixels[level], m_sharedImage->pixels[level]); + + if (width > 1U) + width >>= 1; + + if (height > 1U) + height >>= 1; + + if (depth > 1U && m_sharedImage->type != nzImageType_Cubemap) + depth >>= 1; + } + + return true; +} + + +bool NzImage::FlipVertically() +{ + #if NAZARA_UTILITY_SAFE + if (!m_sharedImage) + { + NazaraError("Image must be valid"); + return false; + } + + if (NzPixelFormat::IsCompressed(m_sharedImage->format)) + { + NazaraError("Cannot flip compressed image"); + return false; + } + #endif + + EnsureOwnership(); + + unsigned int width = m_sharedImage->width; + unsigned int height = m_sharedImage->height; + unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : m_sharedImage->depth; + for (unsigned int level = 0; level < m_sharedImage->levelCount; ++level) + { + NzPixelFormat::Flip(nzPixelFlipping_Vertically, m_sharedImage->format, width, height, depth, m_sharedImage->pixels[level], m_sharedImage->pixels[level]); + + if (width > 1U) + width >>= 1; + + if (height > 1U) + height >>= 1; + + if (depth > 1U && m_sharedImage->type != nzImageType_Cubemap) + depth >>= 1; + } + + return true; +} + +nzUInt8 NzImage::GetBPP() const +{ + return NzPixelFormat::GetBPP(m_sharedImage->format); +} + +const nzUInt8* NzImage::GetConstPixels(unsigned int x, unsigned int y, unsigned int z, nzUInt8 level) const +{ + #if NAZARA_UTILITY_SAFE + if (!m_sharedImage) + { + NazaraError("Image must be valid"); + return nullptr; + } + + if (x >= m_sharedImage->width) + { + NazaraError("X value exceeds width (" + NzString::Number(x) + " >= (" + NzString::Number(m_sharedImage->width) + ')'); + return nullptr; + } + + if (y >= m_sharedImage->height) + { + NazaraError("Y value exceeds width (" + NzString::Number(y) + " >= (" + NzString::Number(m_sharedImage->height) + ')'); + return nullptr; + } + + unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : m_sharedImage->depth; + if (z >= depth) + { + NazaraError("Z value exceeds depth (" + NzString::Number(z) + " >= (" + NzString::Number(depth) + ')'); + return nullptr; + } + + if (level >= m_sharedImage->levelCount) + { + NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_sharedImage->levelCount) + ')'); + return nullptr; + } + #endif + + return GetPixelPtr(m_sharedImage->pixels[level], NzPixelFormat::GetBPP(m_sharedImage->format), x, y, z, m_sharedImage->width, m_sharedImage->height); +} + +unsigned int NzImage::GetDepth(nzUInt8 level) const +{ + #if NAZARA_UTILITY_SAFE + if (level >= m_sharedImage->levelCount) + { + NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_sharedImage->levelCount) + ')'); + return 0; + } + #endif + + return GetLevelSize(m_sharedImage->depth, level); +} + +nzPixelFormat NzImage::GetFormat() const +{ + return m_sharedImage->format; +} + +unsigned int NzImage::GetHeight(nzUInt8 level) const +{ + #if NAZARA_UTILITY_SAFE + if (level >= m_sharedImage->levelCount) + { + NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_sharedImage->levelCount) + ')'); + return 0; + } + #endif + + return GetLevelSize(m_sharedImage->height, level); +} + +nzUInt8 NzImage::GetLevelCount() const +{ + return m_sharedImage->levelCount; +} + +nzUInt8 NzImage::GetMaxLevel() const +{ + return GetMaxLevel(m_sharedImage->width, m_sharedImage->height, m_sharedImage->depth); +} + +NzColor NzImage::GetPixelColor(unsigned int x, unsigned int y, unsigned int z) const +{ + #if NAZARA_UTILITY_SAFE + if (!m_sharedImage) + { + NazaraError("Image must be valid"); + return NzColor(); + } + + if (NzPixelFormat::IsCompressed(m_sharedImage->format)) + { + NazaraError("Cannot access pixels from compressed image"); + return NzColor(); + } + + if (x >= m_sharedImage->width) + { + NazaraError("X value exceeds width (" + NzString::Number(x) + " >= (" + NzString::Number(m_sharedImage->width) + ')'); + return NzColor(); + } + + if (y >= m_sharedImage->height) + { + NazaraError("Y value exceeds width (" + NzString::Number(y) + " >= (" + NzString::Number(m_sharedImage->height) + ')'); + return NzColor(); + } + + unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : m_sharedImage->depth; + if (z >= depth) + { + NazaraError("Z value exceeds depth (" + NzString::Number(z) + " >= (" + NzString::Number(depth) + ')'); + return NzColor(); + } + #endif + + const nzUInt8* pixel = GetPixelPtr(m_sharedImage->pixels[0], NzPixelFormat::GetBPP(m_sharedImage->format), x, y, z, m_sharedImage->width, m_sharedImage->height); + + NzColor color; + if (!NzPixelFormat::Convert(m_sharedImage->format, nzPixelFormat_RGBA8, pixel, &color.r)) + NazaraError("Failed to convert image's format to RGBA8"); + + return color; +} + +nzUInt8* NzImage::GetPixels(unsigned int x, unsigned int y, unsigned int z, nzUInt8 level) +{ + #if NAZARA_UTILITY_SAFE + if (!m_sharedImage) + { + NazaraError("Image must be valid"); + return nullptr; + } + + if (x >= m_sharedImage->width) + { + NazaraError("X value exceeds width (" + NzString::Number(x) + " >= (" + NzString::Number(m_sharedImage->width) + ')'); + return nullptr; + } + + if (y >= m_sharedImage->height) + { + NazaraError("Y value exceeds width (" + NzString::Number(y) + " >= (" + NzString::Number(m_sharedImage->height) + ')'); + return nullptr; + } + + unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : m_sharedImage->depth; + if (z >= depth) + { + NazaraError("Z value exceeds depth (" + NzString::Number(z) + " >= (" + NzString::Number(depth) + ')'); + return nullptr; + } + + + if (level >= m_sharedImage->levelCount) + { + NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_sharedImage->levelCount) + ')'); + return nullptr; + } + #endif + + EnsureOwnership(); + + return GetPixelPtr(m_sharedImage->pixels[level], NzPixelFormat::GetBPP(m_sharedImage->format), x, y, z, m_sharedImage->width, m_sharedImage->height); +} + +unsigned int NzImage::GetSize() const +{ + unsigned int width = m_sharedImage->width; + unsigned int height = m_sharedImage->height; + unsigned int depth = m_sharedImage->depth; + + unsigned int size = 0; + for (unsigned int i = 0; i < m_sharedImage->levelCount; ++i) + { + size += width * height * depth; + + if (width > 1) + width >>= 1; + + if (height > 1) + height >>= 1; + + if (depth > 1) + depth >>= 1; + } + + if (m_sharedImage->type == nzImageType_Cubemap) + size *= 6; + + return size * NzPixelFormat::GetBPP(m_sharedImage->format); +} + +unsigned int NzImage::GetSize(nzUInt8 level) const +{ + #if NAZARA_UTILITY_SAFE + if (level >= m_sharedImage->levelCount) + { + NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_sharedImage->levelCount) + ')'); + return 0; + } + #endif + + return (GetLevelSize(m_sharedImage->width, level)) * + (GetLevelSize(m_sharedImage->height, level)) * + ((m_sharedImage->type == nzImageType_Cubemap) ? 6 : GetLevelSize(m_sharedImage->depth, level)) * + NzPixelFormat::GetBPP(m_sharedImage->format); +} + +nzImageType NzImage::GetType() const +{ + return m_sharedImage->type; +} + +unsigned int NzImage::GetWidth(nzUInt8 level) const +{ + #if NAZARA_UTILITY_SAFE + if (level >= m_sharedImage->levelCount) + { + NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_sharedImage->levelCount) + ')'); + return 0; + } + #endif + + return GetLevelSize(m_sharedImage->width, level); +} + +bool NzImage::IsCompressed() const +{ + return NzPixelFormat::IsCompressed(m_sharedImage->format); +} + +bool NzImage::IsCubemap() const +{ + return m_sharedImage->type == nzImageType_Cubemap; +} + +bool NzImage::IsValid() const +{ + return m_sharedImage != &emptyImage; +} + +bool NzImage::LoadFromFile(const NzString& filePath, const NzImageParams& params) +{ + return NzImageLoader::LoadFromFile(this, filePath, params); +} + +bool NzImage::LoadFromMemory(const void* data, std::size_t size, const NzImageParams& params) +{ + return NzImageLoader::LoadFromMemory(this, data, size, params); +} + +bool NzImage::LoadFromStream(NzInputStream& stream, const NzImageParams& params) +{ + return NzImageLoader::LoadFromStream(this, stream, params); +} + +bool NzImage::SetLevelCount(nzUInt8 levelCount) +{ + #if NAZARA_UTILITY_SAFE + if (!m_sharedImage) + { + NazaraError("Image must be valid"); + return false; + } + + if (levelCount == 0) + { + NazaraError("Level count must be positive"); + return false; + } + #endif + + levelCount = std::min(levelCount, GetMaxLevel(m_sharedImage->width, m_sharedImage->height, m_sharedImage->depth)); + + if (m_sharedImage->levelCount == levelCount) + return true; + + EnsureOwnership(); + + nzUInt8 oldLevelCount = m_sharedImage->levelCount; + nzUInt8 maxLevelCount = std::max(levelCount, oldLevelCount); + m_sharedImage->levelCount = levelCount; // Pour faire fonctionner GetSize + + nzUInt8** pixels = new nzUInt8*[levelCount]; + for (unsigned int i = 0; i < maxLevelCount; ++i) + { + if (i < oldLevelCount) + pixels[i] = m_sharedImage->pixels[i]; + else if (i < levelCount) + pixels[i] = new nzUInt8[GetSize(i)]; + else + delete[] m_sharedImage->pixels[i]; + } + + delete[] m_sharedImage->pixels; + + m_sharedImage->pixels = pixels; + + return true; +} + +bool NzImage::SetPixelColor(const NzColor& color, unsigned int x, unsigned int y, unsigned int z) +{ + #if NAZARA_UTILITY_SAFE + if (!m_sharedImage) + { + NazaraError("Image must be valid"); + return false; + } + + if (NzPixelFormat::IsCompressed(m_sharedImage->format)) + { + NazaraError("Cannot access pixels from compressed image"); + return false; + } + + if (x >= m_sharedImage->width) + { + NazaraError("X value exceeds width (" + NzString::Number(x) + " >= (" + NzString::Number(m_sharedImage->width) + ')'); + return false; + } + + if (y >= m_sharedImage->height) + { + NazaraError("Y value exceeds width (" + NzString::Number(y) + " >= (" + NzString::Number(m_sharedImage->height) + ')'); + return false; + } + + unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : m_sharedImage->depth; + if (z >= depth) + { + NazaraError("Z value exceeds depth (" + NzString::Number(z) + " >= (" + NzString::Number(depth) + ')'); + return false; + } + #endif + + nzUInt8* pixel = GetPixelPtr(m_sharedImage->pixels[0], NzPixelFormat::GetBPP(m_sharedImage->format), x, y, z, m_sharedImage->width, m_sharedImage->height); + + if (!NzPixelFormat::Convert(nzPixelFormat_RGBA8, m_sharedImage->format, &color.r, pixel)) + { + NazaraError("Failed to convert RGBA8 to image's format"); + return false; + } + + return true; +} + +bool NzImage::Update(const nzUInt8* pixels, nzUInt8 level) +{ + #if NAZARA_UTILITY_SAFE + if (!m_sharedImage) + { + NazaraError("Image must be valid"); + return false; + } + + if (!pixels) + { + NazaraError("Invalid pixel source"); + return false; + } + + if (level >= m_sharedImage->levelCount) + { + NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_sharedImage->levelCount) + ')'); + return false; + } + #endif + + EnsureOwnership(); + + std::memcpy(m_sharedImage->pixels[level], pixels, GetSize(level)); + + return true; +} + +bool NzImage::Update(const nzUInt8* pixels, const NzRectui& rect, unsigned int z, nzUInt8 level) +{ + #if NAZARA_UTILITY_SAFE + if (!m_sharedImage) + { + NazaraError("Image must be valid"); + return false; + } + + if (!pixels) + { + NazaraError("Invalid pixel source"); + return false; + } + + if (level >= m_sharedImage->levelCount) + { + NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_sharedImage->levelCount) + ')'); + return false; + } + + if (!rect.IsValid()) + { + NazaraError("Invalid rectangle"); + return false; + } + #endif + + unsigned int width = GetLevelSize(m_sharedImage->width, level); + unsigned int height = GetLevelSize(m_sharedImage->height, level); + + #if NAZARA_UTILITY_SAFE + if (rect.x+rect.width > width || rect.y+rect.height > height) + { + NazaraError("Rectangle dimensions are out of bounds"); + return false; + } + + unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : GetLevelSize(m_sharedImage->depth, level); + if (z >= depth) + { + NazaraError("Z value exceeds depth (" + NzString::Number(z) + " >= " + NzString::Number(depth) + ')'); + return false; + } + #endif + + EnsureOwnership(); + + nzUInt8 bpp = NzPixelFormat::GetBPP(m_sharedImage->format); + nzUInt8* dstPixels = GetPixelPtr(m_sharedImage->pixels[level], bpp, rect.x, rect.y, z, width, height); + unsigned int srcStride = rect.width * bpp; + unsigned int dstStride = m_sharedImage->width * bpp; + for (unsigned int y = 0; y < rect.height; ++y) + { + std::memcpy(dstPixels, pixels, srcStride); + pixels += srcStride; + dstPixels += dstStride; + } + + return true; +} + +bool NzImage::Update(const nzUInt8* pixels, const NzCubeui& cube, nzUInt8 level) +{ + ///FIXME: Vérifier que ça fonctionne correctement + #if NAZARA_UTILITY_SAFE + if (!m_sharedImage) + { + NazaraError("Image must be valid"); + return false; + } + + if (!pixels) + { + NazaraError("Invalid pixel source"); + return false; + } + + if (level >= m_sharedImage->levelCount) + { + NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_sharedImage->levelCount) + ')'); + return false; + } + #endif + + unsigned int width = GetLevelSize(m_sharedImage->width, level); + unsigned int height = GetLevelSize(m_sharedImage->height, level); + unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : GetLevelSize(m_sharedImage->height, level); + + #if NAZARA_UTILITY_SAFE + if (!cube.IsValid()) + { + NazaraError("Invalid cube"); + return false; + } + + if (cube.x+cube.width > width || cube.y+cube.height > height || cube.z+cube.depth > depth) + { + NazaraError("Cube dimensions are out of bounds"); + return false; + } + #endif + + EnsureOwnership(); + + nzUInt8 bpp = NzPixelFormat::GetBPP(m_sharedImage->format); + nzUInt8* dstPixels = GetPixelPtr(m_sharedImage->pixels[level], bpp, cube.x, cube.y, cube.z, width, height); + unsigned int srcStride = cube.width * bpp; + unsigned int dstStride = width * bpp; + unsigned int faceSize = dstStride * height; + for (unsigned int z = 0; z < cube.depth; ++z) + { + nzUInt8* facePixels = dstPixels; + for (unsigned int y = 0; y < cube.height; ++y) + { + std::memcpy(facePixels, pixels, srcStride); + pixels += srcStride; + facePixels += dstStride; + } + + dstPixels += faceSize; + } + + return true; +} + +NzImage& NzImage::operator=(const NzImage& image) +{ + ReleaseImage(); + + m_sharedImage = image.m_sharedImage; + if (m_sharedImage != &emptyImage) + { + NazaraMutexLock(m_sharedImage->mutex); + m_sharedImage->refCount++; + NazaraMutexUnlock(m_sharedImage->mutex); + } + + return *this; +} + +NzImage& NzImage::operator=(NzImage&& image) noexcept +{ + std::swap(m_sharedImage, image.m_sharedImage); + + return *this; +} + +nzUInt8 NzImage::GetMaxLevel(unsigned int width, unsigned int height, unsigned int depth) +{ + static const float l2 = std::log(2.f); + + unsigned int widthLevel = std::log(static_cast(width))/l2; + unsigned int heightLevel = std::log(static_cast(height))/l2; + unsigned int depthLevel = std::log(static_cast(depth))/l2; + + return std::max(std::max(std::max(widthLevel, heightLevel), depthLevel), 1U); +} + +void NzImage::EnsureOwnership() +{ + if (m_sharedImage == &emptyImage) + return; + + NazaraLock(m_sharedImage->mutex); + if (m_sharedImage->refCount > 1) + { + m_sharedImage->refCount--; + + nzUInt8** pixels = new nzUInt8*[m_sharedImage->levelCount]; + for (unsigned int i = 0; i < m_sharedImage->levelCount; ++i) + { + unsigned int size = GetSize(i); + pixels[i] = new nzUInt8[size]; + std::memcpy(pixels[i], m_sharedImage->pixels[i], size); + } + + m_sharedImage = new SharedImage(1, m_sharedImage->type, m_sharedImage->format, m_sharedImage->levelCount, pixels, m_sharedImage->width, m_sharedImage->height, m_sharedImage->depth); + } +} + +void NzImage::ReleaseImage() +{ + if (m_sharedImage == &emptyImage) + return; + + NazaraMutexLock(m_sharedImage->mutex); + bool freeSharedImage = (--m_sharedImage->refCount == 0); + NazaraMutexUnlock(m_sharedImage->mutex); + + if (freeSharedImage) + { + for (unsigned int i = 0; i < m_sharedImage->levelCount; ++i) + delete[] m_sharedImage->pixels[i]; + + delete[] m_sharedImage->pixels; + delete m_sharedImage; + } + + m_sharedImage = &emptyImage; +} + +NzImage::SharedImage NzImage::emptyImage(0, nzImageType_2D, nzPixelFormat_Undefined, 1, nullptr, 0, 0, 0); +NzImageLoader::LoaderList NzImage::s_loaders; diff --git a/src/Nazara/Utility/Loaders/MD2/Loader.cpp b/src/Nazara/Utility/Loaders/MD2/Loader.cpp index bb30b5832..8af677a6b 100644 --- a/src/Nazara/Utility/Loaders/MD2/Loader.cpp +++ b/src/Nazara/Utility/Loaders/MD2/Loader.cpp @@ -1,221 +1,200 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Utility module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace -{ - bool NzLoader_MD2_Check(NzInputStream& stream, const NzMeshParams& parameters) - { - NazaraUnused(parameters); - - nzUInt32 magic[2]; - if (stream.Read(&magic[0], 2*sizeof(nzUInt32)) != 2*sizeof(nzUInt32)) - return false; - - #if defined(NAZARA_BIG_ENDIAN) - NzByteSwap(&magic[0], sizeof(nzUInt32)); - NzByteSwap(&magic[1], sizeof(nzUInt32)); - #endif - - return magic[0] == md2Ident && magic[1] == 8; - } - - bool NzLoader_MD2_Load(NzMesh* mesh, NzInputStream& stream, const NzMeshParams& parameters) - { - md2_header header; - if (stream.Read(&header, sizeof(md2_header)) != sizeof(md2_header)) - { - NazaraError("Failed to read header"); - return false; - } - - // Les fichiers MD2 sont en little endian - #if defined(NAZARA_BIG_ENDIAN) - NzByteSwap(&header.ident, sizeof(nzUInt32)); - #endif - - if (header.ident != md2Ident) - { - NazaraError("Invalid MD2 file"); - return false; - } - - #if defined(NAZARA_BIG_ENDIAN) - NzByteSwap(&header.version, sizeof(nzUInt32)); - #endif - - if (header.version != 8) - { - NazaraError("Bad version number (" + NzString::Number(header.version) + ')'); - return false; - } - - #if defined(NAZARA_BIG_ENDIAN) - NzByteSwap(&header.skinwidth, sizeof(nzUInt32)); - NzByteSwap(&header.skinheight, sizeof(nzUInt32)); - NzByteSwap(&header.framesize, sizeof(nzUInt32)); - NzByteSwap(&header.num_skins, sizeof(nzUInt32)); - NzByteSwap(&header.num_vertices, sizeof(nzUInt32)); - NzByteSwap(&header.num_st, sizeof(nzUInt32)); - NzByteSwap(&header.num_tris, sizeof(nzUInt32)); - NzByteSwap(&header.num_glcmds, sizeof(nzUInt32)); - NzByteSwap(&header.num_frames, sizeof(nzUInt32)); - NzByteSwap(&header.offset_skins, sizeof(nzUInt32)); - NzByteSwap(&header.offset_st, sizeof(nzUInt32)); - NzByteSwap(&header.offset_tris, sizeof(nzUInt32)); - NzByteSwap(&header.offset_frames, sizeof(nzUInt32)); - NzByteSwap(&header.offset_glcmds, sizeof(nzUInt32)); - NzByteSwap(&header.offset_end, sizeof(nzUInt32)); - #endif - - if (stream.GetSize() < header.offset_end) - { - NazaraError("Incomplete MD2 file"); - return false; - } - - /// Création du mesh - // Animé ou statique, c'est la question - bool animated; - unsigned int startFrame = NzClamp(parameters.animation.startFrame, 0U, static_cast(header.num_frames-1)); - unsigned int endFrame = NzClamp(parameters.animation.endFrame, 0U, static_cast(header.num_frames-1)); - - if (parameters.loadAnimations && startFrame != endFrame) - animated = true; - else - animated = false; - - if (!mesh->Create((animated) ? nzAnimationType_Keyframe : nzAnimationType_Static)) // Ne devrait pas échouer - { - NazaraInternalError("Failed to create mesh"); - return false; - } - - /// Chargement des skins - if (header.num_skins > 0) - { - stream.SetCursorPos(header.offset_skins); - { - char skin[68]; - for (unsigned int i = 0; i < header.num_skins; ++i) - { - stream.Read(skin, 68*sizeof(char)); - mesh->AddSkin(skin); - } - } - } - - /// Chargement des animmations - if (animated) - { - NzAnimation* animation = new NzAnimation; - if (animation->Create(nzAnimationType_Keyframe, endFrame-startFrame+1)) - { - // Décodage des séquences - NzString frameName; - - NzSequence sequence; - sequence.framePerSecond = 10; // Par défaut pour les animations MD2 - - char name[16], last[16]; - stream.SetCursorPos(header.offset_frames + startFrame*header.framesize + offsetof(md2_frame, name)); - stream.Read(last, 16*sizeof(char)); - - int pos = std::strlen(last)-1; - for (unsigned int j = 0; j < 2; ++j) - { - if (!std::isdigit(last[pos])) - break; - - pos--; - } - last[pos+1] = '\0'; - - unsigned int numFrames = 0; - for (unsigned int i = startFrame; i <= endFrame; ++i) - { - stream.SetCursorPos(header.offset_frames + i*header.framesize + offsetof(md2_frame, name)); - stream.Read(name, 16*sizeof(char)); - - pos = std::strlen(name)-1; - for (unsigned int j = 0; j < 2; ++j) - { - if (!std::isdigit(name[pos])) - break; - - pos--; - } - name[pos+1] = '\0'; - - if (std::strcmp(name, last) != 0) // Si les deux frames n'ont pas le même nom - { - // Alors on enregistre la séquence - sequence.firstFrame = i-numFrames; - sequence.lastFrame = i-1; - sequence.name = last; - animation->AddSequence(sequence); - - std::strcpy(last, name); - - numFrames = 0; - } - - numFrames++; - } - - // On ajoute la dernière frame (Qui n'a pas été traitée par la boucle) - sequence.firstFrame = endFrame-numFrames; - sequence.lastFrame = endFrame; - sequence.name = last; - animation->AddSequence(sequence); - - mesh->SetAnimation(animation); - animation->SetPersistent(false); - } - else - NazaraInternalError("Failed to create animaton"); - } - - /// Chargement des submesh - // Actuellement le loader ne charge qu'un submesh - // TODO: Utiliser les commandes OpenGL pour accélérer le rendu - NzMD2Mesh* subMesh = new NzMD2Mesh(mesh); - if (!subMesh->Create(header, stream, parameters)) - { - NazaraError("Failed to create MD2 mesh"); - return false; - } - - mesh->AddSubMesh(subMesh); - - return true; - } -} - -void NzLoaders_MD2_Register() -{ - NzMD2Mesh::Initialize(); - - NzMeshLoader::RegisterLoader("md2", NzLoader_MD2_Check, NzLoader_MD2_Load); -} - -void NzLoaders_MD2_Unregister() -{ - NzMeshLoader::UnregisterLoader("md2", NzLoader_MD2_Check, NzLoader_MD2_Load); - - NzMD2Mesh::Uninitialize(); -} +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + bool NzLoader_MD2_Check(NzInputStream& stream, const NzMeshParams& parameters) + { + NazaraUnused(parameters); + + nzUInt32 magic[2]; + if (stream.Read(&magic[0], 2*sizeof(nzUInt32)) != 2*sizeof(nzUInt32)) + return false; + + #if defined(NAZARA_BIG_ENDIAN) + NzByteSwap(&magic[0], sizeof(nzUInt32)); + NzByteSwap(&magic[1], sizeof(nzUInt32)); + #endif + + return magic[0] == md2Ident && magic[1] == 8; + } + + bool NzLoader_MD2_Load(NzMesh* mesh, NzInputStream& stream, const NzMeshParams& parameters) + { + md2_header header; + if (stream.Read(&header, sizeof(md2_header)) != sizeof(md2_header)) + { + NazaraError("Failed to read header"); + return false; + } + + #if defined(NAZARA_BIG_ENDIAN) + NzByteSwap(&header.skinwidth, sizeof(nzUInt32)); + NzByteSwap(&header.skinheight, sizeof(nzUInt32)); + NzByteSwap(&header.framesize, sizeof(nzUInt32)); + NzByteSwap(&header.num_skins, sizeof(nzUInt32)); + NzByteSwap(&header.num_vertices, sizeof(nzUInt32)); + NzByteSwap(&header.num_st, sizeof(nzUInt32)); + NzByteSwap(&header.num_tris, sizeof(nzUInt32)); + NzByteSwap(&header.num_glcmds, sizeof(nzUInt32)); + NzByteSwap(&header.num_frames, sizeof(nzUInt32)); + NzByteSwap(&header.offset_skins, sizeof(nzUInt32)); + NzByteSwap(&header.offset_st, sizeof(nzUInt32)); + NzByteSwap(&header.offset_tris, sizeof(nzUInt32)); + NzByteSwap(&header.offset_frames, sizeof(nzUInt32)); + NzByteSwap(&header.offset_glcmds, sizeof(nzUInt32)); + NzByteSwap(&header.offset_end, sizeof(nzUInt32)); + #endif + + if (stream.GetSize() < header.offset_end) + { + NazaraError("Incomplete MD2 file"); + return false; + } + + /// Création du mesh + // Animé ou statique, c'est la question + bool animated; + unsigned int startFrame = NzClamp(parameters.animation.startFrame, 0U, static_cast(header.num_frames-1)); + unsigned int endFrame = NzClamp(parameters.animation.endFrame, 0U, static_cast(header.num_frames-1)); + + if (parameters.loadAnimations && startFrame != endFrame) + animated = true; + else + animated = false; + + if (!mesh->Create((animated) ? nzAnimationType_Keyframe : nzAnimationType_Static)) // Ne devrait jamais échouer + { + NazaraInternalError("Failed to create mesh"); + return false; + } + + /// Chargement des skins + if (header.num_skins > 0) + { + stream.SetCursorPos(header.offset_skins); + { + char skin[68]; + for (unsigned int i = 0; i < header.num_skins; ++i) + { + stream.Read(skin, 68*sizeof(char)); + mesh->AddSkin(skin); + } + } + } + + /// Chargement des animmations + if (animated) + { + NzAnimation* animation = new NzAnimation; + if (animation->Create(nzAnimationType_Keyframe, endFrame-startFrame+1)) + { + // Décodage des séquences + NzString frameName; + + NzSequence sequence; + sequence.framePerSecond = 10; // Par défaut pour les animations MD2 + + char name[16], last[16]; + stream.SetCursorPos(header.offset_frames + startFrame*header.framesize + offsetof(md2_frame, name)); + stream.Read(last, 16*sizeof(char)); + + int pos = std::strlen(last)-1; + for (unsigned int j = 0; j < 2; ++j) + { + if (!std::isdigit(last[pos])) + break; + + pos--; + } + last[pos+1] = '\0'; + + unsigned int numFrames = 0; + for (unsigned int i = startFrame; i <= endFrame; ++i) + { + stream.SetCursorPos(header.offset_frames + i*header.framesize + offsetof(md2_frame, name)); + stream.Read(name, 16*sizeof(char)); + + pos = std::strlen(name)-1; + for (unsigned int j = 0; j < 2; ++j) + { + if (!std::isdigit(name[pos])) + break; + + pos--; + } + name[pos+1] = '\0'; + + if (std::strcmp(name, last) != 0) // Si les deux frames n'ont pas le même nom + { + // Alors on enregistre la séquence + sequence.firstFrame = i-numFrames; + sequence.lastFrame = i-1; + sequence.name = last; + animation->AddSequence(sequence); + + std::strcpy(last, name); + + numFrames = 0; + } + + numFrames++; + } + + // On ajoute la dernière frame (Qui n'a pas été traitée par la boucle) + sequence.firstFrame = endFrame-numFrames; + sequence.lastFrame = endFrame; + sequence.name = last; + animation->AddSequence(sequence); + + mesh->SetAnimation(animation); + animation->SetPersistent(false); + } + else + NazaraInternalError("Failed to create animaton"); + } + + /// Chargement des submesh + // Actuellement le loader ne charge qu'un submesh + // TODO: Utiliser les commandes OpenGL pour accélérer le rendu + NzMD2Mesh* subMesh = new NzMD2Mesh(mesh); + if (!subMesh->Create(header, stream, parameters)) + { + NazaraError("Failed to create MD2 mesh"); + return false; + } + + mesh->AddSubMesh(subMesh); + + return true; + } +} + +void NzLoaders_MD2_Register() +{ + NzMD2Mesh::Initialize(); + + NzMeshLoader::RegisterLoader("md2", NzLoader_MD2_Check, NzLoader_MD2_Load); +} + +void NzLoaders_MD2_Unregister() +{ + NzMeshLoader::UnregisterLoader("md2", NzLoader_MD2_Check, NzLoader_MD2_Load); + + NzMD2Mesh::Uninitialize(); +} diff --git a/src/Nazara/Utility/Loaders/MD2/Mesh.cpp b/src/Nazara/Utility/Loaders/MD2/Mesh.cpp index 2d25e0db7..c4c311c3c 100644 --- a/src/Nazara/Utility/Loaders/MD2/Mesh.cpp +++ b/src/Nazara/Utility/Loaders/MD2/Mesh.cpp @@ -1,287 +1,292 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Utility module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include -#include - -NzMD2Mesh::NzMD2Mesh(const NzMesh* parent) : -NzKeyframeMesh(parent), -m_frames(nullptr), -m_indexBuffer(nullptr), -m_vertexBuffer(nullptr) -{ -} - -NzMD2Mesh::~NzMD2Mesh() -{ - Destroy(); -} - -bool NzMD2Mesh::Create(const md2_header& header, NzInputStream& stream, const NzMeshParams& parameters) -{ - Destroy(); - - unsigned int startFrame = NzClamp(parameters.animation.startFrame, 0U, static_cast(header.num_frames-1)); - unsigned int endFrame = NzClamp(parameters.animation.endFrame, 0U, static_cast(header.num_frames-1)); - - m_frameCount = endFrame - startFrame + 1; - m_vertexCount = header.num_tris*3; - - /// Chargement des vertices - std::vector texCoords(header.num_st); - std::vector triangles(header.num_tris); - - // Lecture des coordonnées de texture - stream.SetCursorPos(header.offset_st); - stream.Read(&texCoords[0], header.num_st*sizeof(md2_texCoord)); - - #if defined(NAZARA_BIG_ENDIAN) - for (unsigned int i = 0; i < header.num_st; ++i) - { - NzByteSwap(&texCoords[i].u, sizeof(nzInt16)); - NzByteSwap(&texCoords[i].v, sizeof(nzInt16)); - } - #endif - - stream.SetCursorPos(header.offset_tris); - stream.Read(&triangles[0], header.num_tris*sizeof(md2_triangle)); - - #if defined(NAZARA_BIG_ENDIAN) - for (unsigned int i = 0; i < header.num_tris; ++i) - { - NzByteSwap(&triangles[i].vertices[0], sizeof(nzUInt16)); - NzByteSwap(&texCoords[i].texCoords[0], sizeof(nzUInt16)); - - NzByteSwap(&triangles[i].vertices[1], sizeof(nzUInt16)); - NzByteSwap(&texCoords[i].texCoords[1], sizeof(nzUInt16)); - - NzByteSwap(&triangles[i].vertices[2], sizeof(nzUInt16)); - NzByteSwap(&texCoords[i].texCoords[2], sizeof(nzUInt16)); - } - #endif - - stream.SetCursorPos(header.offset_frames + header.framesize*startFrame); - - md2_frame frame; - frame.vertices.resize(header.num_vertices); - - // Pour que le modèle soit correctement aligné, on génère une matrice de rotation que nous appliquerons à chacune des vertices - NzMatrix4f rotationMatrix = NzMatrix4f::Rotate(NzEulerAnglesf(90.f, -90.f, 0.f)); - - unsigned int stride = s_declaration.GetStride(nzElementStream_VertexData); - - m_frames = new Frame[m_frameCount]; - for (unsigned int i = 0; i < m_frameCount; ++i) - { - stream.Read(&frame.scale, sizeof(NzVector3f)); - stream.Read(&frame.translate, sizeof(NzVector3f)); - stream.Read(&frame.name, 16*sizeof(char)); - stream.Read(&frame.vertices[0], header.num_vertices*sizeof(md2_vertex)); - - #if defined(NAZARA_BIG_ENDIAN) - NzByteSwap(&frame.scale.x, sizeof(float)); - NzByteSwap(&frame.scale.y, sizeof(float)); - NzByteSwap(&frame.scale.z, sizeof(float)); - - NzByteSwap(&frame.translate.x, sizeof(float)); - NzByteSwap(&frame.translate.y, sizeof(float)); - NzByteSwap(&frame.translate.z, sizeof(float)); - #endif - - m_frames[i].normal = new nzUInt8[m_vertexCount]; // Nous stockons l'indice de la normale plutôt que la normale (gain d'espace) - m_frames[i].vertices = new NzVector3f[m_vertexCount]; - - NzVector3f max, min; - for (unsigned int t = 0; t < header.num_tris; ++t) - { - for (unsigned int v = 0; v < 3; ++v) - { - const md2_vertex& vert = frame.vertices[triangles[t].vertices[v]]; - - NzVector3f vertex = rotationMatrix * NzVector3f(vert.x * frame.scale.x + frame.translate.x, vert.y * frame.scale.y + frame.translate.y, vert.z * frame.scale.z + frame.translate.z); - max.MakeCeil(vertex); - min.MakeFloor(vertex); - - m_frames[i].normal[t*3+v] = vert.n; - m_frames[i].vertices[t*3+v] = vertex; - } - } - - m_frames[i].aabb.SetExtends(min, max); - } - - m_indexBuffer = nullptr; // Pas d'indexbuffer pour l'instant - m_vertexBuffer = new NzVertexBuffer(m_vertexCount, (3+3+2)*sizeof(float), parameters.storage, nzBufferUsage_Dynamic); - - nzUInt8* ptr = reinterpret_cast(m_vertexBuffer->Map(nzBufferAccess_WriteOnly)); - if (!ptr) - { - NazaraError("Failed to map vertex buffer"); - Destroy(); - - return false; - } - - // On avance jusqu'aux premières coordonnées de texture - ptr += s_declaration.GetElement(nzElementStream_VertexData, nzElementUsage_TexCoord)->offset; - for (unsigned int t = 0; t < header.num_tris; ++t) - { - for (unsigned int v = 0; v < 3; ++v) - { - const md2_texCoord& texC = texCoords[triangles[t].texCoords[v]]; - - NzVector2f* coords = reinterpret_cast(ptr); - coords->x = texC.u / static_cast(header.skinwidth); - coords->y = 1.f - texC.v / static_cast(header.skinheight); - - ptr += stride; - } - } - - if (!m_vertexBuffer->Unmap()) - { - NazaraError("Failed to unmap buffer"); - Destroy(); - - return false; - } - - m_vertexBuffer->AddResourceReference(); - m_vertexBuffer->SetPersistent(false); - - AnimateImpl(0, 0, 0.f); - - return true; -} - -void NzMD2Mesh::Destroy() -{ - if (m_frames) - { - for (unsigned int i = 0; i < m_frameCount; ++i) - { - delete[] m_frames[i].normal; - delete[] m_frames[i].vertices; - } - - delete[] m_frames; - m_frames = nullptr; - } - - if (m_indexBuffer) - { - m_indexBuffer->RemoveResourceReference(); - m_indexBuffer = nullptr; - } - - if (m_vertexBuffer) - { - m_vertexBuffer->RemoveResourceReference(); - m_vertexBuffer = nullptr; - } -} - -const NzAxisAlignedBox& NzMD2Mesh::GetAABB() const -{ - return m_aabb; -} - -nzAnimationType NzMD2Mesh::GetAnimationType() const -{ - if (m_frameCount > 1) - return nzAnimationType_Keyframe; - else - return nzAnimationType_Static; -} - -unsigned int NzMD2Mesh::GetFrameCount() const -{ - return m_frameCount; -} - -const NzIndexBuffer* NzMD2Mesh::GetIndexBuffer() const -{ - return nullptr; - //return m_indexBuffer; -} - -nzPrimitiveType NzMD2Mesh::GetPrimitiveType() const -{ - return nzPrimitiveType_TriangleList; -} - -const NzVertexBuffer* NzMD2Mesh::GetVertexBuffer() const -{ - return m_vertexBuffer; -} - -const NzVertexDeclaration* NzMD2Mesh::GetVertexDeclaration() const -{ - return &s_declaration; -} - -void NzMD2Mesh::Initialize() -{ - NzVertexElement elements[3]; - elements[0].offset = 0; - elements[0].type = nzElementType_Float3; - elements[0].usage = nzElementUsage_Position; - - elements[1].offset = 3*sizeof(float); - elements[1].type = nzElementType_Float3; - elements[1].usage = nzElementUsage_Normal; - - elements[2].offset = 3*sizeof(float) + 3*sizeof(float); - elements[2].type = nzElementType_Float2; - elements[2].usage = nzElementUsage_TexCoord; - - s_declaration.Create(elements, 3); -} - -void NzMD2Mesh::Uninitialize() -{ - s_declaration.Destroy(); -} - -void NzMD2Mesh::AnimateImpl(unsigned int frameA, unsigned int frameB, float interpolation) -{ - nzUInt8* ptr = reinterpret_cast(m_vertexBuffer->Map(nzBufferAccess_WriteOnly)); - if (!ptr) - { - NazaraError("Failed to map vertex buffer"); - return; - } - - unsigned int stride = s_declaration.GetStride(nzElementStream_VertexData); - unsigned int positionOffset = s_declaration.GetElement(nzElementStream_VertexData, nzElementUsage_Position)->offset; - unsigned int normalOffset = s_declaration.GetElement(nzElementStream_VertexData, nzElementUsage_Normal)->offset; - - Frame* fA = &m_frames[frameA]; - Frame* fB = &m_frames[frameB]; - for (unsigned int i = 0; i < m_vertexCount; ++i) - { - NzVector3f* position = reinterpret_cast(ptr + positionOffset); - NzVector3f* normal = reinterpret_cast(ptr + normalOffset); - - *position = fA->vertices[i] + interpolation * (fB->vertices[i] - fA->vertices[i]); - *normal = md2Normals[fA->normal[i]] + interpolation * (md2Normals[fB->normal[i]] - md2Normals[fA->normal[i]]); - - ptr += stride; - } - - if (!m_vertexBuffer->Unmap()) - NazaraWarning("Failed to unmap vertex buffer, expect mesh corruption"); - - // Interpolation de l'AABB - NzVector3f max1 = fA->aabb.GetMaximum(); - NzVector3f min1 = fA->aabb.GetMinimum(); - m_aabb.SetExtends(min1 + interpolation * (fB->aabb.GetMinimum() - min1), max1 + interpolation * (fB->aabb.GetMaximum() - max1)); -} - -NzVertexDeclaration NzMD2Mesh::s_declaration; +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include + +NzMD2Mesh::NzMD2Mesh(const NzMesh* parent) : +NzKeyframeMesh(parent), +m_frames(nullptr), +m_indexBuffer(nullptr), +m_vertexBuffer(nullptr) +{ +} + +NzMD2Mesh::~NzMD2Mesh() +{ + Destroy(); +} + +bool NzMD2Mesh::Create(const md2_header& header, NzInputStream& stream, const NzMeshParams& parameters) +{ + Destroy(); + + unsigned int startFrame = NzClamp(parameters.animation.startFrame, 0U, static_cast(header.num_frames-1)); + unsigned int endFrame = NzClamp(parameters.animation.endFrame, 0U, static_cast(header.num_frames-1)); + + m_frameCount = endFrame - startFrame + 1; + m_vertexCount = header.num_tris * 3; + + /// Chargement des vertices + std::vector texCoords(header.num_st); + std::vector triangles(header.num_tris); + + // Lecture des coordonnées de texture + stream.SetCursorPos(header.offset_st); + stream.Read(&texCoords[0], header.num_st*sizeof(md2_texCoord)); + + #if defined(NAZARA_BIG_ENDIAN) + for (unsigned int i = 0; i < header.num_st; ++i) + { + NzByteSwap(&texCoords[i].u, sizeof(nzInt16)); + NzByteSwap(&texCoords[i].v, sizeof(nzInt16)); + } + #endif + + stream.SetCursorPos(header.offset_tris); + stream.Read(&triangles[0], header.num_tris*sizeof(md2_triangle)); + + #if defined(NAZARA_BIG_ENDIAN) + for (unsigned int i = 0; i < header.num_tris; ++i) + { + NzByteSwap(&triangles[i].vertices[0], sizeof(nzUInt16)); + NzByteSwap(&texCoords[i].texCoords[0], sizeof(nzUInt16)); + + NzByteSwap(&triangles[i].vertices[1], sizeof(nzUInt16)); + NzByteSwap(&texCoords[i].texCoords[1], sizeof(nzUInt16)); + + NzByteSwap(&triangles[i].vertices[2], sizeof(nzUInt16)); + NzByteSwap(&texCoords[i].texCoords[2], sizeof(nzUInt16)); + } + #endif + + stream.SetCursorPos(header.offset_frames + header.framesize*startFrame); + + md2_frame frame; + frame.vertices.resize(header.num_vertices); + + // Pour que le modèle soit correctement aligné, on génère un quaternion que nous appliquerons à chacune des vertices + NzQuaternionf rotationQuat = NzEulerAnglesf(-90.f, 90.f, 0.f); + //NzMatrix4f rotationMatrix = NzMatrix4f::Rotate(NzEulerAnglesf(-90.f, -90.f, 0.f)); + + unsigned int stride = s_declaration.GetStride(nzElementStream_VertexData); + + m_frames = new Frame[m_frameCount]; + for (unsigned int i = 0; i < m_frameCount; ++i) + { + stream.Read(&frame.scale, sizeof(NzVector3f)); + stream.Read(&frame.translate, sizeof(NzVector3f)); + stream.Read(&frame.name, 16*sizeof(char)); + stream.Read(&frame.vertices[0], header.num_vertices*sizeof(md2_vertex)); + + #if defined(NAZARA_BIG_ENDIAN) + NzByteSwap(&frame.scale.x, sizeof(float)); + NzByteSwap(&frame.scale.y, sizeof(float)); + NzByteSwap(&frame.scale.z, sizeof(float)); + + NzByteSwap(&frame.translate.x, sizeof(float)); + NzByteSwap(&frame.translate.y, sizeof(float)); + NzByteSwap(&frame.translate.z, sizeof(float)); + #endif + + m_frames[i].normal = new nzUInt8[m_vertexCount]; // Nous stockons l'indice MD2 de la normale plutôt que la normale (gain d'espace) + m_frames[i].vertices = new NzVector3f[m_vertexCount]; + + NzVector3f max, min; + for (unsigned int t = 0; t < header.num_tris; ++t) + { + for (unsigned int v = 0; v < 3; ++v) + { + const md2_vertex& vert = frame.vertices[triangles[t].vertices[v]]; + + NzVector3f vertex = rotationQuat * NzVector3f(vert.x * frame.scale.x + frame.translate.x, vert.y * frame.scale.y + frame.translate.y, vert.z * frame.scale.z + frame.translate.z); + + // On fait en sorte d'avoir deux vertices de délimitation, définissant un rectangle dans l'espace + max.Maximize(vertex); + min.Minimize(vertex); + + // Le MD2 ne définit pas ses vertices dans le bon ordre, il nous faut donc les ajouter dans l'ordre inverse + unsigned int index = m_vertexCount - (t*3 + v) - 1; + m_frames[i].normal[index] = vert.n; + m_frames[i].vertices[index] = vertex; + } + } + + m_frames[i].aabb.SetExtends(min, max); + } + + m_indexBuffer = nullptr; // Pas d'indexbuffer pour l'instant + m_vertexBuffer = new NzVertexBuffer(m_vertexCount, (3+3+2)*sizeof(float), parameters.storage, nzBufferUsage_Dynamic); + + nzUInt8* ptr = reinterpret_cast(m_vertexBuffer->Map(nzBufferAccess_WriteOnly)); + if (!ptr) + { + NazaraError("Failed to map vertex buffer"); + Destroy(); + + return false; + } + + // On avance jusqu'aux dernières coordonnées de texture et on les définit dans l'ordre inverse + ptr += s_declaration.GetElement(nzElementStream_VertexData, nzElementUsage_TexCoord)->offset + stride * (m_vertexCount-1); + for (unsigned int t = 0; t < header.num_tris; ++t) + { + for (unsigned int v = 0; v < 3; ++v) + { + const md2_texCoord& texC = texCoords[triangles[t].texCoords[v]]; + + NzVector2f* coords = reinterpret_cast(ptr); + coords->x = texC.u / static_cast(header.skinwidth); + coords->y = 1.f - texC.v / static_cast(header.skinheight); + + ptr -= stride; + } + } + + if (!m_vertexBuffer->Unmap()) + { + NazaraError("Failed to unmap buffer"); + Destroy(); + + return false; + } + + m_vertexBuffer->AddResourceReference(); + m_vertexBuffer->SetPersistent(false); + + AnimateImpl(0, 0, 0.f); + + return true; +} + +void NzMD2Mesh::Destroy() +{ + if (m_frames) + { + for (unsigned int i = 0; i < m_frameCount; ++i) + { + delete[] m_frames[i].normal; + delete[] m_frames[i].vertices; + } + + delete[] m_frames; + m_frames = nullptr; + } + + if (m_indexBuffer) + { + m_indexBuffer->RemoveResourceReference(); + m_indexBuffer = nullptr; + } + + if (m_vertexBuffer) + { + m_vertexBuffer->RemoveResourceReference(); + m_vertexBuffer = nullptr; + } +} + +const NzAxisAlignedBox& NzMD2Mesh::GetAABB() const +{ + return m_aabb; +} + +nzAnimationType NzMD2Mesh::GetAnimationType() const +{ + if (m_frameCount > 1) + return nzAnimationType_Keyframe; + else + return nzAnimationType_Static; +} + +unsigned int NzMD2Mesh::GetFrameCount() const +{ + return m_frameCount; +} + +const NzIndexBuffer* NzMD2Mesh::GetIndexBuffer() const +{ + return nullptr; + //return m_indexBuffer; +} + +nzPrimitiveType NzMD2Mesh::GetPrimitiveType() const +{ + return nzPrimitiveType_TriangleList; +} + +const NzVertexBuffer* NzMD2Mesh::GetVertexBuffer() const +{ + return m_vertexBuffer; +} + +const NzVertexDeclaration* NzMD2Mesh::GetVertexDeclaration() const +{ + return &s_declaration; +} + +void NzMD2Mesh::Initialize() +{ + NzVertexElement elements[3]; + elements[0].offset = 0; + elements[0].type = nzElementType_Float3; + elements[0].usage = nzElementUsage_Position; + + elements[1].offset = 3*sizeof(float); + elements[1].type = nzElementType_Float3; + elements[1].usage = nzElementUsage_Normal; + + elements[2].offset = 3*sizeof(float) + 3*sizeof(float); + elements[2].type = nzElementType_Float2; + elements[2].usage = nzElementUsage_TexCoord; + + s_declaration.Create(elements, 3); +} + +void NzMD2Mesh::Uninitialize() +{ + s_declaration.Destroy(); +} + +void NzMD2Mesh::AnimateImpl(unsigned int frameA, unsigned int frameB, float interpolation) +{ + nzUInt8* ptr = reinterpret_cast(m_vertexBuffer->Map(nzBufferAccess_WriteOnly)); + if (!ptr) + { + NazaraError("Failed to map vertex buffer"); + return; + } + + unsigned int stride = s_declaration.GetStride(nzElementStream_VertexData); + unsigned int positionOffset = s_declaration.GetElement(nzElementStream_VertexData, nzElementUsage_Position)->offset; + unsigned int normalOffset = s_declaration.GetElement(nzElementStream_VertexData, nzElementUsage_Normal)->offset; + + Frame* fA = &m_frames[frameA]; + Frame* fB = &m_frames[frameB]; + for (unsigned int i = 0; i < m_vertexCount; ++i) + { + NzVector3f* position = reinterpret_cast(ptr + positionOffset); + NzVector3f* normal = reinterpret_cast(ptr + normalOffset); + + *position = fA->vertices[i] + interpolation * (fB->vertices[i] - fA->vertices[i]); + *normal = md2Normals[fA->normal[i]] + interpolation * (md2Normals[fB->normal[i]] - md2Normals[fA->normal[i]]); + + ptr += stride; + } + + if (!m_vertexBuffer->Unmap()) + NazaraWarning("Failed to unmap vertex buffer, expect mesh corruption"); + + // Interpolation de l'AABB + NzVector3f max1 = fA->aabb.GetMaximum(); + NzVector3f min1 = fA->aabb.GetMinimum(); + m_aabb.SetExtends(min1 + interpolation * (fB->aabb.GetMinimum() - min1), max1 + interpolation * (fB->aabb.GetMaximum() - max1)); +} + +NzVertexDeclaration NzMD2Mesh::s_declaration; diff --git a/src/Nazara/Utility/Loaders/PCX/Loader.cpp b/src/Nazara/Utility/Loaders/PCX/Loader.cpp index 74fc38725..b6b5f437a 100644 --- a/src/Nazara/Utility/Loaders/PCX/Loader.cpp +++ b/src/Nazara/Utility/Loaders/PCX/Loader.cpp @@ -1,352 +1,346 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Utility module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include -#include -#include - -// Auteur du loader original : David Henry - -namespace -{ - struct pcx_header - { - nzUInt8 manufacturer; - nzUInt8 version; - nzUInt8 encoding; - nzUInt8 bitsPerPixel; - - nzUInt16 xmin, ymin; - nzUInt16 xmax, ymax; - nzUInt16 horzRes, vertRes; - - nzUInt8 palette[48]; - nzUInt8 reserved; - nzUInt8 numColorPlanes; - - nzUInt16 bytesPerScanLine; - nzUInt16 paletteType; - nzUInt16 horzSize, vertSize; - - nzUInt8 padding[54]; - }; - - bool NzLoader_PCX_Check(NzInputStream& stream, const NzImageParams& parameters) - { - NazaraUnused(parameters); - - nzUInt8 manufacturer; - if (stream.Read(&manufacturer, 1) != 1) - return false; - - return manufacturer == 0x0a; - } - - bool NzLoader_PCX_Load(NzImage* image, NzInputStream& stream, const NzImageParams& parameters) - { - NazaraUnused(parameters); - - pcx_header header; - if (stream.Read(&header, sizeof(pcx_header)) != sizeof(pcx_header)) - { - NazaraError("Failed to read header"); - return false; - } - - if (header.manufacturer != 0x0a) - { - NazaraError("Bad version number (" + NzString::Number(header.manufacturer) + ')'); - return false; - } - - #if defined(NAZARA_BIG_ENDIAN) - // Les fichiers PCX sont en little endian - NzByteSwap(&header.xmin, sizeof(nzUInt16)); - NzByteSwap(&header.ymin, sizeof(nzUInt16)); - NzByteSwap(&header.xmax, sizeof(nzUInt16)); - NzByteSwap(&header.ymax, sizeof(nzUInt16)); - NzByteSwap(&header.horzRes, sizeof(nzUInt16)); - NzByteSwap(&header.vertRes, sizeof(nzUInt16)); - - NzByteSwap(&header.bytesPerScanLine, sizeof(nzUInt16)); - NzByteSwap(&header.paletteType, sizeof(nzUInt16)); - NzByteSwap(&header.horzSize, sizeof(nzUInt16)); - NzByteSwap(&header.vertSize, sizeof(nzUInt16)); - #endif - - unsigned int bitCount = header.bitsPerPixel * header.numColorPlanes; - unsigned int width = header.xmax - header.xmin+1; - unsigned int height = header.ymax - header.ymin+1; - - if (!image->Create(nzImageType_2D, nzPixelFormat_RGB8, width, height, 1, (parameters.levelCount > 0) ? parameters.levelCount : 1)) - { - NazaraError("Failed to create image"); - return false; - } - - nzUInt8* pixels = image->GetPixels(); - - int rle_value = 0; - unsigned int rle_count = 0; - - switch (bitCount) - { - case 1: - { - for (unsigned int y = 0; y < height; ++y) - { - nzUInt8* ptr = &pixels[y * width * 3]; - int bytes = header.bytesPerScanLine; - - /* decode line number y */ - while (bytes--) - { - if (rle_count == 0) - { - if (!stream.Read(&rle_value, 1)) - { - NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')'); - return false; - } - - if (rle_value < 0xc0) - rle_count = 1; - else - { - rle_count = rle_value - 0xc0; - if (!stream.Read(&rle_value, 1)) - { - NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')'); - return false; - } - } - } - - rle_count--; - - for (int i = 7; i >= 0; --i) - { - int colorIndex = ((rle_value & (1 << i)) > 0); - - *ptr++ = header.palette[colorIndex * 3 + 0]; - *ptr++ = header.palette[colorIndex * 3 + 1]; - *ptr++ = header.palette[colorIndex * 3 + 2]; - } - } - } - break; - } - - case 4: - { - nzUInt8* colorIndex = new nzUInt8[width]; - nzUInt8* line = new nzUInt8[header.bytesPerScanLine]; - - for (unsigned int y = 0; y < height; ++y) - { - nzUInt8* ptr = &pixels[y * width * 3]; - - std::memset(colorIndex, 0, width); - - for (unsigned int c = 0; c < 4; ++c) - { - nzUInt8* pLine = line; - int bytes = header.bytesPerScanLine; - - /* decode line number y */ - while (bytes--) - { - if (rle_count == 0) - { - if (!stream.Read(&rle_value, 1)) - { - NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')'); - delete[] colorIndex; - delete[] line; - - return false; - } - - if (rle_value < 0xc0) - rle_count = 1; - else - { - rle_count = rle_value - 0xc0; - if (!stream.Read(&rle_value, 1)) - { - NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')'); - delete[] colorIndex; - delete[] line; - - return false; - } - } - } - - rle_count--; - *(pLine++) = rle_value; - } - - /* compute line's color indexes */ - for (unsigned int x = 0; x < width; ++x) - { - if (line[x / 8] & (128 >> (x % 8))) - colorIndex[x] += (1 << c); - } - } - - /* decode scan line. color index => rgb */ - for (unsigned int x = 0; x < width; ++x) - { - *ptr++ = header.palette[colorIndex[x] * 3 + 0]; - *ptr++ = header.palette[colorIndex[x] * 3 + 1]; - *ptr++ = header.palette[colorIndex[x] * 3 + 2]; - } - } - - /* release memory */ - delete[] colorIndex; - delete[] line; - break; - } - - case 8: - { - nzUInt8 palette[768]; - - /* the palette is contained in the last 769 bytes of the file */ - unsigned int curPos = stream.GetCursorPos(); - stream.SetCursorPos(stream.GetSize()-769); - nzUInt8 magic; - if (!stream.Read(&magic, 1)) - { - NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')'); - return false; - } - - /* first byte must be equal to 0x0c (12) */ - if (magic != 0x0c) - { - NazaraError("Colormap's first byte must be 0x0c (0x" + NzString::Number(magic, 16) + ')'); - return false; - } - - /* read palette */ - if (stream.Read(palette, 768) != 768) - { - NazaraError("Failed to read palette"); - return false; - } - - stream.SetCursorPos(curPos); - - /* read pixel data */ - for (unsigned int y = 0; y < height; ++y) - { - nzUInt8* ptr = &pixels[y * width * 3]; - int bytes = header.bytesPerScanLine; - - /* decode line number y */ - while (bytes--) - { - if (rle_count == 0) - { - if (!stream.Read(&rle_value, 1)) - { - NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')'); - return false; - } - - if (rle_value < 0xc0) - rle_count = 1; - else - { - rle_count = rle_value - 0xc0; - if (!stream.Read(&rle_value, 1)) - { - NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')'); - return false; - } - } - } - - rle_count--; - - *ptr++ = palette[rle_value * 3 + 0]; - *ptr++ = palette[rle_value * 3 + 1]; - *ptr++ = palette[rle_value * 3 + 2]; - } - } - break; - } - - case 24: - { - for (unsigned int y = 0; y < height; ++y) - { - /* for each color plane */ - for (int c = 0; c < 3; ++c) - { - nzUInt8* ptr = &pixels[y * width * 4]; - int bytes = header.bytesPerScanLine; - - /* decode line number y */ - while (bytes--) - { - if (rle_count == 0) - { - if (!stream.Read(&rle_value, 1)) - { - NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')'); - return false; - } - - if (rle_value < 0xc0) - rle_count = 1; - else - { - rle_count = rle_value - 0xc0; - if (!stream.Read(&rle_value, 1)) - { - NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')'); - return false; - } - } - } - - rle_count--; - ptr[c] = static_cast(rle_value); - ptr += 3; - } - } - } - break; - } - - default: - NazaraError("Failed to load " + NzString::Number(bitCount) + " bitcount pcx files"); - return false; - } - - if (parameters.loadFormat != nzPixelFormat_Undefined) - image->Convert(parameters.loadFormat); - - return true; - } -} - -void NzLoaders_PCX_Register() -{ - NzImageLoader::RegisterLoader("pcx", NzLoader_PCX_Check, NzLoader_PCX_Load); -} - -void NzLoaders_PCX_Unregister() -{ - NzImageLoader::UnregisterLoader("pcx", NzLoader_PCX_Check, NzLoader_PCX_Load); -} +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include +#include + +// Auteur du loader original : David Henry + +namespace +{ + struct pcx_header + { + nzUInt8 manufacturer; + nzUInt8 version; + nzUInt8 encoding; + nzUInt8 bitsPerPixel; + + nzUInt16 xmin, ymin; + nzUInt16 xmax, ymax; + nzUInt16 horzRes, vertRes; + + nzUInt8 palette[48]; + nzUInt8 reserved; + nzUInt8 numColorPlanes; + + nzUInt16 bytesPerScanLine; + nzUInt16 paletteType; + nzUInt16 horzSize, vertSize; + + nzUInt8 padding[54]; + }; + + bool NzLoader_PCX_Check(NzInputStream& stream, const NzImageParams& parameters) + { + NazaraUnused(parameters); + + nzUInt8 manufacturer; + if (stream.Read(&manufacturer, 1) != 1) + return false; + + return manufacturer == 0x0a; + } + + bool NzLoader_PCX_Load(NzImage* image, NzInputStream& stream, const NzImageParams& parameters) + { + NazaraUnused(parameters); + + pcx_header header; + if (stream.Read(&header, sizeof(pcx_header)) != sizeof(pcx_header)) + { + NazaraError("Failed to read header"); + return false; + } + + #if defined(NAZARA_BIG_ENDIAN) + // Les fichiers PCX sont en little endian + NzByteSwap(&header.xmin, sizeof(nzUInt16)); + NzByteSwap(&header.ymin, sizeof(nzUInt16)); + NzByteSwap(&header.xmax, sizeof(nzUInt16)); + NzByteSwap(&header.ymax, sizeof(nzUInt16)); + NzByteSwap(&header.horzRes, sizeof(nzUInt16)); + NzByteSwap(&header.vertRes, sizeof(nzUInt16)); + + NzByteSwap(&header.bytesPerScanLine, sizeof(nzUInt16)); + NzByteSwap(&header.paletteType, sizeof(nzUInt16)); + NzByteSwap(&header.horzSize, sizeof(nzUInt16)); + NzByteSwap(&header.vertSize, sizeof(nzUInt16)); + #endif + + unsigned int bitCount = header.bitsPerPixel * header.numColorPlanes; + unsigned int width = header.xmax - header.xmin+1; + unsigned int height = header.ymax - header.ymin+1; + + if (!image->Create(nzImageType_2D, nzPixelFormat_RGB8, width, height, 1, (parameters.levelCount > 0) ? parameters.levelCount : 1)) + { + NazaraError("Failed to create image"); + return false; + } + + nzUInt8* pixels = image->GetPixels(); + + int rle_value = 0; + unsigned int rle_count = 0; + + switch (bitCount) + { + case 1: + { + for (unsigned int y = 0; y < height; ++y) + { + nzUInt8* ptr = &pixels[y * width * 3]; + int bytes = header.bytesPerScanLine; + + /* decode line number y */ + while (bytes--) + { + if (rle_count == 0) + { + if (!stream.Read(&rle_value, 1)) + { + NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')'); + return false; + } + + if (rle_value < 0xc0) + rle_count = 1; + else + { + rle_count = rle_value - 0xc0; + if (!stream.Read(&rle_value, 1)) + { + NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')'); + return false; + } + } + } + + rle_count--; + + for (int i = 7; i >= 0; --i) + { + int colorIndex = ((rle_value & (1 << i)) > 0); + + *ptr++ = header.palette[colorIndex * 3 + 0]; + *ptr++ = header.palette[colorIndex * 3 + 1]; + *ptr++ = header.palette[colorIndex * 3 + 2]; + } + } + } + break; + } + + case 4: + { + nzUInt8* colorIndex = new nzUInt8[width]; + nzUInt8* line = new nzUInt8[header.bytesPerScanLine]; + + for (unsigned int y = 0; y < height; ++y) + { + nzUInt8* ptr = &pixels[y * width * 3]; + + std::memset(colorIndex, 0, width); + + for (unsigned int c = 0; c < 4; ++c) + { + nzUInt8* pLine = line; + int bytes = header.bytesPerScanLine; + + /* decode line number y */ + while (bytes--) + { + if (rle_count == 0) + { + if (!stream.Read(&rle_value, 1)) + { + NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')'); + delete[] colorIndex; + delete[] line; + + return false; + } + + if (rle_value < 0xc0) + rle_count = 1; + else + { + rle_count = rle_value - 0xc0; + if (!stream.Read(&rle_value, 1)) + { + NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')'); + delete[] colorIndex; + delete[] line; + + return false; + } + } + } + + rle_count--; + *(pLine++) = rle_value; + } + + /* compute line's color indexes */ + for (unsigned int x = 0; x < width; ++x) + { + if (line[x / 8] & (128 >> (x % 8))) + colorIndex[x] += (1 << c); + } + } + + /* decode scan line. color index => rgb */ + for (unsigned int x = 0; x < width; ++x) + { + *ptr++ = header.palette[colorIndex[x] * 3 + 0]; + *ptr++ = header.palette[colorIndex[x] * 3 + 1]; + *ptr++ = header.palette[colorIndex[x] * 3 + 2]; + } + } + + /* release memory */ + delete[] colorIndex; + delete[] line; + break; + } + + case 8: + { + nzUInt8 palette[768]; + + /* the palette is contained in the last 769 bytes of the file */ + unsigned int curPos = stream.GetCursorPos(); + stream.SetCursorPos(stream.GetSize()-769); + nzUInt8 magic; + if (!stream.Read(&magic, 1)) + { + NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')'); + return false; + } + + /* first byte must be equal to 0x0c (12) */ + if (magic != 0x0c) + { + NazaraError("Colormap's first byte must be 0x0c (0x" + NzString::Number(magic, 16) + ')'); + return false; + } + + /* read palette */ + if (stream.Read(palette, 768) != 768) + { + NazaraError("Failed to read palette"); + return false; + } + + stream.SetCursorPos(curPos); + + /* read pixel data */ + for (unsigned int y = 0; y < height; ++y) + { + nzUInt8* ptr = &pixels[y * width * 3]; + int bytes = header.bytesPerScanLine; + + /* decode line number y */ + while (bytes--) + { + if (rle_count == 0) + { + if (!stream.Read(&rle_value, 1)) + { + NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')'); + return false; + } + + if (rle_value < 0xc0) + rle_count = 1; + else + { + rle_count = rle_value - 0xc0; + if (!stream.Read(&rle_value, 1)) + { + NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')'); + return false; + } + } + } + + rle_count--; + + *ptr++ = palette[rle_value * 3 + 0]; + *ptr++ = palette[rle_value * 3 + 1]; + *ptr++ = palette[rle_value * 3 + 2]; + } + } + break; + } + + case 24: + { + for (unsigned int y = 0; y < height; ++y) + { + /* for each color plane */ + for (int c = 0; c < 3; ++c) + { + nzUInt8* ptr = &pixels[y * width * 4]; + int bytes = header.bytesPerScanLine; + + /* decode line number y */ + while (bytes--) + { + if (rle_count == 0) + { + if (!stream.Read(&rle_value, 1)) + { + NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')'); + return false; + } + + if (rle_value < 0xc0) + rle_count = 1; + else + { + rle_count = rle_value - 0xc0; + if (!stream.Read(&rle_value, 1)) + { + NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')'); + return false; + } + } + } + + rle_count--; + ptr[c] = static_cast(rle_value); + ptr += 3; + } + } + } + break; + } + + default: + NazaraError("Failed to load " + NzString::Number(bitCount) + " bitcount pcx files"); + return false; + } + + if (parameters.loadFormat != nzPixelFormat_Undefined) + image->Convert(parameters.loadFormat); + + return true; + } +} + +void NzLoaders_PCX_Register() +{ + NzImageLoader::RegisterLoader("pcx", NzLoader_PCX_Check, NzLoader_PCX_Load); +} + +void NzLoaders_PCX_Unregister() +{ + NzImageLoader::UnregisterLoader("pcx", NzLoader_PCX_Check, NzLoader_PCX_Load); +} diff --git a/src/Nazara/Utility/Mesh.cpp b/src/Nazara/Utility/Mesh.cpp index 797c0bcf9..7a8845dc7 100644 --- a/src/Nazara/Utility/Mesh.cpp +++ b/src/Nazara/Utility/Mesh.cpp @@ -1,623 +1,681 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Utility module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -bool NzMeshParams::IsValid() const -{ - if (!animation.IsValid()) - { - NazaraError("Invalid animation parameters"); - return false; - } - - if (!NzBuffer::IsSupported(storage)) - { - NazaraError("Storage not supported"); - return false; - } - - return true; -} - -struct NzMeshImpl -{ - std::map subMeshMap; - std::deque skins; - std::vector subMeshes; - nzAnimationType animationType; - NzAxisAlignedBox aabb; - const NzAnimation* animation = nullptr; -}; - -NzMesh::~NzMesh() -{ - Destroy(); -} - -unsigned int NzMesh::AddSkin(const NzString& skin, bool setDefault) -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Mesh not created"); - return 0; - } - - if (skin.IsEmpty()) - { - NazaraError("Skin is empty"); - return 0; - } - #endif - - if (setDefault) - m_impl->skins.push_front(skin); - else - m_impl->skins.push_back(skin); - - return m_impl->skins.size()-1; -} - -nzUInt8 NzMesh::AddSubMesh(NzSubMesh* subMesh) -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Mesh not created"); - return 0; - } - - if (!subMesh) - { - NazaraError("Invalid submesh"); - return 0; - } - #endif - - subMesh->AddResourceReference(); - - m_impl->aabb.SetNull(); // On invalide l'AABB - m_impl->subMeshes.push_back(subMesh); - - return m_impl->subMeshes.size()-1; -} - -nzUInt8 NzMesh::AddSubMesh(const NzString& identifier, NzSubMesh* subMesh) -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Mesh not created"); - return 0; - } - - if (identifier.IsEmpty()) - { - NazaraError("Identifier is empty"); - return 0; - } - - auto it = m_impl->subMeshMap.find(identifier); - if (it != m_impl->subMeshMap.end()) - { - NazaraError("SubMesh identifier \"" + identifier + "\" is already used"); - return it->second; - } - - if (!subMesh) - { - NazaraError("Invalid submesh"); - return 0; - } - #endif - - nzUInt8 index = m_impl->subMeshes.size(); - - subMesh->AddResourceReference(); - - m_impl->aabb.SetNull(); // On invalide l'AABB - m_impl->subMeshes.push_back(subMesh); - m_impl->subMeshMap[identifier] = index; - - return index; -} - -void NzMesh::Animate(unsigned int frameA, unsigned int frameB, float interpolation) -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Mesh not created"); - return; - } - - if (!m_impl->animation) - { - NazaraError("Mesh has no animation"); - return; - } - - unsigned int frameCount = m_impl->animation->GetFrameCount(); - if (frameA >= frameCount) - { - NazaraError("Frame A is out of range (" + NzString::Number(frameA) + " >= " + NzString::Number(frameCount) + ')'); - return; - } - - if (frameB >= frameCount) - { - NazaraError("Frame B is out of range (" + NzString::Number(frameB) + " >= " + NzString::Number(frameCount) + ')'); - return; - } - - if (interpolation < 0.f || interpolation > 1.f) - { - NazaraError("Interpolation must be in range [0..1] (Got " + NzString::Number(interpolation) + ')'); - return; - } - #endif - - for (NzSubMesh* subMesh : m_impl->subMeshes) - subMesh->AnimateImpl(frameA, frameB, interpolation); - - m_impl->aabb.SetNull(); // On invalide l'AABB -} - -bool NzMesh::Create(nzAnimationType type) -{ - Destroy(); - - m_impl = new NzMeshImpl; - m_impl->animationType = type; - - return true; -} - -void NzMesh::Destroy() -{ - if (m_impl) - { - if (m_impl->animation) - m_impl->animation->RemoveResourceReference(); - - for (NzSubMesh* subMesh : m_impl->subMeshes) - subMesh->RemoveResourceReference(); - - delete m_impl; - m_impl = nullptr; - } -} - -const NzAxisAlignedBox& NzMesh::GetAABB() const -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Mesh not created"); - return NzAxisAlignedBox::Null; - } - #endif - - if (m_impl->aabb.IsNull()) - { - for (NzSubMesh* subMesh : m_impl->subMeshes) - m_impl->aabb.ExtendTo(subMesh->GetAABB()); - } - - return m_impl->aabb; -} - -const NzAnimation* NzMesh::GetAnimation() const -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Mesh not created"); - return nullptr; - } - #endif - - return m_impl->animation; -} - -nzAnimationType NzMesh::GetAnimationType() const -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Mesh not created"); - return nzAnimationType_Static; - } - #endif - - return m_impl->animationType; -} - -unsigned int NzMesh::GetFrameCount() const -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Mesh not created"); - return 0; - } - - if (!m_impl->animation) - { - NazaraError("Mesh has no animation"); - return 0; - } - #endif - - return m_impl->animation->GetFrameCount(); -} - -NzString NzMesh::GetSkin(unsigned int index) const -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Mesh not created"); - return NzString(); - } - - if (index >= m_impl->skins.size()) - { - NazaraError("Skin index out of range (" + NzString::Number(index) + " >= " + NzString::Number(m_impl->skins.size()) + ')'); - return NzString(); - } - #endif - - return m_impl->skins[index]; -} - -unsigned int NzMesh::GetSkinCount() const -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Mesh not created"); - return 0; - } - #endif - - return m_impl->skins.size(); -} - -NzSubMesh* NzMesh::GetSubMesh(const NzString& identifier) -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Mesh not created"); - return nullptr; - } - - auto it = m_impl->subMeshMap.find(identifier); - if (it == m_impl->subMeshMap.end()) - { - NazaraError("SubMesh not found"); - return nullptr; - } - - return m_impl->subMeshes[it->second]; - #else - return m_impl->subMeshes[m_impl->subMeshMap[identifier]]; - #endif -} - -NzSubMesh* NzMesh::GetSubMesh(nzUInt8 index) -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Mesh not created"); - return nullptr; - } - - if (index >= m_impl->subMeshes.size()) - { - NazaraError("SubMesh index out of range (" + NzString::Number(index) + " >= " + NzString::Number(m_impl->subMeshes.size()) + ')'); - return nullptr; - } - #endif - - return m_impl->subMeshes[index]; -} - -const NzSubMesh* NzMesh::GetSubMesh(const NzString& identifier) const -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Mesh not created"); - return nullptr; - } - - auto it = m_impl->subMeshMap.find(identifier); - if (it == m_impl->subMeshMap.end()) - { - NazaraError("SubMesh not found"); - return nullptr; - } - - return m_impl->subMeshes[it->second]; - #else - return m_impl->subMeshes[m_impl->subMeshMap[identifier]]; - #endif -} - -const NzSubMesh* NzMesh::GetSubMesh(nzUInt8 index) const -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Mesh not created"); - return nullptr; - } - - if (index >= m_impl->subMeshes.size()) - { - NazaraError("SubMesh index out of range (" + NzString::Number(index) + " >= " + NzString::Number(m_impl->subMeshes.size()) + ')'); - return nullptr; - } - #endif - - return m_impl->subMeshes[index]; -} - -nzUInt8 NzMesh::GetSubMeshCount() const -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Mesh not created"); - return 0; - } - #endif - - return m_impl->subMeshes.size(); -} - -unsigned int NzMesh::GetVertexCount() const -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Mesh not created"); - return 0; - } - #endif - - unsigned int vertexCount = 0; - for (NzSubMesh* subMesh : m_impl->subMeshes) - vertexCount += subMesh->GetVertexCount(); - - return vertexCount; -} - -void NzMesh::InvalidateAABB() const -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Mesh not created"); - return; - } - #endif - - m_impl->aabb.SetNull(); -} - -bool NzMesh::HasAnimation() const -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Mesh not created"); - return false; - } - #endif - - return m_impl->animation != nullptr; -} - -bool NzMesh::HasSkin(unsigned int index) const -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Mesh not created"); - return false; - } - #endif - - return m_impl->skins.size() > index; -} - -bool NzMesh::HasSubMesh(const NzString& identifier) const -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Mesh not created"); - return false; - } - #endif - - return m_impl->subMeshMap.find(identifier) != m_impl->subMeshMap.end(); -} - -bool NzMesh::HasSubMesh(nzUInt8 index) const -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Mesh not created"); - return false; - } - #endif - - return index < m_impl->subMeshes.size(); -} - -bool NzMesh::IsAnimable() const -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Mesh not created"); - return false; - } - #endif - - return m_impl->animationType != nzAnimationType_Static; -} - -bool NzMesh::IsValid() const -{ - return m_impl != nullptr; -} - -bool NzMesh::LoadFromFile(const NzString& filePath, const NzMeshParams& params) -{ - return NzMeshLoader::LoadFromFile(this, filePath, params); -} - -bool NzMesh::LoadFromMemory(const void* data, std::size_t size, const NzMeshParams& params) -{ - return NzMeshLoader::LoadFromMemory(this, data, size, params); -} - -bool NzMesh::LoadFromStream(NzInputStream& stream, const NzMeshParams& params) -{ - return NzMeshLoader::LoadFromStream(this, stream, params); -} - -void NzMesh::RemoveSkin(unsigned int index) -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Mesh not created"); - return; - } - - if (m_impl->skins.size() <= index) - { - NazaraError("Skin index out of range (" + NzString::Number(index) + " >= " + NzString::Number(m_impl->skins.size()) + ')'); - return; - } - #endif - - auto it = m_impl->skins.begin(); - std::advance(it, index); - - m_impl->skins.erase(it); -} - -void NzMesh::RemoveSubMesh(const NzString& identifier) -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Mesh not created"); - return; - } - - auto it = m_impl->subMeshMap.find(identifier); - if (it == m_impl->subMeshMap.end()) - { - NazaraError("SubMesh not found"); - return; - } - - unsigned int index = it->second; - #else - unsigned int index = m_impl->subMeshMap[identifier]; - #endif - - auto it2 = m_impl->subMeshes.begin(); - std::advance(it2, index); - - m_impl->subMeshes.erase(it2); - - m_impl->aabb.SetNull(); // On invalide l'AABB -} - -void NzMesh::RemoveSubMesh(nzUInt8 index) -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Mesh not created"); - return; - } - - if (index >= m_impl->subMeshes.size()) - { - NazaraError("SubMesh index out of range (" + NzString::Number(index) + " >= " + NzString::Number(m_impl->subMeshes.size()) + ')'); - return; - } - #endif - - auto it = m_impl->subMeshes.begin(); - std::advance(it, index); - - m_impl->subMeshes.erase(it); - - m_impl->aabb.SetNull(); // On invalide l'AABB -} - -bool NzMesh::SetAnimation(const NzAnimation* animation) -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Mesh not created"); - return false; - } - - if (m_impl->animationType == nzAnimationType_Static) - { - NazaraError("Static meshes cannot have animation"); - return false; - } - #endif - - if (animation == m_impl->animation) - return true; - - if (m_impl->animation) - m_impl->animation->RemoveResourceReference(); - - if (animation) - { - #if NAZARA_UTILITY_SAFE - if (animation->GetType() != m_impl->animationType) - { - NazaraError("Animation's type must match mesh animation type"); - return false; - } - #endif - - m_impl->animation = animation; - m_impl->animation->AddResourceReference(); - } - else - m_impl->animation = nullptr; - - return true; -} - -NzMeshLoader::LoaderList NzMesh::s_loaders; +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +bool NzMeshParams::IsValid() const +{ + if (!animation.IsValid()) + { + NazaraError("Invalid animation parameters"); + return false; + } + + if (!NzBuffer::IsSupported(storage)) + { + NazaraError("Storage not supported"); + return false; + } + + return true; +} + +struct NzMeshImpl +{ + std::deque skins; + std::map subMeshMap; + std::vector subMeshes; + nzAnimationType animationType; + NzAxisAlignedBox aabb; + const NzAnimation* animation = nullptr; +}; + +NzMesh::~NzMesh() +{ + Destroy(); +} + +bool NzMesh::AddSkin(const NzString& skin, bool setDefault) +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Mesh not created"); + return false; + } + + if (skin.IsEmpty()) + { + NazaraError("Skin is empty"); + return false; + } + #endif + + if (setDefault) + m_impl->skins.push_front(skin); + else + m_impl->skins.push_back(skin); + + return true; +} + +bool NzMesh::AddSubMesh(NzSubMesh* subMesh) +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Mesh not created"); + return false; + } + + if (!subMesh) + { + NazaraError("Invalid submesh"); + return false; + } + #endif + + subMesh->AddResourceListener(this, m_impl->subMeshes.size()); + + m_impl->aabb.SetNull(); // On invalide l'AABB + m_impl->subMeshes.push_back(subMesh); + + return true; +} + +bool NzMesh::AddSubMesh(const NzString& identifier, NzSubMesh* subMesh) +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Mesh not created"); + return false; + } + + if (identifier.IsEmpty()) + { + NazaraError("Identifier is empty"); + return false; + } + + auto it = m_impl->subMeshMap.find(identifier); + if (it != m_impl->subMeshMap.end()) + { + NazaraError("SubMesh identifier \"" + identifier + "\" is already used"); + return false; + } + + if (!subMesh) + { + NazaraError("Invalid submesh"); + return false; + } + #endif + + int index = m_impl->subMeshes.size(); + + subMesh->AddResourceListener(this, index); + + m_impl->aabb.SetNull(); // On invalide l'AABB + m_impl->subMeshes.push_back(subMesh); + m_impl->subMeshMap[identifier] = index; + + return true; +} + +void NzMesh::Animate(unsigned int frameA, unsigned int frameB, float interpolation) +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Mesh not created"); + return; + } + + if (!m_impl->animation) + { + NazaraError("Mesh has no animation"); + return; + } + + unsigned int frameCount = m_impl->animation->GetFrameCount(); + if (frameA >= frameCount) + { + NazaraError("Frame A is out of range (" + NzString::Number(frameA) + " >= " + NzString::Number(frameCount) + ')'); + return; + } + + if (frameB >= frameCount) + { + NazaraError("Frame B is out of range (" + NzString::Number(frameB) + " >= " + NzString::Number(frameCount) + ')'); + return; + } + + if (interpolation < 0.f || interpolation > 1.f) + { + NazaraError("Interpolation must be in range [0..1] (Got " + NzString::Number(interpolation) + ')'); + return; + } + #endif + + for (NzSubMesh* subMesh : m_impl->subMeshes) + subMesh->AnimateImpl(frameA, frameB, interpolation); + + m_impl->aabb.SetNull(); // On invalide l'AABB +} + +bool NzMesh::Create(nzAnimationType type) +{ + Destroy(); + + m_impl = new NzMeshImpl; + m_impl->animationType = type; + + NotifyCreated(); + + return true; +} + +void NzMesh::Destroy() +{ + if (m_impl) + { + NotifyDestroy(); + + if (m_impl->animation) + m_impl->animation->RemoveResourceListener(this); + + for (NzSubMesh* subMesh : m_impl->subMeshes) + subMesh->RemoveResourceListener(this); + + delete m_impl; + m_impl = nullptr; + } +} + +const NzAxisAlignedBox& NzMesh::GetAABB() const +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Mesh not created"); + return NzAxisAlignedBox::Null; + } + #endif + + if (m_impl->aabb.IsNull()) + { + for (NzSubMesh* subMesh : m_impl->subMeshes) + m_impl->aabb.ExtendTo(subMesh->GetAABB()); + } + + return m_impl->aabb; +} + +const NzAnimation* NzMesh::GetAnimation() const +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Mesh not created"); + return nullptr; + } + #endif + + return m_impl->animation; +} + +nzAnimationType NzMesh::GetAnimationType() const +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Mesh not created"); + return nzAnimationType_Static; + } + #endif + + return m_impl->animationType; +} + +unsigned int NzMesh::GetFrameCount() const +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Mesh not created"); + return 0; + } + + if (!m_impl->animation) + { + NazaraError("Mesh has no animation"); + return 0; + } + #endif + + return m_impl->animation->GetFrameCount(); +} + +NzString NzMesh::GetSkin(unsigned int index) const +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Mesh not created"); + return NzString(); + } + + if (index >= m_impl->skins.size()) + { + NazaraError("Skin index out of range (" + NzString::Number(index) + " >= " + NzString::Number(m_impl->skins.size()) + ')'); + return NzString(); + } + #endif + + return m_impl->skins[index]; +} + +unsigned int NzMesh::GetSkinCount() const +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Mesh not created"); + return 0; + } + #endif + + return m_impl->skins.size(); +} + +NzSubMesh* NzMesh::GetSubMesh(const NzString& identifier) +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Mesh not created"); + return nullptr; + } + + auto it = m_impl->subMeshMap.find(identifier); + if (it == m_impl->subMeshMap.end()) + { + NazaraError("SubMesh not found"); + return nullptr; + } + + return m_impl->subMeshes[it->second]; + #else + return m_impl->subMeshes[m_impl->subMeshMap[identifier]]; + #endif +} + +NzSubMesh* NzMesh::GetSubMesh(unsigned int index) +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Mesh not created"); + return nullptr; + } + + if (index >= m_impl->subMeshes.size()) + { + NazaraError("SubMesh index out of range (" + NzString::Number(index) + " >= " + NzString::Number(m_impl->subMeshes.size()) + ')'); + return nullptr; + } + #endif + + return m_impl->subMeshes[index]; +} + +const NzSubMesh* NzMesh::GetSubMesh(const NzString& identifier) const +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Mesh not created"); + return nullptr; + } + + auto it = m_impl->subMeshMap.find(identifier); + if (it == m_impl->subMeshMap.end()) + { + NazaraError("SubMesh not found"); + return nullptr; + } + + return m_impl->subMeshes[it->second]; + #else + return m_impl->subMeshes[m_impl->subMeshMap[identifier]]; + #endif +} + +const NzSubMesh* NzMesh::GetSubMesh(unsigned int index) const +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Mesh not created"); + return nullptr; + } + + if (index >= m_impl->subMeshes.size()) + { + NazaraError("SubMesh index out of range (" + NzString::Number(index) + " >= " + NzString::Number(m_impl->subMeshes.size()) + ')'); + return nullptr; + } + #endif + + return m_impl->subMeshes[index]; +} + +unsigned int NzMesh::GetSubMeshCount() const +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Mesh not created"); + return 0; + } + #endif + + return m_impl->subMeshes.size(); +} + +int NzMesh::GetSubMeshIndex(const NzString& identifier) const +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Mesh not created"); + return -1; + } + + auto it = m_impl->subMeshMap.find(identifier); + if (it == m_impl->subMeshMap.end()) + { + NazaraError("SubMesh not found"); + return -1; + } + + return it->second; + #else + return m_impl->subMeshMap[identifier]; + #endif +} + +unsigned int NzMesh::GetVertexCount() const +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Mesh not created"); + return 0; + } + #endif + + unsigned int vertexCount = 0; + for (NzSubMesh* subMesh : m_impl->subMeshes) + vertexCount += subMesh->GetVertexCount(); + + return vertexCount; +} + +void NzMesh::InvalidateAABB() const +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Mesh not created"); + return; + } + #endif + + m_impl->aabb.SetNull(); +} + +bool NzMesh::HasAnimation() const +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Mesh not created"); + return false; + } + #endif + + return m_impl->animation != nullptr; +} + +bool NzMesh::HasSkin(unsigned int index) const +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Mesh not created"); + return false; + } + #endif + + return m_impl->skins.size() > index; +} + +bool NzMesh::HasSubMesh(const NzString& identifier) const +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Mesh not created"); + return false; + } + #endif + + return m_impl->subMeshMap.find(identifier) != m_impl->subMeshMap.end(); +} + +bool NzMesh::HasSubMesh(unsigned int index) const +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Mesh not created"); + return false; + } + #endif + + return index < m_impl->subMeshes.size(); +} + +bool NzMesh::IsAnimable() const +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Mesh not created"); + return false; + } + #endif + + return m_impl->animationType != nzAnimationType_Static; +} + +bool NzMesh::IsValid() const +{ + return m_impl != nullptr; +} + +bool NzMesh::LoadFromFile(const NzString& filePath, const NzMeshParams& params) +{ + return NzMeshLoader::LoadFromFile(this, filePath, params); +} + +bool NzMesh::LoadFromMemory(const void* data, std::size_t size, const NzMeshParams& params) +{ + return NzMeshLoader::LoadFromMemory(this, data, size, params); +} + +bool NzMesh::LoadFromStream(NzInputStream& stream, const NzMeshParams& params) +{ + return NzMeshLoader::LoadFromStream(this, stream, params); +} + +void NzMesh::RemoveSkin(unsigned int index) +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Mesh not created"); + return; + } + + if (m_impl->skins.size() <= index) + { + NazaraError("Skin index out of range (" + NzString::Number(index) + " >= " + NzString::Number(m_impl->skins.size()) + ')'); + return; + } + #endif + + // On accède à l'itérateur correspondant à l'entrée #index + auto it = m_impl->skins.begin(); + std::advance(it, index); + + m_impl->skins.erase(it); +} + +void NzMesh::RemoveSubMesh(const NzString& identifier) +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Mesh not created"); + return; + } + + auto it = m_impl->subMeshMap.find(identifier); + if (it == m_impl->subMeshMap.end()) + { + NazaraError("SubMesh not found"); + return; + } + + unsigned int index = it->second; + #else + unsigned int index = m_impl->subMeshMap[identifier]; + #endif + + // On déplace l'itérateur du début d'une distance de x + auto it2 = m_impl->subMeshes.begin(); + std::advance(it, index); + + // On libère la ressource + (*it2)->RemoveResourceListener(this); + m_impl->subMeshes.erase(it2); + + m_impl->aabb.SetNull(); // On invalide l'AABB +} + +void NzMesh::RemoveSubMesh(unsigned int index) +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Mesh not created"); + return; + } + + if (index >= m_impl->subMeshes.size()) + { + NazaraError("SubMesh index out of range (" + NzString::Number(index) + " >= " + NzString::Number(m_impl->subMeshes.size()) + ')'); + return; + } + #endif + + // On déplace l'itérateur du début de x + auto it = m_impl->subMeshes.begin(); + std::advance(it, index); + + // On libère la ressource + (*it)->RemoveResourceListener(this); + m_impl->subMeshes.erase(it); + + m_impl->aabb.SetNull(); // On invalide l'AABB +} + +bool NzMesh::SetAnimation(const NzAnimation* animation) +{ + #if NAZARA_UTILITY_SAFE + if (!m_impl) + { + NazaraError("Mesh not created"); + return false; + } + + if (m_impl->animationType == nzAnimationType_Static) + { + NazaraError("Static meshes cannot have animation"); + return false; + } + #endif + + if (animation == m_impl->animation) + return true; + + if (m_impl->animation) + m_impl->animation->RemoveResourceListener(this); + + if (animation) + { + #if NAZARA_UTILITY_SAFE + if (animation->GetType() != m_impl->animationType) + { + NazaraError("Animation's type must match mesh animation type"); + return false; + } + #endif + + animation->AddResourceListener(this); + } + + m_impl->animation = animation; + + return true; +} + +void NzMesh::OnResourceCreated(const NzResource* resource, int index) +{ + NazaraUnused(index); + + if (resource == m_impl->animation) + { + #if NAZARA_UTILITY_SAFE + if (m_impl->animation->GetType() != m_impl->animationType) + { + NazaraError("Animation's type must match mesh animation type"); + + m_impl->animation->RemoveResourceListener(this); + m_impl->animation = nullptr; + } + #endif + } +} + +void NzMesh::OnResourceReleased(const NzResource* resource, int index) +{ + if (resource == m_impl->animation) + SetAnimation(nullptr); + else + RemoveSubMesh(index); +} + +NzMeshLoader::LoaderList NzMesh::s_loaders; diff --git a/src/Nazara/Utility/StaticMesh.cpp b/src/Nazara/Utility/StaticMesh.cpp index a85273327..bf1fe2b6b 100644 --- a/src/Nazara/Utility/StaticMesh.cpp +++ b/src/Nazara/Utility/StaticMesh.cpp @@ -1,186 +1,199 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Utility module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include - -NzStaticMesh::NzStaticMesh(const NzMesh* parent) : -NzSubMesh(parent) -{ -} - -NzStaticMesh::NzStaticMesh(const NzMesh* parent, const NzVertexDeclaration* vertexDeclaration, NzVertexBuffer* vertexBuffer, NzIndexBuffer* indexBuffer) : -NzSubMesh(parent) -{ - #ifdef NAZARA_DEBUG - if (!Create(vertexDeclaration, vertexBuffer, indexBuffer)) - { - NazaraError("Failed to create mesh"); - throw std::runtime_error("Constructor failed"); - } - #else - Create(vertexDeclaration, vertexBuffer, indexBuffer); - #endif -} - -NzStaticMesh::~NzStaticMesh() -{ - Destroy(); -} - -bool NzStaticMesh::Create(const NzVertexDeclaration* vertexDeclaration, NzVertexBuffer* vertexBuffer, NzIndexBuffer* indexBuffer) -{ - Destroy(); - - #if NAZARA_UTILITY_SAFE - if (!vertexDeclaration) - { - NazaraError("Vertex declaration is null"); - return false; - } - - if (!vertexBuffer) - { - NazaraError("Vertex buffer is null"); - return false; - } - #endif - - if (indexBuffer) - { - m_indexBuffer = indexBuffer; - m_indexBuffer->AddResourceReference(); - } - - m_vertexBuffer = vertexBuffer; - m_vertexBuffer->AddResourceReference(); - - m_vertexDeclaration = vertexDeclaration; - m_vertexDeclaration->AddResourceReference(); - - return true; -} - -void NzStaticMesh::Destroy() -{ - m_aabb.SetNull(); - - if (m_indexBuffer) - { - m_indexBuffer->RemoveResourceReference(); - m_indexBuffer = nullptr; - } - - if (m_vertexBuffer) - { - m_vertexBuffer->RemoveResourceReference(); - m_vertexBuffer = nullptr; - } - - if (m_vertexDeclaration) - { - m_vertexDeclaration->RemoveResourceReference(); - m_vertexDeclaration = nullptr; - } -} - -bool NzStaticMesh::GenerateAABB() -{ - if (!m_aabb.IsNull()) - return true; - - const NzVertexElement* position = m_vertexDeclaration->GetElement(nzElementStream_VertexData, nzElementUsage_Position); - if (position && position->type == nzElementType_Float3) // Si nous avons des positions du type Vec3 - { - // On lock le buffer pour itérer sur toutes les positions et composer notre AABB - nzUInt8* buffer = reinterpret_cast(m_vertexBuffer->Map(nzBufferAccess_ReadOnly)); - if (!buffer) - { - NazaraWarning("Failed to lock vertex buffer"); - return false; - } - - buffer += position->offset; - unsigned int stride = m_vertexDeclaration->GetStride(nzElementStream_VertexData); - unsigned int vertexCount = m_vertexBuffer->GetVertexCount(); - for (unsigned int i = 0; i < vertexCount; ++i) - { - m_aabb.ExtendTo(*reinterpret_cast(buffer)); - - buffer += stride; - } - - if (!m_vertexBuffer->Unmap()) - NazaraWarning("Failed to unmap vertex buffer"); - } - - return true; -} - -const NzAxisAlignedBox& NzStaticMesh::GetAABB() const -{ - return m_aabb; -} - -nzAnimationType NzStaticMesh::GetAnimationType() const -{ - return nzAnimationType_Static; -} - -unsigned int NzStaticMesh::GetFrameCount() const -{ - return 1; -} - -const NzIndexBuffer* NzStaticMesh::GetIndexBuffer() const -{ - return m_indexBuffer; -} - -nzPrimitiveType NzStaticMesh::GetPrimitiveType() const -{ - return m_primitiveType; -} - -const NzVertexBuffer* NzStaticMesh::GetVertexBuffer() const -{ - return m_vertexBuffer; -} - -const NzVertexDeclaration* NzStaticMesh::GetVertexDeclaration() const -{ - return m_vertexDeclaration; -} - -bool NzStaticMesh::IsAnimated() const -{ - return false; -} - -bool NzStaticMesh::IsValid() const -{ - return m_vertexBuffer != nullptr && m_vertexDeclaration != nullptr; -} - -void NzStaticMesh::SetAABB(const NzAxisAlignedBox& aabb) -{ - m_aabb = aabb; -} - -void NzStaticMesh::SetPrimitiveType(nzPrimitiveType primitiveType) -{ - m_primitiveType = primitiveType; -} - -void NzStaticMesh::AnimateImpl(unsigned int frameA, unsigned int frameB, float interpolation) -{ - NazaraUnused(frameA); - NazaraUnused(frameB); - NazaraUnused(interpolation); - - // Le safe mode est censé nous protéger de cet appel - NazaraError("Static mesh have no animation, please enable safe mode"); -} +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include + +NzStaticMesh::NzStaticMesh(const NzMesh* parent) : +NzSubMesh(parent) +{ +} + +NzStaticMesh::NzStaticMesh(const NzMesh* parent, const NzVertexDeclaration* vertexDeclaration, NzVertexBuffer* vertexBuffer, NzIndexBuffer* indexBuffer) : +NzSubMesh(parent) +{ + #ifdef NAZARA_DEBUG + if (!Create(vertexDeclaration, vertexBuffer, indexBuffer)) + { + NazaraError("Failed to create mesh"); + throw std::runtime_error("Constructor failed"); + } + #else + Create(vertexDeclaration, vertexBuffer, indexBuffer); + #endif +} + +NzStaticMesh::~NzStaticMesh() +{ + Destroy(); +} + +bool NzStaticMesh::Create(const NzVertexDeclaration* vertexDeclaration, NzVertexBuffer* vertexBuffer, NzIndexBuffer* indexBuffer) +{ + Destroy(); + + #if NAZARA_UTILITY_SAFE + if (!vertexDeclaration) + { + NazaraError("Invalid vertex declaration"); + return false; + } + + if (!vertexBuffer) + { + NazaraError("Invalid vertex buffer"); + return false; + } + #endif + + if (indexBuffer) + indexBuffer->AddResourceListener(this); + + m_indexBuffer = indexBuffer; + + m_vertexBuffer = vertexBuffer; + m_vertexBuffer->AddResourceListener(this); + + m_vertexDeclaration = vertexDeclaration; + m_vertexDeclaration->AddResourceListener(this); + + return true; +} + +void NzStaticMesh::Destroy() +{ + m_aabb.SetNull(); + + if (m_indexBuffer) + { + m_indexBuffer->RemoveResourceListener(this); + m_indexBuffer = nullptr; + } + + if (m_vertexBuffer) + { + m_vertexBuffer->RemoveResourceListener(this); + m_vertexBuffer = nullptr; + } + + if (m_vertexDeclaration) + { + m_vertexDeclaration->RemoveResourceListener(this); + m_vertexDeclaration = nullptr; + } +} + +bool NzStaticMesh::GenerateAABB() +{ + if (!m_aabb.IsNull()) + return true; + + const NzVertexElement* position = m_vertexDeclaration->GetElement(nzElementStream_VertexData, nzElementUsage_Position); + if (position && position->type == nzElementType_Float3) // Si nous avons des positions du type Vec3 + { + // On lock le buffer pour itérer sur toutes les positions et composer notre AABB + nzUInt8* buffer = reinterpret_cast(m_vertexBuffer->Map(nzBufferAccess_ReadOnly)); + if (!buffer) + { + NazaraWarning("Failed to lock vertex buffer"); + return false; + } + + buffer += position->offset; + unsigned int stride = m_vertexDeclaration->GetStride(nzElementStream_VertexData); + unsigned int vertexCount = m_vertexBuffer->GetVertexCount(); + for (unsigned int i = 0; i < vertexCount; ++i) + { + m_aabb.ExtendTo(*reinterpret_cast(buffer)); + + buffer += stride; + } + + if (!m_vertexBuffer->Unmap()) + NazaraWarning("Failed to unmap vertex buffer"); + } + + return true; +} + +const NzAxisAlignedBox& NzStaticMesh::GetAABB() const +{ + return m_aabb; +} + +nzAnimationType NzStaticMesh::GetAnimationType() const +{ + return nzAnimationType_Static; +} + +unsigned int NzStaticMesh::GetFrameCount() const +{ + return 1; +} + +const NzIndexBuffer* NzStaticMesh::GetIndexBuffer() const +{ + return m_indexBuffer; +} + +nzPrimitiveType NzStaticMesh::GetPrimitiveType() const +{ + return m_primitiveType; +} + +const NzVertexBuffer* NzStaticMesh::GetVertexBuffer() const +{ + return m_vertexBuffer; +} + +const NzVertexDeclaration* NzStaticMesh::GetVertexDeclaration() const +{ + return m_vertexDeclaration; +} + +bool NzStaticMesh::IsAnimated() const +{ + return false; +} + +bool NzStaticMesh::IsValid() const +{ + return m_vertexBuffer != nullptr && m_vertexDeclaration != nullptr; +} + +void NzStaticMesh::SetAABB(const NzAxisAlignedBox& aabb) +{ + m_aabb = aabb; +} + +void NzStaticMesh::SetPrimitiveType(nzPrimitiveType primitiveType) +{ + m_primitiveType = primitiveType; +} + +void NzStaticMesh::AnimateImpl(unsigned int frameA, unsigned int frameB, float interpolation) +{ + NazaraUnused(frameA); + NazaraUnused(frameB); + NazaraUnused(interpolation); + + // Le safe mode est censé nous protéger de cet appel + NazaraError("Static mesh have no animation, please enable safe mode"); +} + +void NzStaticMesh::OnResourceReleased(const NzResource* resource, int index) +{ + NazaraUnused(index); + + if (resource == m_indexBuffer) + m_indexBuffer = nullptr; + else if (resource == m_vertexBuffer) + m_vertexBuffer = nullptr; + else if (resource == m_vertexDeclaration) + m_vertexDeclaration = nullptr; + else + NazaraInternalError("Not listening to " + NzString::Pointer(resource)); +} diff --git a/src/Nazara/Utility/VertexDeclaration.cpp b/src/Nazara/Utility/VertexDeclaration.cpp index f6be21c60..6c66a8f67 100644 --- a/src/Nazara/Utility/VertexDeclaration.cpp +++ b/src/Nazara/Utility/VertexDeclaration.cpp @@ -1,346 +1,372 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Utility module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include -#include - -#if NAZARA_UTILITY_THREADSAFE && NAZARA_THREADSAFETY_VERTEXDECLARATION -#include -#else -#include -#endif - -#include - -namespace -{ - const unsigned int size[] = - { - sizeof(nzUInt32), // nzElementType_Color - 1*sizeof(double), // nzElementType_Double1 - 2*sizeof(double), // nzElementType_Double2 - 3*sizeof(double), // nzElementType_Double3 - 4*sizeof(double), // nzElementType_Double4 - 1*sizeof(float), // nzElementType_Float1 - 2*sizeof(float), // nzElementType_Float2 - 3*sizeof(float), // nzElementType_Float3 - 4*sizeof(float) // nzElementType_Float4 - }; - - bool VertexElementCompare(const NzVertexElement& elementA, const NzVertexElement& elementB) - { - // Nous classons d'abord par stream - if (elementA.stream == elementB.stream) - { - // Ensuite par usage - if (elementA.usage == elementB.usage) - // Et finalement par usageIndex - return elementA.usageIndex < elementB.usageIndex; - else - return elementA.usage < elementB.usage; - } - else - return elementA.stream < elementB.stream; - } -} - -struct NzVertexDeclarationImpl -{ - std::vector elements; - int elementPos[nzElementStream_Max+1][nzElementUsage_Max+1]; - int streamPos[nzElementStream_Max+1]; - unsigned int stride[nzElementStream_Max+1] = {0}; - - unsigned short refCount = 1; - NazaraMutex(mutex) -}; - -NzVertexDeclaration::NzVertexDeclaration(const NzVertexElement* elements, unsigned int elementCount) -{ - #ifdef NAZARA_DEBUG - if (!Create(elements, elementCount)) - { - NazaraError("Failed to create declaration"); - throw std::runtime_error("Constructor failed"); - } - #else - Create(elements, elementCount); - #endif -} - -NzVertexDeclaration::NzVertexDeclaration(const NzVertexDeclaration& declaration) : -NzResource(), -m_sharedImpl(declaration.m_sharedImpl) -{ - if (m_sharedImpl) - { - NazaraMutexLock(m_sharedImpl->mutex); - m_sharedImpl->refCount++; - NazaraMutexUnlock(m_sharedImpl->mutex); - } -} - -NzVertexDeclaration::NzVertexDeclaration(NzVertexDeclaration&& declaration) noexcept : -m_sharedImpl(declaration.m_sharedImpl) -{ - declaration.m_sharedImpl = nullptr; -} - -NzVertexDeclaration::~NzVertexDeclaration() -{ - Destroy(); -} - -bool NzVertexDeclaration::Create(const NzVertexElement* elements, unsigned int elementCount) -{ - Destroy(); - - #if NAZARA_UTILITY_SAFE - if (!elements || elementCount == 0) - { - NazaraError("No element"); - return false; - } - #endif - - NzVertexDeclarationImpl* impl = new NzVertexDeclarationImpl; - std::memset(&impl->elementPos, -1, (nzElementStream_Max+1)*(nzElementUsage_Max+1)*sizeof(int)); - std::memset(&impl->streamPos, -1, (nzElementStream_Max+1)*sizeof(int)); - - // On copie et trie les éléments - impl->elements.resize(elementCount); - std::memcpy(&impl->elements[0], elements, elementCount*sizeof(NzVertexElement)); - std::sort(impl->elements.begin(), impl->elements.end(), VertexElementCompare); - - for (unsigned int i = 0; i < elementCount; ++i) - { - NzVertexElement& current = impl->elements[i]; - #if NAZARA_UTILITY_SAFE - // Notre tableau étant trié, s'il y a collision, les deux éléments identiques se suivent... - if (i > 0) - { - NzVertexElement& previous = impl->elements[i-1]; // On accède à l'élément précédent - if (previous.usage == current.usage && previous.usageIndex == current.usageIndex && previous.stream == current.stream) - { - // Les deux éléments sont identiques là où ils ne devraient pas, nous avons une collision... - NazaraError("Element usage 0x" + NzString::Number(current.usage, 16) + " collision with usage index " + NzString::Number(current.usageIndex) + " on stream 0x" + NzString::Number(current.stream, 16)); - delete impl; - - return false; - } - } - #endif - - if (current.usageIndex == 0) - impl->elementPos[current.stream][current.usage] = i; - - if (impl->streamPos[current.stream] == -1) - impl->streamPos[current.stream] = i; // Premier élément du stream (via le triage) - - impl->stride[current.stream] += size[current.type]; - } - - #if NAZARA_UTILITY_FORCE_DECLARATION_STRIDE_MULTIPLE_OF_32 - for (unsigned int& stride : impl->stride) - stride = ((static_cast(stride)-1)/32+1)*32; - #endif - - m_sharedImpl = impl; - - return true; -} - -void NzVertexDeclaration::Destroy() -{ - if (!m_sharedImpl) - return; - - NazaraMutexLock(m_sharedImpl->mutex); - bool freeSharedImpl = (--m_sharedImpl->refCount == 0); - NazaraMutexUnlock(m_sharedImpl->mutex); - - if (freeSharedImpl) - delete m_sharedImpl; - - m_sharedImpl = nullptr; -} - -const NzVertexElement* NzVertexDeclaration::GetElement(unsigned int i) const -{ - #if NAZARA_UTILITY_SAFE - if (!m_sharedImpl) - { - NazaraError("Declaration not created"); - return nullptr; - } - - if (i >= m_sharedImpl->elements.size()) - { - NazaraError("Element index out of range (" + NzString::Number(i) + " >= " + NzString::Number(m_sharedImpl->elements.size()) + ')'); - return nullptr; - } - #endif - - return &m_sharedImpl->elements[i]; -} - -const NzVertexElement* NzVertexDeclaration::GetElement(nzElementStream stream, unsigned int i) const -{ - #if NAZARA_UTILITY_SAFE - if (!m_sharedImpl) - { - NazaraError("Declaration not created"); - return nullptr; - } - - int streamPos = m_sharedImpl->streamPos[stream]; - if (streamPos == -1) - { - NazaraError("Declaration has no stream 0x" + NzString::Number(stream, 16)); - return nullptr; - } - - unsigned int upperLimit = GetElementCount(stream); - if (i >= upperLimit) - { - NazaraError("Element index out of range (" + NzString::Number(i) + " >= " + NzString::Number(upperLimit) + ')'); - return nullptr; - } - #endif - - return &m_sharedImpl->elements[m_sharedImpl->streamPos[stream]+i]; -} - -const NzVertexElement* NzVertexDeclaration::GetElement(nzElementStream stream, nzElementUsage usage, unsigned int usageIndex) const -{ - #if NAZARA_UTILITY_SAFE - if (!m_sharedImpl) - { - NazaraError("Declaration not created"); - return nullptr; - } - #endif - - int elementPos = m_sharedImpl->elementPos[stream][usage]; - if (elementPos == -1) - return nullptr; - - elementPos += usageIndex; - if (static_cast(elementPos) >= m_sharedImpl->elements.size()) - return nullptr; - - NzVertexElement& element = m_sharedImpl->elements[elementPos]; - if (element.stream != stream || element.usage != usage || element.usageIndex != usageIndex) - return nullptr; - - return &element; -} - -unsigned int NzVertexDeclaration::GetElementCount() const -{ - #if NAZARA_UTILITY_SAFE - if (!m_sharedImpl) - { - NazaraError("Declaration not created"); - return 0; - } - #endif - - return m_sharedImpl->elements.size(); -} - -unsigned int NzVertexDeclaration::GetElementCount(nzElementStream stream) const -{ - #if NAZARA_UTILITY_SAFE - if (!m_sharedImpl) - { - NazaraError("Declaration not created"); - return 0; - } - #endif - - int streamPos = m_sharedImpl->streamPos[stream]; - if (streamPos == -1) - return 0; - - unsigned int upperLimit = 0; - if (stream == nzElementStream_Max) - upperLimit = m_sharedImpl->elements.size(); - else - { - for (unsigned int upperStream = stream+1; upperStream <= nzElementStream_Max; ++upperStream) - { - if (m_sharedImpl->streamPos[upperStream] != -1) - { - upperLimit = m_sharedImpl->streamPos[upperStream]; - break; - } - else if (upperStream == nzElementStream_Max) // Dernier stream, toujours pas de limite - upperLimit = m_sharedImpl->elements.size(); - } - } - - return upperLimit-streamPos; -} - -unsigned int NzVertexDeclaration::GetStride(nzElementStream stream) const -{ - #if NAZARA_UTILITY_SAFE - if (!m_sharedImpl) - { - NazaraError("Declaration not created"); - return 0; - } - #endif - - return m_sharedImpl->stride[stream]; -} - -bool NzVertexDeclaration::HasStream(nzElementStream stream) const -{ - #if NAZARA_UTILITY_SAFE - if (!m_sharedImpl) - { - NazaraError("Declaration not created"); - return false; - } - #endif - - return m_sharedImpl->streamPos[stream] != -1; -} - -bool NzVertexDeclaration::IsValid() const -{ - return m_sharedImpl != nullptr; -} - -NzVertexDeclaration& NzVertexDeclaration::operator=(const NzVertexDeclaration& declaration) -{ - Destroy(); - - m_sharedImpl = declaration.m_sharedImpl; - if (m_sharedImpl) - { - NazaraMutexLock(m_sharedImpl->mutex); - m_sharedImpl->refCount++; - NazaraMutexUnlock(m_sharedImpl->mutex); - } - - return *this; -} - -NzVertexDeclaration& NzVertexDeclaration::operator=(NzVertexDeclaration&& declaration) noexcept -{ - Destroy(); - - m_sharedImpl = declaration.m_sharedImpl; - declaration.m_sharedImpl = nullptr; - - return *this; -} +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include + +#if NAZARA_UTILITY_THREADSAFE && NAZARA_THREADSAFETY_VERTEXDECLARATION +#include +#else +#include +#endif + +#include + +namespace +{ + const unsigned int elementCount[] = + { + 4, // nzElementType_Color + 1, // nzElementType_Double1 + 2, // nzElementType_Double2 + 3, // nzElementType_Double3 + 4, // nzElementType_Double4 + 1, // nzElementType_Float1 + 2, // nzElementType_Float2 + 3, // nzElementType_Float3 + 4 // nzElementType_Float4 + }; + + const unsigned int elementSize[] = + { + 4*sizeof(nzUInt8), // nzElementType_Color + 1*sizeof(double), // nzElementType_Double1 + 2*sizeof(double), // nzElementType_Double2 + 3*sizeof(double), // nzElementType_Double3 + 4*sizeof(double), // nzElementType_Double4 + 1*sizeof(float), // nzElementType_Float1 + 2*sizeof(float), // nzElementType_Float2 + 3*sizeof(float), // nzElementType_Float3 + 4*sizeof(float) // nzElementType_Float4 + }; + + bool VertexElementCompare(const NzVertexElement& elementA, const NzVertexElement& elementB) + { + // Nous classons d'abord par stream + if (elementA.stream == elementB.stream) + { + // Ensuite par usage + if (elementA.usage == elementB.usage) + // Et finalement par usageIndex + return elementA.usageIndex < elementB.usageIndex; + else + return elementA.usage < elementB.usage; + } + else + return elementA.stream < elementB.stream; + } +} + +struct NzVertexDeclarationImpl +{ + std::vector elements; + int elementPos[nzElementStream_Max+1][nzElementUsage_Max+1]; + int streamPos[nzElementStream_Max+1]; + unsigned int stride[nzElementStream_Max+1] = {0}; + + unsigned short refCount = 1; + NazaraMutex(mutex) +}; + +NzVertexDeclaration::NzVertexDeclaration(const NzVertexElement* elements, unsigned int elementCount) +{ + #ifdef NAZARA_DEBUG + if (!Create(elements, elementCount)) + { + NazaraError("Failed to create declaration"); + throw std::runtime_error("Constructor failed"); + } + #else + Create(elements, elementCount); + #endif +} + +NzVertexDeclaration::NzVertexDeclaration(const NzVertexDeclaration& declaration) : +NzResource(), +m_sharedImpl(declaration.m_sharedImpl) +{ + if (m_sharedImpl) + { + NazaraMutexLock(m_sharedImpl->mutex); + m_sharedImpl->refCount++; + NazaraMutexUnlock(m_sharedImpl->mutex); + } +} + +NzVertexDeclaration::NzVertexDeclaration(NzVertexDeclaration&& declaration) noexcept : +m_sharedImpl(declaration.m_sharedImpl) +{ + declaration.m_sharedImpl = nullptr; +} + +NzVertexDeclaration::~NzVertexDeclaration() +{ + Destroy(); +} + +bool NzVertexDeclaration::Create(const NzVertexElement* elements, unsigned int elementCount) +{ + Destroy(); + + #if NAZARA_UTILITY_SAFE + if (!elements || elementCount == 0) + { + NazaraError("No element"); + return false; + } + #endif + + NzVertexDeclarationImpl* impl = new NzVertexDeclarationImpl; + std::memset(&impl->elementPos, -1, (nzElementStream_Max+1)*(nzElementUsage_Max+1)*sizeof(int)); + std::memset(&impl->streamPos, -1, (nzElementStream_Max+1)*sizeof(int)); + + // On copie et trie les éléments + impl->elements.resize(elementCount); + std::memcpy(&impl->elements[0], elements, elementCount*sizeof(NzVertexElement)); + std::sort(impl->elements.begin(), impl->elements.end(), VertexElementCompare); + + for (unsigned int i = 0; i < elementCount; ++i) + { + NzVertexElement& current = impl->elements[i]; + #if NAZARA_UTILITY_SAFE + // Notre tableau étant trié, s'il y a collision, les deux éléments identiques se suivent... + if (i > 0) + { + NzVertexElement& previous = impl->elements[i-1]; // On accède à l'élément précédent + if (previous.usage == current.usage && previous.usageIndex == current.usageIndex && previous.stream == current.stream) + { + // Les deux éléments sont identiques là où ils ne devraient pas, nous avons une collision... + NazaraError("Element usage 0x" + NzString::Number(current.usage, 16) + " collision with usage index " + NzString::Number(current.usageIndex) + " on stream 0x" + NzString::Number(current.stream, 16)); + delete impl; + + return false; + } + } + #endif + + if (current.usageIndex == 0) + impl->elementPos[current.stream][current.usage] = i; + + if (impl->streamPos[current.stream] == -1) + impl->streamPos[current.stream] = i; // Premier élément du stream (via le triage) + + impl->stride[current.stream] += elementSize[current.type]; + } + + #if NAZARA_UTILITY_FORCE_DECLARATION_STRIDE_MULTIPLE_OF_32 + for (unsigned int& stride : impl->stride) + stride = ((static_cast(stride)-1)/32+1)*32; + #endif + + m_sharedImpl = impl; + + NotifyCreated(); + return true; +} + +void NzVertexDeclaration::Destroy() +{ + if (!m_sharedImpl) + return; + + NotifyDestroy(); + + NazaraMutexLock(m_sharedImpl->mutex); + bool freeSharedImpl = (--m_sharedImpl->refCount == 0); + NazaraMutexUnlock(m_sharedImpl->mutex); + + if (freeSharedImpl) + delete m_sharedImpl; + + m_sharedImpl = nullptr; +} + +const NzVertexElement* NzVertexDeclaration::GetElement(unsigned int i) const +{ + #if NAZARA_UTILITY_SAFE + if (!m_sharedImpl) + { + NazaraError("Declaration not created"); + return nullptr; + } + + if (i >= m_sharedImpl->elements.size()) + { + NazaraError("Element index out of range (" + NzString::Number(i) + " >= " + NzString::Number(m_sharedImpl->elements.size()) + ')'); + return nullptr; + } + #endif + + return &m_sharedImpl->elements[i]; +} + +const NzVertexElement* NzVertexDeclaration::GetElement(nzElementStream stream, unsigned int i) const +{ + #if NAZARA_UTILITY_SAFE + if (!m_sharedImpl) + { + NazaraError("Declaration not created"); + return nullptr; + } + + int streamPos = m_sharedImpl->streamPos[stream]; + if (streamPos == -1) + { + NazaraError("Declaration has no stream 0x" + NzString::Number(stream, 16)); + return nullptr; + } + + unsigned int upperLimit = GetElementCount(stream); + if (i >= upperLimit) + { + NazaraError("Element index out of range (" + NzString::Number(i) + " >= " + NzString::Number(upperLimit) + ')'); + return nullptr; + } + #endif + + return &m_sharedImpl->elements[m_sharedImpl->streamPos[stream]+i]; +} + +const NzVertexElement* NzVertexDeclaration::GetElement(nzElementStream stream, nzElementUsage usage, unsigned int usageIndex) const +{ + #if NAZARA_UTILITY_SAFE + if (!m_sharedImpl) + { + NazaraError("Declaration not created"); + return nullptr; + } + #endif + + int elementPos = m_sharedImpl->elementPos[stream][usage]; + if (elementPos == -1) + return nullptr; + + elementPos += usageIndex; + if (static_cast(elementPos) >= m_sharedImpl->elements.size()) + return nullptr; + + NzVertexElement& element = m_sharedImpl->elements[elementPos]; + if (element.stream != stream || element.usage != usage || element.usageIndex != usageIndex) + return nullptr; + + return &element; +} + +unsigned int NzVertexDeclaration::GetElementCount() const +{ + #if NAZARA_UTILITY_SAFE + if (!m_sharedImpl) + { + NazaraError("Declaration not created"); + return 0; + } + #endif + + return m_sharedImpl->elements.size(); +} + +unsigned int NzVertexDeclaration::GetElementCount(nzElementStream stream) const +{ + #if NAZARA_UTILITY_SAFE + if (!m_sharedImpl) + { + NazaraError("Declaration not created"); + return 0; + } + #endif + + int streamPos = m_sharedImpl->streamPos[stream]; + if (streamPos == -1) + return 0; + + unsigned int upperLimit = 0; + if (stream == nzElementStream_Max) + upperLimit = m_sharedImpl->elements.size(); + else + { + for (unsigned int upperStream = stream+1; upperStream <= nzElementStream_Max; ++upperStream) + { + if (m_sharedImpl->streamPos[upperStream] != -1) + { + upperLimit = m_sharedImpl->streamPos[upperStream]; + break; + } + else if (upperStream == nzElementStream_Max) // Dernier stream, toujours pas de limite + upperLimit = m_sharedImpl->elements.size(); + } + } + + return upperLimit-streamPos; +} + +unsigned int NzVertexDeclaration::GetStride(nzElementStream stream) const +{ + #if NAZARA_UTILITY_SAFE + if (!m_sharedImpl) + { + NazaraError("Declaration not created"); + return 0; + } + #endif + + return m_sharedImpl->stride[stream]; +} + +bool NzVertexDeclaration::HasStream(nzElementStream stream) const +{ + #if NAZARA_UTILITY_SAFE + if (!m_sharedImpl) + { + NazaraError("Declaration not created"); + return false; + } + #endif + + return m_sharedImpl->streamPos[stream] != -1; +} + +bool NzVertexDeclaration::IsValid() const +{ + return m_sharedImpl != nullptr; +} + +NzVertexDeclaration& NzVertexDeclaration::operator=(const NzVertexDeclaration& declaration) +{ + Destroy(); + + m_sharedImpl = declaration.m_sharedImpl; + if (m_sharedImpl) + { + NazaraMutexLock(m_sharedImpl->mutex); + m_sharedImpl->refCount++; + NazaraMutexUnlock(m_sharedImpl->mutex); + } + + return *this; +} + +NzVertexDeclaration& NzVertexDeclaration::operator=(NzVertexDeclaration&& declaration) noexcept +{ + Destroy(); + + m_sharedImpl = declaration.m_sharedImpl; + declaration.m_sharedImpl = nullptr; + + return *this; +} + +unsigned int NzVertexDeclaration::GetElementCount(nzElementType type) +{ + return elementCount[type]; +} + +unsigned int NzVertexDeclaration::GetElementSize(nzElementType type) +{ + return elementSize[type]; +} diff --git a/src/Nazara/Utility/Window.cpp b/src/Nazara/Utility/Window.cpp index a9d3e1ed2..acfecb9e5 100644 --- a/src/Nazara/Utility/Window.cpp +++ b/src/Nazara/Utility/Window.cpp @@ -1,529 +1,534 @@ -// Copyright (C) 2012 Jérôme Leclercq -// This file is part of the "Nazara Engine - Utility module" -// For conditions of distribution and use, see copyright notice in Config.hpp - -#include -#include -#include -#include -#include -#include -#include - -#if defined(NAZARA_PLATFORM_WINDOWS) - #include - #include - #include -#elif defined(NAZARA_PLATFORM_LINUX) - #include - #include - #include -#else - #error Lack of implementation: Window -#endif - -#include - -namespace -{ - NzWindow* fullscreenWindow = nullptr; -} - -NzWindow::NzWindow() : -#if NAZARA_UTILITY_THREADED_WINDOW -m_impl(nullptr), -m_eventListener(true), -m_waitForEvent(false) -#else -m_impl(nullptr) -#endif -{ -} - -NzWindow::NzWindow(NzVideoMode mode, const NzString& title, nzUInt32 style) : -#if NAZARA_UTILITY_THREADED_WINDOW -m_impl(nullptr), -m_eventListener(true), -m_waitForEvent(false) -#else -m_impl(nullptr) -#endif -{ - Create(mode, title, style); - - #ifdef NAZARA_DEBUG - if (!m_impl) - { - NazaraError("Failed to create window"); - throw std::runtime_error("Constructor failed"); - } - #endif -} - -NzWindow::NzWindow(NzWindowHandle handle) : -#if NAZARA_UTILITY_THREADED_WINDOW -m_impl(nullptr), -m_eventListener(true), -m_waitForEvent(false) -#else -m_impl(nullptr) -#endif -{ - Create(handle); - - #ifdef NAZARA_DEBUG - if (!m_impl) - { - NazaraError("Failed to create window"); - throw std::runtime_error("Constructor failed"); - } - #endif -} - -NzWindow::~NzWindow() -{ - Destroy(); -} - -bool NzWindow::Create(NzVideoMode mode, const NzString& title, nzUInt32 style) -{ - // Si la fenêtre est déjà ouverte, nous conservons sa position - bool opened = IsOpen(); - NzVector2i position; - if (opened) - position = m_impl->GetPosition(); - - Destroy(); - - // Inspiré du code de la SFML par Laurent Gomila - if (style & nzWindowStyle_Fullscreen) - { - if (fullscreenWindow) - { - NazaraError("Window (" + NzString::Pointer(fullscreenWindow) + ") already in fullscreen mode"); - style &= ~nzWindowStyle_Fullscreen; - } - else - { - if (!mode.IsFullscreenValid()) - { - NazaraWarning("Mode is not fullscreen valid"); - mode = NzVideoMode::GetFullscreenModes()[0]; - } - - fullscreenWindow = this; - } - } - else if (style & nzWindowStyle_Closable || style & nzWindowStyle_Resizable) - style |= nzWindowStyle_Titlebar; - - m_impl = new NzWindowImpl(this); - if (!m_impl->Create(mode, title, style)) - { - NazaraError("Failed to create window implementation"); - delete m_impl; - m_impl = nullptr; - - return false; - } - - m_ownsWindow = true; - - if (!OnWindowCreated()) - { - NazaraError("Failed to initialize window extension"); - delete m_impl; - m_impl = nullptr; - - return false; - } - - // Paramètres par défaut - m_impl->EnableKeyRepeat(true); - m_impl->EnableSmoothScrolling(false); - m_impl->SetCursor(nzWindowCursor_Default); - m_impl->SetMaximumSize(-1, -1); - m_impl->SetMinimumSize(-1, -1); - m_impl->SetVisible(true); - - if (opened) - m_impl->SetPosition(position.x, position.y); - - return true; -} - -bool NzWindow::Create(NzWindowHandle handle) -{ - Destroy(); - - m_impl = new NzWindowImpl(this); - if (!m_impl->Create(handle)) - { - NazaraError("Unable to create window implementation"); - delete m_impl; - m_impl = nullptr; - - return false; - } - - m_ownsWindow = false; - - if (!OnWindowCreated()) - { - NazaraError("Failed to initialize window's derivate"); - delete m_impl; - m_impl = nullptr; - - return false; - } - - return true; -} - -void NzWindow::Destroy() -{ - if (m_impl) - { - OnWindowDestroying(); - - m_impl->Destroy(); - delete m_impl; - m_impl = nullptr; - - if (fullscreenWindow == this) - fullscreenWindow = nullptr; - } -} - -void NzWindow::EnableKeyRepeat(bool enable) -{ - if (m_impl) - m_impl->EnableKeyRepeat(enable); -} - -void NzWindow::EnableSmoothScrolling(bool enable) -{ - if (m_impl) - m_impl->EnableSmoothScrolling(enable); -} - -NzWindowHandle NzWindow::GetHandle() const -{ - if (m_impl) - return m_impl->GetHandle(); - else - return reinterpret_cast(0); -} - -unsigned int NzWindow::GetHeight() const -{ - if (m_impl) - return m_impl->GetHeight(); - else - return 0; -} - -NzVector2i NzWindow::GetPosition() const -{ - if (m_impl) - return m_impl->GetPosition(); - else - return NzVector2i(0); -} - -NzVector2ui NzWindow::GetSize() const -{ - if (m_impl) - return m_impl->GetSize(); - else - return NzVector2ui(0U); -} - -NzString NzWindow::GetTitle() const -{ - if (m_impl) - return m_impl->GetTitle(); - else - return NzString(); -} - -unsigned int NzWindow::GetWidth() const -{ - if (m_impl) - return m_impl->GetWidth(); - else - return 0; -} - -bool NzWindow::HasFocus() const -{ - if (m_impl) - return m_impl->HasFocus(); - else - return false; -} - -bool NzWindow::IsOpen() const -{ - return m_impl != nullptr; -} - -bool NzWindow::IsMinimized() const -{ - if (m_impl) - return m_impl->IsMinimized(); - else - return false; -} - -bool NzWindow::IsVisible() const -{ - if (m_impl) - return m_impl->IsVisible(); - else - return false; -} - -bool NzWindow::PollEvent(NzEvent* event) -{ - if (!m_impl) - return false; - - #if NAZARA_UTILITY_THREADED_WINDOW - NzLockGuard lock(m_eventMutex); - #else - m_impl->ProcessEvents(false); - #endif - - if (!m_events.empty()) - { - if (event) - *event = m_events.front(); - - m_events.pop(); - - return true; - } - - return false; -} - -void NzWindow::SetCursor(nzWindowCursor cursor) -{ - if (m_impl) - m_impl->SetCursor(cursor); -} - -void NzWindow::SetCursor(const NzCursor& cursor) -{ - #if NAZARA_UTILITY_SAFE - if (!cursor.IsValid()) - { - NazaraError("Cursor is not valid"); - return; - } - #endif - - if (m_impl) - m_impl->SetCursor(cursor); -} - -void NzWindow::SetEventListener(bool listener) -{ - if (!m_impl) - return; - - #if NAZARA_UTILITY_THREADED_WINDOW - m_impl->SetEventListener(listener); - if (!listener) - { - // On vide la pile des évènements - NzLockGuard lock(m_eventMutex); - while (!m_events.empty()) - m_events.pop(); - } - #else - if (m_ownsWindow) - { - // Inutile de transmettre l'ordre dans ce cas-là - if (!listener) - NazaraError("A non-threaded window needs to listen to events"); - } - else - m_impl->SetEventListener(listener); - #endif -} - -void NzWindow::SetFocus() -{ - if (m_impl) - m_impl->SetFocus(); -} - -void NzWindow::SetIcon(const NzIcon& icon) -{ - #if NAZARA_UTILITY_SAFE - if (!icon.IsValid()) - { - NazaraError("Icon is not valid"); - return; - } - #endif - - if (m_impl) - m_impl->SetIcon(icon); -} - -void NzWindow::SetMaximumSize(const NzVector2i& maxSize) -{ - if (m_impl) - m_impl->SetMaximumSize(maxSize.x, maxSize.y); -} - -void NzWindow::SetMaximumSize(int width, int height) -{ - if (m_impl) - m_impl->SetMaximumSize(width, height); -} - -void NzWindow::SetMinimumSize(const NzVector2i& minSize) -{ - if (m_impl) - m_impl->SetMinimumSize(minSize.x, minSize.y); -} - -void NzWindow::SetMinimumSize(int width, int height) -{ - if (m_impl) - m_impl->SetMinimumSize(width, height); -} - -void NzWindow::SetPosition(const NzVector2i& position) -{ - if (m_impl) - m_impl->SetPosition(position.x, position.y); -} - -void NzWindow::SetPosition(int x, int y) -{ - if (m_impl) - m_impl->SetPosition(x, y); -} - -void NzWindow::SetSize(const NzVector2i& size) -{ - if (m_impl) - m_impl->SetSize(size.x, size.y); -} - -void NzWindow::SetSize(unsigned int width, unsigned int height) -{ - if (m_impl) - m_impl->SetSize(width, height); -} - -void NzWindow::SetStayOnTop(bool stayOnTop) -{ - if (m_impl) - m_impl->SetStayOnTop(stayOnTop); -} - -void NzWindow::SetTitle(const NzString& title) -{ - if (m_impl) - m_impl->SetTitle(title); -} - -void NzWindow::SetVisible(bool visible) -{ - if (m_impl) - m_impl->SetVisible(visible); -} - -bool NzWindow::WaitEvent(NzEvent* event) -{ - if (!m_impl) - return false; - - #if NAZARA_UTILITY_THREADED_WINDOW - NzLockGuard lock(m_eventMutex); - - if (m_events.empty()) - { - m_waitForEvent = true; - m_eventConditionMutex.Lock(); - m_eventMutex.Unlock(); - m_eventCondition.Wait(&m_eventConditionMutex); - m_eventMutex.Lock(); - m_eventConditionMutex.Unlock(); - m_waitForEvent = false; - } - - if (!m_events.empty()) - { - if (event) - *event = m_events.front(); - - m_events.pop(); - - return true; - } - - return false; - #else - while (m_events.empty()) - m_impl->ProcessEvents(true); - - if (event) - *event = m_events.front(); - - m_events.pop(); - - return true; - #endif -} - -void NzWindow::OnWindowDestroying() -{ -} - -bool NzWindow::OnWindowCreated() -{ - return true; -} - -void NzWindow::IgnoreNextMouseEvent(int mouseX, int mouseY) const -{ - if (m_impl) - m_impl->IgnoreNextMouseEvent(mouseX, mouseY); -} - -void NzWindow::PushEvent(const NzEvent& event) -{ - #if NAZARA_UTILITY_THREADED_WINDOW - m_eventMutex.Lock(); - #endif - - m_events.push(event); - - #if NAZARA_UTILITY_THREADED_WINDOW - m_eventMutex.Unlock(); - - if (m_waitForEvent) - { - m_eventConditionMutex.Lock(); - m_eventCondition.Signal(); - m_eventConditionMutex.Unlock(); - } - #endif -} - -bool NzWindow::Initialize() -{ - return NzWindowImpl::Initialize(); -} - -void NzWindow::Uninitialize() -{ - NzWindowImpl::Uninitialize(); -} +// Copyright (C) 2012 Jérôme Leclercq +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include + +#if defined(NAZARA_PLATFORM_WINDOWS) + #include + #include + #include +#elif defined(NAZARA_PLATFORM_LINUX) + #include + #include + #include +#else + #error Lack of implementation: Window +#endif + +#include + +namespace +{ + NzWindow* fullscreenWindow = nullptr; +} + +NzWindow::NzWindow() : +#if NAZARA_UTILITY_THREADED_WINDOW +m_impl(nullptr), +m_eventListener(true), +m_waitForEvent(false) +#else +m_impl(nullptr) +#endif +{ +} + +NzWindow::NzWindow(NzVideoMode mode, const NzString& title, nzUInt32 style) : +#if NAZARA_UTILITY_THREADED_WINDOW +m_impl(nullptr), +m_eventListener(true), +m_waitForEvent(false) +#else +m_impl(nullptr) +#endif +{ + Create(mode, title, style); + + #ifdef NAZARA_DEBUG + if (!m_impl) + { + NazaraError("Failed to create window"); + throw std::runtime_error("Constructor failed"); + } + #endif +} + +NzWindow::NzWindow(NzWindowHandle handle) : +#if NAZARA_UTILITY_THREADED_WINDOW +m_impl(nullptr), +m_eventListener(true), +m_waitForEvent(false) +#else +m_impl(nullptr) +#endif +{ + Create(handle); + + #ifdef NAZARA_DEBUG + if (!m_impl) + { + NazaraError("Failed to create window"); + throw std::runtime_error("Constructor failed"); + } + #endif +} + +NzWindow::~NzWindow() +{ + Destroy(); +} + +bool NzWindow::Create(NzVideoMode mode, const NzString& title, nzUInt32 style) +{ + // Si la fenêtre est déjà ouverte, nous conservons sa position + bool opened = IsOpen(); + NzVector2i position; + if (opened) + position = m_impl->GetPosition(); + + Destroy(); + + // Inspiré du code de la SFML par Laurent Gomila + if (style & nzWindowStyle_Fullscreen) + { + if (fullscreenWindow) + { + NazaraError("Window (" + NzString::Pointer(fullscreenWindow) + ") already in fullscreen mode"); + style &= ~nzWindowStyle_Fullscreen; + } + else + { + if (!mode.IsFullscreenValid()) + { + NazaraWarning("Video mode is not fullscreen valid"); + mode = NzVideoMode::GetFullscreenModes()[0]; + } + + fullscreenWindow = this; + } + } + else if (style & nzWindowStyle_Closable || style & nzWindowStyle_Resizable) + style |= nzWindowStyle_Titlebar; + + m_impl = new NzWindowImpl(this); + if (!m_impl->Create(mode, title, style)) + { + NazaraError("Failed to create window implementation"); + delete m_impl; + m_impl = nullptr; + + return false; + } + + m_ownsWindow = true; + + if (!OnWindowCreated()) + { + NazaraError("Failed to initialize window extension"); + delete m_impl; + m_impl = nullptr; + + return false; + } + + // Paramètres par défaut + m_impl->EnableKeyRepeat(true); + m_impl->EnableSmoothScrolling(false); + m_impl->SetCursor(nzWindowCursor_Default); + m_impl->SetMaximumSize(-1, -1); + m_impl->SetMinimumSize(-1, -1); + m_impl->SetVisible(true); + + if (opened) + m_impl->SetPosition(position.x, position.y); + + return true; +} + +bool NzWindow::Create(NzWindowHandle handle) +{ + Destroy(); + + m_impl = new NzWindowImpl(this); + if (!m_impl->Create(handle)) + { + NazaraError("Unable to create window implementation"); + delete m_impl; + m_impl = nullptr; + + return false; + } + + m_ownsWindow = false; + + if (!OnWindowCreated()) + { + NazaraError("Failed to initialize window's derivate"); + delete m_impl; + m_impl = nullptr; + + return false; + } + + return true; +} + +void NzWindow::Destroy() +{ + if (m_impl) + { + OnWindowDestroy(); + + m_impl->Destroy(); + delete m_impl; + m_impl = nullptr; + + if (fullscreenWindow == this) + fullscreenWindow = nullptr; + } +} + +void NzWindow::EnableKeyRepeat(bool enable) +{ + if (m_impl) + m_impl->EnableKeyRepeat(enable); +} + +void NzWindow::EnableSmoothScrolling(bool enable) +{ + if (m_impl) + m_impl->EnableSmoothScrolling(enable); +} + +NzWindowHandle NzWindow::GetHandle() const +{ + if (m_impl) + return m_impl->GetHandle(); + else + return static_cast(0); +} + +unsigned int NzWindow::GetHeight() const +{ + if (m_impl) + return m_impl->GetHeight(); + else + return 0; +} + +NzVector2i NzWindow::GetPosition() const +{ + if (m_impl) + return m_impl->GetPosition(); + else + return NzVector2i(0); +} + +NzVector2ui NzWindow::GetSize() const +{ + if (m_impl) + return m_impl->GetSize(); + else + return NzVector2ui(0U); +} + +NzString NzWindow::GetTitle() const +{ + if (m_impl) + return m_impl->GetTitle(); + else + return NzString(); +} + +unsigned int NzWindow::GetWidth() const +{ + if (m_impl) + return m_impl->GetWidth(); + else + return 0; +} + +bool NzWindow::HasFocus() const +{ + if (m_impl) + return m_impl->HasFocus(); + else + return false; +} + +bool NzWindow::IsOpen() const +{ + return m_impl != nullptr; +} + +bool NzWindow::IsMinimized() const +{ + if (m_impl) + return m_impl->IsMinimized(); + else + return false; +} + +bool NzWindow::IsValid() const +{ + return m_impl != nullptr; +} + +bool NzWindow::IsVisible() const +{ + if (m_impl) + return m_impl->IsVisible(); + else + return false; +} + +bool NzWindow::PollEvent(NzEvent* event) +{ + if (!m_impl) + return false; + + #if NAZARA_UTILITY_THREADED_WINDOW + NzLockGuard lock(m_eventMutex); + #else + m_impl->ProcessEvents(false); + #endif + + if (!m_events.empty()) + { + if (event) + *event = m_events.front(); + + m_events.pop(); + + return true; + } + + return false; +} + +void NzWindow::SetCursor(nzWindowCursor cursor) +{ + if (m_impl) + m_impl->SetCursor(cursor); +} + +void NzWindow::SetCursor(const NzCursor& cursor) +{ + #if NAZARA_UTILITY_SAFE + if (!cursor.IsValid()) + { + NazaraError("Cursor is not valid"); + return; + } + #endif + + if (m_impl) + m_impl->SetCursor(cursor); +} + +void NzWindow::SetEventListener(bool listener) +{ + if (!m_impl) + return; + + #if NAZARA_UTILITY_THREADED_WINDOW + m_impl->SetEventListener(listener); + if (!listener) + { + // On vide la pile des évènements + NzLockGuard lock(m_eventMutex); + while (!m_events.empty()) + m_events.pop(); + } + #else + if (m_ownsWindow) + { + // Inutile de transmettre l'ordre dans ce cas-là + if (!listener) + NazaraError("A non-threaded window needs to listen to events"); + } + else + m_impl->SetEventListener(listener); + #endif +} + +void NzWindow::SetFocus() +{ + if (m_impl) + m_impl->SetFocus(); +} + +void NzWindow::SetIcon(const NzIcon& icon) +{ + #if NAZARA_UTILITY_SAFE + if (!icon.IsValid()) + { + NazaraError("Icon is not valid"); + return; + } + #endif + + if (m_impl) + m_impl->SetIcon(icon); +} + +void NzWindow::SetMaximumSize(const NzVector2i& maxSize) +{ + if (m_impl) + m_impl->SetMaximumSize(maxSize.x, maxSize.y); +} + +void NzWindow::SetMaximumSize(int width, int height) +{ + if (m_impl) + m_impl->SetMaximumSize(width, height); +} + +void NzWindow::SetMinimumSize(const NzVector2i& minSize) +{ + if (m_impl) + m_impl->SetMinimumSize(minSize.x, minSize.y); +} + +void NzWindow::SetMinimumSize(int width, int height) +{ + if (m_impl) + m_impl->SetMinimumSize(width, height); +} + +void NzWindow::SetPosition(const NzVector2i& position) +{ + if (m_impl) + m_impl->SetPosition(position.x, position.y); +} + +void NzWindow::SetPosition(int x, int y) +{ + if (m_impl) + m_impl->SetPosition(x, y); +} + +void NzWindow::SetSize(const NzVector2i& size) +{ + if (m_impl) + m_impl->SetSize(size.x, size.y); +} + +void NzWindow::SetSize(unsigned int width, unsigned int height) +{ + if (m_impl) + m_impl->SetSize(width, height); +} + +void NzWindow::SetStayOnTop(bool stayOnTop) +{ + if (m_impl) + m_impl->SetStayOnTop(stayOnTop); +} + +void NzWindow::SetTitle(const NzString& title) +{ + if (m_impl) + m_impl->SetTitle(title); +} + +void NzWindow::SetVisible(bool visible) +{ + if (m_impl) + m_impl->SetVisible(visible); +} + +bool NzWindow::WaitEvent(NzEvent* event) +{ + if (!m_impl) + return false; + + #if NAZARA_UTILITY_THREADED_WINDOW + NzLockGuard lock(m_eventMutex); + + if (m_events.empty()) + { + m_waitForEvent = true; + m_eventConditionMutex.Lock(); + m_eventMutex.Unlock(); + m_eventCondition.Wait(&m_eventConditionMutex); + m_eventMutex.Lock(); + m_eventConditionMutex.Unlock(); + m_waitForEvent = false; + } + + if (!m_events.empty()) + { + if (event) + *event = m_events.front(); + + m_events.pop(); + + return true; + } + + return false; + #else + while (m_events.empty()) + m_impl->ProcessEvents(true); + + if (event) + *event = m_events.front(); + + m_events.pop(); + + return true; + #endif +} + +bool NzWindow::OnWindowCreated() +{ + return true; +} + +void NzWindow::OnWindowDestroy() +{ +} + +void NzWindow::IgnoreNextMouseEvent(int mouseX, int mouseY) const +{ + if (m_impl) + m_impl->IgnoreNextMouseEvent(mouseX, mouseY); +} + +void NzWindow::PushEvent(const NzEvent& event) +{ + #if NAZARA_UTILITY_THREADED_WINDOW + m_eventMutex.Lock(); + #endif + + m_events.push(event); + + #if NAZARA_UTILITY_THREADED_WINDOW + m_eventMutex.Unlock(); + + if (m_waitForEvent) + { + m_eventConditionMutex.Lock(); + m_eventCondition.Signal(); + m_eventConditionMutex.Unlock(); + } + #endif +} + +bool NzWindow::Initialize() +{ + return NzWindowImpl::Initialize(); +} + +void NzWindow::Uninitialize() +{ + NzWindowImpl::Uninitialize(); +}