diff --git a/assimp-vc141-mt.dll b/assimp-vc141-mt.dll new file mode 100644 index 0000000..86f52a0 Binary files /dev/null and b/assimp-vc141-mt.dll differ diff --git a/assimp-vc141-mtd.dll b/assimp-vc141-mtd.dll new file mode 100644 index 0000000..2e5fd66 Binary files /dev/null and b/assimp-vc141-mtd.dll differ diff --git a/cw_8/assimp-vc141-mt.dll b/cw_8/assimp-vc141-mt.dll new file mode 100644 index 0000000..86f52a0 Binary files /dev/null and b/cw_8/assimp-vc141-mt.dll differ diff --git a/cw_8/assimp-vc141-mtd.dll b/cw_8/assimp-vc141-mtd.dll new file mode 100644 index 0000000..2e5fd66 Binary files /dev/null and b/cw_8/assimp-vc141-mtd.dll differ diff --git a/cw_8/freeglut.dll b/cw_8/freeglut.dll new file mode 100644 index 0000000..c768c15 Binary files /dev/null and b/cw_8/freeglut.dll differ diff --git a/cw_8/glew32.dll b/cw_8/glew32.dll new file mode 100644 index 0000000..8a49a3b Binary files /dev/null and b/cw_8/glew32.dll differ diff --git a/cw_8/grk-cw8.vcxproj b/cw_8/grk-cw8.vcxproj new file mode 100644 index 0000000..8d8954a --- /dev/null +++ b/cw_8/grk-cw8.vcxproj @@ -0,0 +1,122 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {6D813233-7D21-4888-944E-8E3CCAC3EFD3} + Win32Proj + grk-cw8 + 10.0 + grk-cw8 + + + + Application + true + Unicode + v143 + + + Application + false + true + Unicode + v143 + + + + + + + + + + + + + false + $(SolutionDir)dependencies\freeglut\lib;$(SolutionDir)dependencies\glew-2.0.0\lib\Release\Win32;$(SolutionDir)dependencies\glfw-3.3.8.bin.WIN32\lib-vc2019;$(SolutionDir)dependencies\assimp;$(LibraryPath) + $(SolutionDir)dependencies\freeglut\include\GL;$(SolutionDir)dependencies\glew-2.0.0\include\GL;$(SolutionDir)dependencies\glm;$(SolutionDir)dependencies\glfw-3.3.8.bin.WIN32\include;$(SolutionDir)dependencies\assimp\include;$(IncludePath) + $(ExecutablePath) + + + false + $(SolutionDir)dependencies\freeglut\lib;$(SolutionDir)dependencies\glew-2.0.0\lib\Release\Win32;$(SolutionDir)dependencies\glfw-3.3.8.bin.WIN32\lib-vc2019;$(SolutionDir)dependencies\assimp;$(LibraryPath) + $(SolutionDir)dependencies\freeglut\include\GL;$(SolutionDir)dependencies\glew-2.0.0\include\GL;$(SolutionDir)dependencies\glm;$(SolutionDir)dependencies\glfw-3.3.8.bin.WIN32\include;$(SolutionDir)dependencies\assimp\include;$(IncludePath) + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + opengl32.lib;freeglut.lib;glew32.lib;$(SolutionDir)\dependencies\glfw-3.3.8.bin.WIN32\lib-vc2019\glfw3.lib;$(SolutionDir)\dependencies\glfw-3.3.8.bin.WIN32\lib-vc2019\glfw3dll.lib;zlibd.lib;assimp-vc141-mtd.lib;%(AdditionalDependencies) + $(SolutionDir)dependencies\glfw-3.3.8.bin.WIN32\lib-vc2019;%(AdditionalLibraryDirectories) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + opengl32.lib;freeglut.lib;glew32.lib;$(SolutionDir)\dependencies\glfw-3.3.8.bin.WIN32\lib-vc2019\glfw3.lib;$(SolutionDir)\dependencies\glfw-3.3.8.bin.WIN32\lib-vc2019\glfw3dll.lib;zlibd.lib;assimp-vc141-mtd.lib;%(AdditionalDependencies) + $(SolutionDir)dependencies\glfw-3.3.8.bin.WIN32\lib-vc2019;%(AdditionalLibraryDirectories) + + + + + + \ No newline at end of file diff --git a/cw_8/grk-cw8.vcxproj.filters b/cw_8/grk-cw8.vcxproj.filters new file mode 100644 index 0000000..c1e06a3 --- /dev/null +++ b/cw_8/grk-cw8.vcxproj.filters @@ -0,0 +1,101 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {0a247bb8-2e8e-4a90-b0ef-17415b0941ba} + + + {0af44075-33f4-4953-b1d6-1d28d61d758f} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files\SOIL + + + Source Files\SOIL + + + Source Files\SOIL + + + Source Files\SOIL + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files\SOIL + + + Source Files\SOIL + + + Source Files\SOIL + + + Source Files\SOIL + + + Source Files\SOIL + + + Source Files\SOIL + + + Source Files + + + + + Shader Files + + + Shader Files + + + \ No newline at end of file diff --git a/cw_8/shaders/pbr.frag b/cw_8/shaders/pbr.frag new file mode 100644 index 0000000..9a5dcc2 --- /dev/null +++ b/cw_8/shaders/pbr.frag @@ -0,0 +1,125 @@ +#version 430 core + +out vec4 FragColor; + +in vec2 TexCoords; +in vec3 worldPos; +in vec3 Normal; + +uniform vec3 cameraPos; + +uniform vec3 lightPos; +uniform vec3 lightColor; + +uniform sampler2D albedoMap; +uniform sampler2D normalMap; +uniform sampler2D metallicMap; +uniform sampler2D roughnessMap; +uniform sampler2D aoMap; + +const float PI = 3.14159265359; + +vec3 getNormalFromMap() +{ + vec3 tangentNormal = texture(normalMap, TexCoords).xyz * 2.0 - 1.0; + + vec3 Q1 = dFdx(worldPos); + vec3 Q2 = dFdy(worldPos); + vec2 st1 = dFdx(TexCoords); + vec2 st2 = dFdy(TexCoords); + + vec3 N = normalize(Normal); + vec3 T = normalize(Q1*st2.t - Q2*st1.t); + vec3 B = -normalize(cross(N, T)); + mat3 TBN = mat3(T, B, N); + + return normalize(TBN * tangentNormal); +} + +vec3 fresnelSchlick(float cosTheta, vec3 F0){ + return F0 + (1.0 - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0); +} + +float DistributionGGX(vec3 N, vec3 H, float roughness) +{ + float a = roughness*roughness; + float a2 = a*a; + float NdotH = max(dot(N, H), 0.0); + float NdotH2 = NdotH*NdotH; + + float num = a2; + float denom = (NdotH2 * (a2 - 1.0) + 1.0); + denom = PI * denom * denom; + + return num / denom; +} + +float GeometrySchlickGGX(float NdotV, float roughness) +{ + float r = (roughness + 1.0); + float k = (r*r) / 8.0; + + float num = NdotV; + float denom = NdotV * (1.0 - k) + k; + + return num / denom; +} +float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) +{ + float NdotV = max(dot(N, V), 0.0); + float NdotL = max(dot(N, L), 0.0); + float ggx2 = GeometrySchlickGGX(NdotV, roughness); + float ggx1 = GeometrySchlickGGX(NdotL, roughness); + + return ggx1 * ggx2; +} + +void main() +{ + vec3 albedo = pow(texture(albedoMap, TexCoords).rgb, vec3(2.2)); + float metallic = texture(metallicMap, TexCoords).r; + float roughness = texture(roughnessMap, TexCoords).r; + float ao = texture(aoMap, TexCoords).r; + + vec3 N = getNormalFromMap(); + vec3 V = normalize(cameraPos - worldPos); + + vec3 F0 = vec3(0.04); + F0 = mix(F0, albedo, metallic); + + + vec3 Lo = vec3(0.0); + + vec3 L = normalize(lightPos - worldPos); + vec3 H = normalize(V + L); + float distance = length(lightPos - worldPos); + float attenuation = 1.0 / (distance * distance); + vec3 radiance = lightColor * attenuation; + + float NDF = DistributionGGX(N, H, roughness); + float G = GeometrySmith(N, V, L, roughness); + vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0); + + vec3 numerator = NDF * G * F; + float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0) + 0.0001; + vec3 specular = numerator / denominator; + + vec3 kS = F; + + vec3 kD = vec3(1.0) - kS; + + kD *= 1.0 - metallic; + + float NdotL = max(dot(N, L), 0.0); + + Lo += (kD * albedo / PI + specular) * radiance * NdotL; + + vec3 ambient = vec3(0.03) * albedo * ao; + + vec3 color = ambient + Lo; + + color = color / (color + vec3(1.0)); + color = pow(color, vec3(1.0/2.2)); + + FragColor = vec4(color, 1.0); +} diff --git a/cw_8/shaders/pbr.vert b/cw_8/shaders/pbr.vert new file mode 100644 index 0000000..5a0427a --- /dev/null +++ b/cw_8/shaders/pbr.vert @@ -0,0 +1,24 @@ +#version 430 core + +layout(location = 0) in vec3 vertexPosition; +layout(location = 1) in vec3 vertexNormal; +layout(location = 2) in vec2 vertexTexCoord; + +uniform mat4 transformation; +uniform mat4 modelMatrix; +uniform vec3 cameraPos; + +out vec3 Normal; +out vec3 worldPos; +out vec2 TexCoords; + +void main() +{ + + TexCoords = vertexTexCoord; + worldPos = vec3(modelMatrix * vec4(vertexPosition,1)); + Normal = normalize((modelMatrix * vec4(vertexNormal,0)).xyz); + + gl_Position = transformation * vec4(worldPos, 1.0); + +} diff --git a/cw_8/src/Box.cpp b/cw_8/src/Box.cpp new file mode 100644 index 0000000..28445d3 --- /dev/null +++ b/cw_8/src/Box.cpp @@ -0,0 +1,55 @@ + + +// dane 36 wierzcholkow i kolorow opisujace model pudelka +const float box[] = { + + // points colors + 0.25f, 0.25f, 0.75f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + 0.25f, -0.25f, 0.75f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + -0.25f, 0.25f, 0.75f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + + 0.25f, -0.25f, 0.75f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + -0.25f, -0.25f, 0.75f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + -0.25f, 0.25f, 0.75f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + + 0.25f, 0.25f, -0.75f, 1.0f, 0.8f, 0.8f, 0.8f, 1.0f, + -0.25f, 0.25f, -0.75f, 1.0f, 0.8f, 0.8f, 0.8f, 1.0f, + 0.25f, -0.25f, -0.75f, 1.0f, 0.8f, 0.8f, 0.8f, 1.0f, + + 0.25f, -0.25f, -0.75f, 1.0f, 0.8f, 0.8f, 0.8f, 1.0f, + -0.25f, 0.25f, -0.75f, 1.0f, 0.8f, 0.8f, 0.8f, 1.0f, + -0.25f, -0.25f, -0.75f, 1.0f, 0.8f, 0.8f, 0.8f, 1.0f, + + -0.25f, 0.25f, 0.75f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, + -0.25f, -0.25f, 0.75f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, + -0.25f, -0.25f, -0.75f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, + + -0.25f, 0.25f, 0.75f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, + -0.25f, -0.25f, -0.75f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, + -0.25f, 0.25f, -0.75f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, + + 0.25f, 0.25f, 0.75f, 1.0f, 0.5f, 0.5f, 0.0f, 1.0f, + 0.25f, -0.25f, -0.75f, 1.0f, 0.5f, 0.5f, 0.0f, 1.0f, + 0.25f, -0.25f, 0.75f, 1.0f, 0.5f, 0.5f, 0.0f, 1.0f, + + 0.25f, 0.25f, 0.75f, 1.0f, 0.5f, 0.5f, 0.0f, 1.0f, + 0.25f, 0.25f, -0.75f, 1.0f, 0.5f, 0.5f, 0.0f, 1.0f, + 0.25f, -0.25f, -0.75f, 1.0f, 0.5f, 0.5f, 0.0f, 1.0f, + + 0.25f, 0.25f, -0.75f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, + 0.25f, 0.25f, 0.75f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, + -0.25f, 0.25f, 0.75f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, + + 0.25f, 0.25f, -0.75f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, + -0.25f, 0.25f, 0.75f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, + -0.25f, 0.25f, -0.75f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, + + 0.25f, -0.25f, -0.75f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, + -0.25f, -0.25f, 0.75f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, + 0.25f, -0.25f, 0.75f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, + + 0.25f, -0.25f, -0.75f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, + -0.25f, -0.25f, -0.75f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, + -0.25f, -0.25f, 0.75f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, +}; + diff --git a/cw_8/src/Camera.cpp b/cw_8/src/Camera.cpp new file mode 100644 index 0000000..90cc40c --- /dev/null +++ b/cw_8/src/Camera.cpp @@ -0,0 +1,31 @@ +#include "Camera.h" + +glm::mat4 Core::createPerspectiveMatrix(float zNear, float zFar, float frustumScale) +{ + glm::mat4 perspective; + perspective[0][0] = 1.f; + perspective[1][1] = frustumScale; + perspective[2][2] = (zFar + zNear) / (zNear - zFar); + perspective[3][2] = (2 * zFar * zNear) / (zNear - zFar); + perspective[2][3] = -1; + perspective[3][3] = 0; + + return perspective; +} + +glm::mat4 Core::createViewMatrix( glm::vec3 position, glm::vec3 forward, glm::vec3 up ) +{ + glm::vec3 side = glm::cross(forward, up); + + // Trzeba pamietac o minusie przy ustawianiu osi Z kamery. + // Wynika to z tego, ze standardowa macierz perspektywiczna zaklada, ze "z przodu" jest ujemna (a nie dodatnia) czesc osi Z. + glm::mat4 cameraRotation; + cameraRotation[0][0] = side.x; cameraRotation[1][0] = side.y; cameraRotation[2][0] = side.z; + cameraRotation[0][1] = up.x; cameraRotation[1][1] = up.y; cameraRotation[2][1] = up.z; + cameraRotation[0][2] = -forward.x; cameraRotation[1][2] = -forward.y; cameraRotation[2][2] = -forward.z; + + glm::mat4 cameraTranslation; + cameraTranslation[3] = glm::vec4(-position, 1.0f); + + return cameraRotation * cameraTranslation; +} diff --git a/cw_8/src/Camera.h b/cw_8/src/Camera.h new file mode 100644 index 0000000..54c29b3 --- /dev/null +++ b/cw_8/src/Camera.h @@ -0,0 +1,14 @@ +#pragma once + +#include "glm.hpp" + +namespace Core +{ + glm::mat4 createPerspectiveMatrix(float zNear = 0.1f, float zFar = 100.0f, float frustumScale = 1.f); + + // position - pozycja kamery + // forward - wektor "do przodu" kamery (jednostkowy) + // up - wektor "w gore" kamery (jednostkowy) + // up i forward musza byc ortogonalne! + glm::mat4 createViewMatrix(glm::vec3 position, glm::vec3 forward, glm::vec3 up); +} \ No newline at end of file diff --git a/cw_8/src/Render_Utils.cpp b/cw_8/src/Render_Utils.cpp new file mode 100644 index 0000000..18f71f9 --- /dev/null +++ b/cw_8/src/Render_Utils.cpp @@ -0,0 +1,128 @@ +#include "Render_Utils.h" + +#include + +#include "glew.h" +#include "freeglut.h" +#include +#include +#include + + + +void Core::RenderContext::initFromAssimpMesh(aiMesh* mesh) { + vertexArray = 0; + vertexBuffer = 0; + vertexIndexBuffer = 0; + + std::vector textureCoord; + std::vector indices; + //tex coord must be converted to 2d vecs + for (unsigned int i = 0; i < mesh->mNumVertices; i++) + { + if (mesh->mTextureCoords[0] != nullptr) { + textureCoord.push_back(mesh->mTextureCoords[0][i].x); + textureCoord.push_back(mesh->mTextureCoords[0][i].y); + } + else { + textureCoord.push_back(0.0f); + textureCoord.push_back(0.0f); + } + } + if (mesh->mTextureCoords[0] == nullptr) { + std::cout << "no uv coords\n"; + } + for (unsigned int i = 0; i < mesh->mNumFaces; i++) + { + aiFace face = mesh->mFaces[i]; + // retrieve all indices of the face and store them in the indices vector + for (unsigned int j = 0; j < face.mNumIndices; j++) + indices.push_back(face.mIndices[j]); + } + + unsigned int vertexDataBufferSize = sizeof(float) * mesh->mNumVertices * 3; + unsigned int vertexNormalBufferSize = sizeof(float) * mesh->mNumVertices * 3; + unsigned int vertexTexBufferSize = sizeof(float) * mesh->mNumVertices * 2; + unsigned int vertexTangentBufferSize = sizeof(float) * mesh->mNumVertices * 3; + unsigned int vertexBiTangentBufferSize = sizeof(float) * mesh->mNumVertices * 3; + + unsigned int vertexElementBufferSize = sizeof(unsigned int) * indices.size(); + size = indices.size(); + + glGenVertexArrays(1, &vertexArray); + glBindVertexArray(vertexArray); + + + glGenBuffers(1, &vertexIndexBuffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vertexIndexBuffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, vertexElementBufferSize, &indices[0], GL_STATIC_DRAW); + + glGenBuffers(1, &vertexBuffer); + glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); + //std::cout << vertexBuffer; + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + glEnableVertexAttribArray(3); + glEnableVertexAttribArray(4); + + glBufferData(GL_ARRAY_BUFFER, vertexDataBufferSize + vertexNormalBufferSize + vertexTexBufferSize + vertexTangentBufferSize + vertexBiTangentBufferSize, NULL, GL_STATIC_DRAW); + + glBufferSubData(GL_ARRAY_BUFFER, 0, vertexDataBufferSize, mesh->mVertices); + + glBufferSubData(GL_ARRAY_BUFFER, vertexDataBufferSize, vertexNormalBufferSize, mesh->mNormals); + + glBufferSubData(GL_ARRAY_BUFFER, vertexDataBufferSize + vertexNormalBufferSize, vertexTexBufferSize, &textureCoord[0]); + + glBufferSubData(GL_ARRAY_BUFFER, vertexDataBufferSize + vertexNormalBufferSize + vertexTexBufferSize, vertexTangentBufferSize, mesh->mTangents); + + glBufferSubData(GL_ARRAY_BUFFER, vertexDataBufferSize + vertexNormalBufferSize + vertexTexBufferSize + vertexTangentBufferSize, vertexBiTangentBufferSize, mesh->mBitangents); + + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)(0)); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)(vertexDataBufferSize)); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, (void*)(vertexNormalBufferSize + vertexDataBufferSize)); + glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 0, (void*)(vertexDataBufferSize + vertexNormalBufferSize + vertexTexBufferSize)); + glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, 0, (void*)(vertexDataBufferSize + vertexNormalBufferSize + vertexTexBufferSize + vertexTangentBufferSize)); + +} + +void Core::DrawVertexArray(const float * vertexArray, int numVertices, int elementSize ) +{ + glVertexAttribPointer(0, elementSize, GL_FLOAT, false, 0, vertexArray); + glEnableVertexAttribArray(0); + + glDrawArrays(GL_TRIANGLES, 0, numVertices); +} + +void Core::DrawVertexArrayIndexed( const float * vertexArray, const int * indexArray, int numIndexes, int elementSize ) +{ + glVertexAttribPointer(0, elementSize, GL_FLOAT, false, 0, vertexArray); + glEnableVertexAttribArray(0); + + glDrawElements(GL_TRIANGLES, numIndexes, GL_UNSIGNED_INT, indexArray); +} + + +void Core::DrawVertexArray( const VertexData & data ) +{ + int numAttribs = std::min(VertexData::MAX_ATTRIBS, data.NumActiveAttribs); + for(int i = 0; i < numAttribs; i++) + { + glVertexAttribPointer(i, data.Attribs[i].Size, GL_FLOAT, false, 0, data.Attribs[i].Pointer); + glEnableVertexAttribArray(i); + } + glDrawArrays(GL_TRIANGLES, 0, data.NumVertices); +} + +void Core::DrawContext(Core::RenderContext& context) +{ + + glBindVertexArray(context.vertexArray); + glDrawElements( + GL_TRIANGLES, // mode + context.size, // count + GL_UNSIGNED_INT, // type + (void*)0 // element array buffer offset + ); + glBindVertexArray(0); +} diff --git a/cw_8/src/Render_Utils.h b/cw_8/src/Render_Utils.h new file mode 100644 index 0000000..052008d --- /dev/null +++ b/cw_8/src/Render_Utils.h @@ -0,0 +1,72 @@ +#pragma once +#include "glm.hpp" +#include "glew.h" +#include "objload.h" +#include +#include +#include + +#define BUFFER_OFFSET(i) ((char *)NULL + (i)) + +namespace Core +{ + struct RenderContext + { + GLuint vertexArray; + GLuint vertexBuffer; + GLuint vertexIndexBuffer; + int size = 0; + + void initFromOBJ(obj::Model& model); + + void initFromAssimpMesh(aiMesh* mesh); + }; + + // vertexArray - jednowymiarowa tablica zawierajaca wartosci opisujace pozycje kolejnych wierzcholkow w jednym ciagu (x1, y1, z1, w1, x2, y2, z2, w2, ...) + // numVertices - liczba wierzcholkow do narysowania + // elementSize - liczba wartosci opisujacych pojedynczy wierzcholek (np. 3 gdy wierzcholek opisany jest trojka (x, y, z)) + void DrawVertexArray(const float * vertexArray, int numVertices, int elementSize); + + // indexArray - jednowymiarowa tablica zawierajaca indeksy wierzcholkow kolejnych trojkatow w jednym ciagu (t1_i1, t1_i2, t1_i3, t2_i1, t2_i2, t2_i3, ...) + // numIndexes - liczba indeksow w tablicy indexArray + void DrawVertexArrayIndexed(const float * vertexArray, const int * indexArray, int numIndexes, int elementSize); + + + struct VertexAttribute + { + const void * Pointer; + int Size; + }; + + struct VertexData + { + static const int MAX_ATTRIBS = 8; + VertexAttribute Attribs[MAX_ATTRIBS]; + int NumActiveAttribs; + int NumVertices; + }; + + // Ta funkcja sluzy do rysowania trojkatow, ktorych wierzcholki moga byc opisane wiecej niz jednym atrybutem. + // Funkcja przyjmuje jako parametr strukture, w ktorej nalezy zawrzec wszystkie potrzebne dane. + // + // Przykladowe wywolanie funkcji - narysowanie trojkata jak na pierwszych zajeciach: + /* + + const float vertices[] = { + 0.25f, 0.25f, 0.0f, 1.0f, + 0.25f, -0.25f, 0.0f, 1.0f, + -0.25f, -0.25f, 0.0f, 1.0f + }; + + Core::VertexData vertexData; + vertexData.NumActiveAttribs = 1; // Liczba uzywanych atrybutow wierzcholka + vertexData.Attribs[0].Pointer = vertices; // Wskaznik na dane zerowego atrybutu + vertexData.Attribs[0].Size = 4; // Wielkosc zerowego atrybutu (ilosc liczb opisujacych ten atrybut w pojedynczym wierzcholku) + vertexData.NumVertices = 3; // Liczba wierzcholkow do narysowania + Core::DrawVertexArray(vertexData); + + */ + void DrawVertexArray(const VertexData & data); + + void DrawContext(RenderContext& context); +} \ No newline at end of file diff --git a/cw_8/src/SOIL/SOIL.c b/cw_8/src/SOIL/SOIL.c new file mode 100644 index 0000000..1ee4daf --- /dev/null +++ b/cw_8/src/SOIL/SOIL.c @@ -0,0 +1,2024 @@ +/* + Jonathan Dummer + 2007-07-26-10.36 + + Simple OpenGL Image Library + + Public Domain + using Sean Barret's stb_image as a base + + Thanks to: + * Sean Barret - for the awesome stb_image + * Dan Venkitachalam - for finding some non-compliant DDS files, and patching some explicit casts + * everybody at gamedev.net +*/ + +#define SOIL_CHECK_FOR_GL_ERRORS 0 + +#ifdef WIN32 + #define WIN32_LEAN_AND_MEAN + #include + #include + #include +#elif defined(__APPLE__) || defined(__APPLE_CC__) + /* I can't test this Apple stuff! */ + #include + #include + #define APIENTRY +#else + #include + #include +#endif + +#include "SOIL.h" +#include "stb_image_aug.h" +#include "image_helper.h" +#include "image_DXT.h" + +#include +#include + +/* error reporting */ +char *result_string_pointer = "SOIL initialized"; + +/* for loading cube maps */ +enum{ + SOIL_CAPABILITY_UNKNOWN = -1, + SOIL_CAPABILITY_NONE = 0, + SOIL_CAPABILITY_PRESENT = 1 +}; +static int has_cubemap_capability = SOIL_CAPABILITY_UNKNOWN; +int query_cubemap_capability( void ); +#define SOIL_TEXTURE_WRAP_R 0x8072 +#define SOIL_CLAMP_TO_EDGE 0x812F +#define SOIL_NORMAL_MAP 0x8511 +#define SOIL_REFLECTION_MAP 0x8512 +#define SOIL_TEXTURE_CUBE_MAP 0x8513 +#define SOIL_TEXTURE_BINDING_CUBE_MAP 0x8514 +#define SOIL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#define SOIL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 +#define SOIL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 +#define SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 +#define SOIL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 +#define SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A +#define SOIL_PROXY_TEXTURE_CUBE_MAP 0x851B +#define SOIL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C +/* for non-power-of-two texture */ +static int has_NPOT_capability = SOIL_CAPABILITY_UNKNOWN; +int query_NPOT_capability( void ); +/* for texture rectangles */ +static int has_tex_rectangle_capability = SOIL_CAPABILITY_UNKNOWN; +int query_tex_rectangle_capability( void ); +#define SOIL_TEXTURE_RECTANGLE_ARB 0x84F5 +#define SOIL_MAX_RECTANGLE_TEXTURE_SIZE_ARB 0x84F8 +/* for using DXT compression */ +static int has_DXT_capability = SOIL_CAPABILITY_UNKNOWN; +int query_DXT_capability( void ); +#define SOIL_RGB_S3TC_DXT1 0x83F0 +#define SOIL_RGBA_S3TC_DXT1 0x83F1 +#define SOIL_RGBA_S3TC_DXT3 0x83F2 +#define SOIL_RGBA_S3TC_DXT5 0x83F3 +typedef void (APIENTRY * P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid * data); +P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC soilGlCompressedTexImage2D = NULL; +unsigned int SOIL_direct_load_DDS( + const char *filename, + unsigned int reuse_texture_ID, + int flags, + int loading_as_cubemap ); +unsigned int SOIL_direct_load_DDS_from_memory( + const unsigned char *const buffer, + int buffer_length, + unsigned int reuse_texture_ID, + int flags, + int loading_as_cubemap ); +/* other functions */ +unsigned int + SOIL_internal_create_OGL_texture + ( + const unsigned char *const data, + int width, int height, int channels, + unsigned int reuse_texture_ID, + unsigned int flags, + unsigned int opengl_texture_type, + unsigned int opengl_texture_target, + unsigned int texture_check_size_enum + ); + +/* and the code magic begins here [8^) */ +unsigned int + SOIL_load_OGL_texture + ( + const char *filename, + int force_channels, + unsigned int reuse_texture_ID, + unsigned int flags + ) +{ + /* variables */ + unsigned char* img; + int width, height, channels; + unsigned int tex_id; + /* does the user want direct uploading of the image as a DDS file? */ + if( flags & SOIL_FLAG_DDS_LOAD_DIRECT ) + { + /* 1st try direct loading of the image as a DDS file + note: direct uploading will only load what is in the + DDS file, no MIPmaps will be generated, the image will + not be flipped, etc. */ + tex_id = SOIL_direct_load_DDS( filename, reuse_texture_ID, flags, 0 ); + if( tex_id ) + { + /* hey, it worked!! */ + return tex_id; + } + } + /* try to load the image */ + img = SOIL_load_image( filename, &width, &height, &channels, force_channels ); + /* channels holds the original number of channels, which may have been forced */ + if( (force_channels >= 1) && (force_channels <= 4) ) + { + channels = force_channels; + } + if( NULL == img ) + { + /* image loading failed */ + result_string_pointer = stbi_failure_reason(); + return 0; + } + /* OK, make it a texture! */ + tex_id = SOIL_internal_create_OGL_texture( + img, width, height, channels, + reuse_texture_ID, flags, + GL_TEXTURE_2D, GL_TEXTURE_2D, + GL_MAX_TEXTURE_SIZE ); + /* and nuke the image data */ + SOIL_free_image_data( img ); + /* and return the handle, such as it is */ + return tex_id; +} + +unsigned int + SOIL_load_OGL_HDR_texture + ( + const char *filename, + int fake_HDR_format, + int rescale_to_max, + unsigned int reuse_texture_ID, + unsigned int flags + ) +{ + /* variables */ + unsigned char* img; + int width, height, channels; + unsigned int tex_id; + /* no direct uploading of the image as a DDS file */ + /* error check */ + if( (fake_HDR_format != SOIL_HDR_RGBE) && + (fake_HDR_format != SOIL_HDR_RGBdivA) && + (fake_HDR_format != SOIL_HDR_RGBdivA2) ) + { + result_string_pointer = "Invalid fake HDR format specified"; + return 0; + } + /* try to load the image (only the HDR type) */ + img = stbi_hdr_load_rgbe( filename, &width, &height, &channels, 4 ); + /* channels holds the original number of channels, which may have been forced */ + if( NULL == img ) + { + /* image loading failed */ + result_string_pointer = stbi_failure_reason(); + return 0; + } + /* the load worked, do I need to convert it? */ + if( fake_HDR_format == SOIL_HDR_RGBdivA ) + { + RGBE_to_RGBdivA( img, width, height, rescale_to_max ); + } else if( fake_HDR_format == SOIL_HDR_RGBdivA2 ) + { + RGBE_to_RGBdivA2( img, width, height, rescale_to_max ); + } + /* OK, make it a texture! */ + tex_id = SOIL_internal_create_OGL_texture( + img, width, height, channels, + reuse_texture_ID, flags, + GL_TEXTURE_2D, GL_TEXTURE_2D, + GL_MAX_TEXTURE_SIZE ); + /* and nuke the image data */ + SOIL_free_image_data( img ); + /* and return the handle, such as it is */ + return tex_id; +} + +unsigned int + SOIL_load_OGL_texture_from_memory + ( + const unsigned char *const buffer, + int buffer_length, + int force_channels, + unsigned int reuse_texture_ID, + unsigned int flags + ) +{ + /* variables */ + unsigned char* img; + int width, height, channels; + unsigned int tex_id; + /* does the user want direct uploading of the image as a DDS file? */ + if( flags & SOIL_FLAG_DDS_LOAD_DIRECT ) + { + /* 1st try direct loading of the image as a DDS file + note: direct uploading will only load what is in the + DDS file, no MIPmaps will be generated, the image will + not be flipped, etc. */ + tex_id = SOIL_direct_load_DDS_from_memory( + buffer, buffer_length, + reuse_texture_ID, flags, 0 ); + if( tex_id ) + { + /* hey, it worked!! */ + return tex_id; + } + } + /* try to load the image */ + img = SOIL_load_image_from_memory( + buffer, buffer_length, + &width, &height, &channels, + force_channels ); + /* channels holds the original number of channels, which may have been forced */ + if( (force_channels >= 1) && (force_channels <= 4) ) + { + channels = force_channels; + } + if( NULL == img ) + { + /* image loading failed */ + result_string_pointer = stbi_failure_reason(); + return 0; + } + /* OK, make it a texture! */ + tex_id = SOIL_internal_create_OGL_texture( + img, width, height, channels, + reuse_texture_ID, flags, + GL_TEXTURE_2D, GL_TEXTURE_2D, + GL_MAX_TEXTURE_SIZE ); + /* and nuke the image data */ + SOIL_free_image_data( img ); + /* and return the handle, such as it is */ + return tex_id; +} + +unsigned int + SOIL_load_OGL_cubemap + ( + const char *x_pos_file, + const char *x_neg_file, + const char *y_pos_file, + const char *y_neg_file, + const char *z_pos_file, + const char *z_neg_file, + int force_channels, + unsigned int reuse_texture_ID, + unsigned int flags + ) +{ + /* variables */ + unsigned char* img; + int width, height, channels; + unsigned int tex_id; + /* error checking */ + if( (x_pos_file == NULL) || + (x_neg_file == NULL) || + (y_pos_file == NULL) || + (y_neg_file == NULL) || + (z_pos_file == NULL) || + (z_neg_file == NULL) ) + { + result_string_pointer = "Invalid cube map files list"; + return 0; + } + /* capability checking */ + if( query_cubemap_capability() != SOIL_CAPABILITY_PRESENT ) + { + result_string_pointer = "No cube map capability present"; + return 0; + } + /* 1st face: try to load the image */ + img = SOIL_load_image( x_pos_file, &width, &height, &channels, force_channels ); + /* channels holds the original number of channels, which may have been forced */ + if( (force_channels >= 1) && (force_channels <= 4) ) + { + channels = force_channels; + } + if( NULL == img ) + { + /* image loading failed */ + result_string_pointer = stbi_failure_reason(); + return 0; + } + /* upload the texture, and create a texture ID if necessary */ + tex_id = SOIL_internal_create_OGL_texture( + img, width, height, channels, + reuse_texture_ID, flags, + SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_POSITIVE_X, + SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); + /* and nuke the image data */ + SOIL_free_image_data( img ); + /* continue? */ + if( tex_id != 0 ) + { + /* 1st face: try to load the image */ + img = SOIL_load_image( x_neg_file, &width, &height, &channels, force_channels ); + /* channels holds the original number of channels, which may have been forced */ + if( (force_channels >= 1) && (force_channels <= 4) ) + { + channels = force_channels; + } + if( NULL == img ) + { + /* image loading failed */ + result_string_pointer = stbi_failure_reason(); + return 0; + } + /* upload the texture, but reuse the assigned texture ID */ + tex_id = SOIL_internal_create_OGL_texture( + img, width, height, channels, + tex_id, flags, + SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_NEGATIVE_X, + SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); + /* and nuke the image data */ + SOIL_free_image_data( img ); + } + /* continue? */ + if( tex_id != 0 ) + { + /* 1st face: try to load the image */ + img = SOIL_load_image( y_pos_file, &width, &height, &channels, force_channels ); + /* channels holds the original number of channels, which may have been forced */ + if( (force_channels >= 1) && (force_channels <= 4) ) + { + channels = force_channels; + } + if( NULL == img ) + { + /* image loading failed */ + result_string_pointer = stbi_failure_reason(); + return 0; + } + /* upload the texture, but reuse the assigned texture ID */ + tex_id = SOIL_internal_create_OGL_texture( + img, width, height, channels, + tex_id, flags, + SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_POSITIVE_Y, + SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); + /* and nuke the image data */ + SOIL_free_image_data( img ); + } + /* continue? */ + if( tex_id != 0 ) + { + /* 1st face: try to load the image */ + img = SOIL_load_image( y_neg_file, &width, &height, &channels, force_channels ); + /* channels holds the original number of channels, which may have been forced */ + if( (force_channels >= 1) && (force_channels <= 4) ) + { + channels = force_channels; + } + if( NULL == img ) + { + /* image loading failed */ + result_string_pointer = stbi_failure_reason(); + return 0; + } + /* upload the texture, but reuse the assigned texture ID */ + tex_id = SOIL_internal_create_OGL_texture( + img, width, height, channels, + tex_id, flags, + SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); + /* and nuke the image data */ + SOIL_free_image_data( img ); + } + /* continue? */ + if( tex_id != 0 ) + { + /* 1st face: try to load the image */ + img = SOIL_load_image( z_pos_file, &width, &height, &channels, force_channels ); + /* channels holds the original number of channels, which may have been forced */ + if( (force_channels >= 1) && (force_channels <= 4) ) + { + channels = force_channels; + } + if( NULL == img ) + { + /* image loading failed */ + result_string_pointer = stbi_failure_reason(); + return 0; + } + /* upload the texture, but reuse the assigned texture ID */ + tex_id = SOIL_internal_create_OGL_texture( + img, width, height, channels, + tex_id, flags, + SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_POSITIVE_Z, + SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); + /* and nuke the image data */ + SOIL_free_image_data( img ); + } + /* continue? */ + if( tex_id != 0 ) + { + /* 1st face: try to load the image */ + img = SOIL_load_image( z_neg_file, &width, &height, &channels, force_channels ); + /* channels holds the original number of channels, which may have been forced */ + if( (force_channels >= 1) && (force_channels <= 4) ) + { + channels = force_channels; + } + if( NULL == img ) + { + /* image loading failed */ + result_string_pointer = stbi_failure_reason(); + return 0; + } + /* upload the texture, but reuse the assigned texture ID */ + tex_id = SOIL_internal_create_OGL_texture( + img, width, height, channels, + tex_id, flags, + SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Z, + SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); + /* and nuke the image data */ + SOIL_free_image_data( img ); + } + /* and return the handle, such as it is */ + return tex_id; +} + +unsigned int + SOIL_load_OGL_cubemap_from_memory + ( + const unsigned char *const x_pos_buffer, + int x_pos_buffer_length, + const unsigned char *const x_neg_buffer, + int x_neg_buffer_length, + const unsigned char *const y_pos_buffer, + int y_pos_buffer_length, + const unsigned char *const y_neg_buffer, + int y_neg_buffer_length, + const unsigned char *const z_pos_buffer, + int z_pos_buffer_length, + const unsigned char *const z_neg_buffer, + int z_neg_buffer_length, + int force_channels, + unsigned int reuse_texture_ID, + unsigned int flags + ) +{ + /* variables */ + unsigned char* img; + int width, height, channels; + unsigned int tex_id; + /* error checking */ + if( (x_pos_buffer == NULL) || + (x_neg_buffer == NULL) || + (y_pos_buffer == NULL) || + (y_neg_buffer == NULL) || + (z_pos_buffer == NULL) || + (z_neg_buffer == NULL) ) + { + result_string_pointer = "Invalid cube map buffers list"; + return 0; + } + /* capability checking */ + if( query_cubemap_capability() != SOIL_CAPABILITY_PRESENT ) + { + result_string_pointer = "No cube map capability present"; + return 0; + } + /* 1st face: try to load the image */ + img = SOIL_load_image_from_memory( + x_pos_buffer, x_pos_buffer_length, + &width, &height, &channels, force_channels ); + /* channels holds the original number of channels, which may have been forced */ + if( (force_channels >= 1) && (force_channels <= 4) ) + { + channels = force_channels; + } + if( NULL == img ) + { + /* image loading failed */ + result_string_pointer = stbi_failure_reason(); + return 0; + } + /* upload the texture, and create a texture ID if necessary */ + tex_id = SOIL_internal_create_OGL_texture( + img, width, height, channels, + reuse_texture_ID, flags, + SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_POSITIVE_X, + SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); + /* and nuke the image data */ + SOIL_free_image_data( img ); + /* continue? */ + if( tex_id != 0 ) + { + /* 1st face: try to load the image */ + img = SOIL_load_image_from_memory( + x_neg_buffer, x_neg_buffer_length, + &width, &height, &channels, force_channels ); + /* channels holds the original number of channels, which may have been forced */ + if( (force_channels >= 1) && (force_channels <= 4) ) + { + channels = force_channels; + } + if( NULL == img ) + { + /* image loading failed */ + result_string_pointer = stbi_failure_reason(); + return 0; + } + /* upload the texture, but reuse the assigned texture ID */ + tex_id = SOIL_internal_create_OGL_texture( + img, width, height, channels, + tex_id, flags, + SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_NEGATIVE_X, + SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); + /* and nuke the image data */ + SOIL_free_image_data( img ); + } + /* continue? */ + if( tex_id != 0 ) + { + /* 1st face: try to load the image */ + img = SOIL_load_image_from_memory( + y_pos_buffer, y_pos_buffer_length, + &width, &height, &channels, force_channels ); + /* channels holds the original number of channels, which may have been forced */ + if( (force_channels >= 1) && (force_channels <= 4) ) + { + channels = force_channels; + } + if( NULL == img ) + { + /* image loading failed */ + result_string_pointer = stbi_failure_reason(); + return 0; + } + /* upload the texture, but reuse the assigned texture ID */ + tex_id = SOIL_internal_create_OGL_texture( + img, width, height, channels, + tex_id, flags, + SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_POSITIVE_Y, + SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); + /* and nuke the image data */ + SOIL_free_image_data( img ); + } + /* continue? */ + if( tex_id != 0 ) + { + /* 1st face: try to load the image */ + img = SOIL_load_image_from_memory( + y_neg_buffer, y_neg_buffer_length, + &width, &height, &channels, force_channels ); + /* channels holds the original number of channels, which may have been forced */ + if( (force_channels >= 1) && (force_channels <= 4) ) + { + channels = force_channels; + } + if( NULL == img ) + { + /* image loading failed */ + result_string_pointer = stbi_failure_reason(); + return 0; + } + /* upload the texture, but reuse the assigned texture ID */ + tex_id = SOIL_internal_create_OGL_texture( + img, width, height, channels, + tex_id, flags, + SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); + /* and nuke the image data */ + SOIL_free_image_data( img ); + } + /* continue? */ + if( tex_id != 0 ) + { + /* 1st face: try to load the image */ + img = SOIL_load_image_from_memory( + z_pos_buffer, z_pos_buffer_length, + &width, &height, &channels, force_channels ); + /* channels holds the original number of channels, which may have been forced */ + if( (force_channels >= 1) && (force_channels <= 4) ) + { + channels = force_channels; + } + if( NULL == img ) + { + /* image loading failed */ + result_string_pointer = stbi_failure_reason(); + return 0; + } + /* upload the texture, but reuse the assigned texture ID */ + tex_id = SOIL_internal_create_OGL_texture( + img, width, height, channels, + tex_id, flags, + SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_POSITIVE_Z, + SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); + /* and nuke the image data */ + SOIL_free_image_data( img ); + } + /* continue? */ + if( tex_id != 0 ) + { + /* 1st face: try to load the image */ + img = SOIL_load_image_from_memory( + z_neg_buffer, z_neg_buffer_length, + &width, &height, &channels, force_channels ); + /* channels holds the original number of channels, which may have been forced */ + if( (force_channels >= 1) && (force_channels <= 4) ) + { + channels = force_channels; + } + if( NULL == img ) + { + /* image loading failed */ + result_string_pointer = stbi_failure_reason(); + return 0; + } + /* upload the texture, but reuse the assigned texture ID */ + tex_id = SOIL_internal_create_OGL_texture( + img, width, height, channels, + tex_id, flags, + SOIL_TEXTURE_CUBE_MAP, SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Z, + SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); + /* and nuke the image data */ + SOIL_free_image_data( img ); + } + /* and return the handle, such as it is */ + return tex_id; +} + +unsigned int + SOIL_load_OGL_single_cubemap + ( + const char *filename, + const char face_order[6], + int force_channels, + unsigned int reuse_texture_ID, + unsigned int flags + ) +{ + /* variables */ + unsigned char* img; + int width, height, channels, i; + unsigned int tex_id = 0; + /* error checking */ + if( filename == NULL ) + { + result_string_pointer = "Invalid single cube map file name"; + return 0; + } + /* does the user want direct uploading of the image as a DDS file? */ + if( flags & SOIL_FLAG_DDS_LOAD_DIRECT ) + { + /* 1st try direct loading of the image as a DDS file + note: direct uploading will only load what is in the + DDS file, no MIPmaps will be generated, the image will + not be flipped, etc. */ + tex_id = SOIL_direct_load_DDS( filename, reuse_texture_ID, flags, 1 ); + if( tex_id ) + { + /* hey, it worked!! */ + return tex_id; + } + } + /* face order checking */ + for( i = 0; i < 6; ++i ) + { + if( (face_order[i] != 'N') && + (face_order[i] != 'S') && + (face_order[i] != 'W') && + (face_order[i] != 'E') && + (face_order[i] != 'U') && + (face_order[i] != 'D') ) + { + result_string_pointer = "Invalid single cube map face order"; + return 0; + }; + } + /* capability checking */ + if( query_cubemap_capability() != SOIL_CAPABILITY_PRESENT ) + { + result_string_pointer = "No cube map capability present"; + return 0; + } + /* 1st off, try to load the full image */ + img = SOIL_load_image( filename, &width, &height, &channels, force_channels ); + /* channels holds the original number of channels, which may have been forced */ + if( (force_channels >= 1) && (force_channels <= 4) ) + { + channels = force_channels; + } + if( NULL == img ) + { + /* image loading failed */ + result_string_pointer = stbi_failure_reason(); + return 0; + } + /* now, does this image have the right dimensions? */ + if( (width != 6*height) && + (6*width != height) ) + { + SOIL_free_image_data( img ); + result_string_pointer = "Single cubemap image must have a 6:1 ratio"; + return 0; + } + /* try the image split and create */ + tex_id = SOIL_create_OGL_single_cubemap( + img, width, height, channels, + face_order, reuse_texture_ID, flags + ); + /* nuke the temporary image data and return the texture handle */ + SOIL_free_image_data( img ); + return tex_id; +} + +unsigned int + SOIL_load_OGL_single_cubemap_from_memory + ( + const unsigned char *const buffer, + int buffer_length, + const char face_order[6], + int force_channels, + unsigned int reuse_texture_ID, + unsigned int flags + ) +{ + /* variables */ + unsigned char* img; + int width, height, channels, i; + unsigned int tex_id = 0; + /* error checking */ + if( buffer == NULL ) + { + result_string_pointer = "Invalid single cube map buffer"; + return 0; + } + /* does the user want direct uploading of the image as a DDS file? */ + if( flags & SOIL_FLAG_DDS_LOAD_DIRECT ) + { + /* 1st try direct loading of the image as a DDS file + note: direct uploading will only load what is in the + DDS file, no MIPmaps will be generated, the image will + not be flipped, etc. */ + tex_id = SOIL_direct_load_DDS_from_memory( + buffer, buffer_length, + reuse_texture_ID, flags, 1 ); + if( tex_id ) + { + /* hey, it worked!! */ + return tex_id; + } + } + /* face order checking */ + for( i = 0; i < 6; ++i ) + { + if( (face_order[i] != 'N') && + (face_order[i] != 'S') && + (face_order[i] != 'W') && + (face_order[i] != 'E') && + (face_order[i] != 'U') && + (face_order[i] != 'D') ) + { + result_string_pointer = "Invalid single cube map face order"; + return 0; + }; + } + /* capability checking */ + if( query_cubemap_capability() != SOIL_CAPABILITY_PRESENT ) + { + result_string_pointer = "No cube map capability present"; + return 0; + } + /* 1st off, try to load the full image */ + img = SOIL_load_image_from_memory( + buffer, buffer_length, + &width, &height, &channels, + force_channels ); + /* channels holds the original number of channels, which may have been forced */ + if( (force_channels >= 1) && (force_channels <= 4) ) + { + channels = force_channels; + } + if( NULL == img ) + { + /* image loading failed */ + result_string_pointer = stbi_failure_reason(); + return 0; + } + /* now, does this image have the right dimensions? */ + if( (width != 6*height) && + (6*width != height) ) + { + SOIL_free_image_data( img ); + result_string_pointer = "Single cubemap image must have a 6:1 ratio"; + return 0; + } + /* try the image split and create */ + tex_id = SOIL_create_OGL_single_cubemap( + img, width, height, channels, + face_order, reuse_texture_ID, flags + ); + /* nuke the temporary image data and return the texture handle */ + SOIL_free_image_data( img ); + return tex_id; +} + +unsigned int + SOIL_create_OGL_single_cubemap + ( + const unsigned char *const data, + int width, int height, int channels, + const char face_order[6], + unsigned int reuse_texture_ID, + unsigned int flags + ) +{ + /* variables */ + unsigned char* sub_img; + int dw, dh, sz, i; + unsigned int tex_id; + /* error checking */ + if( data == NULL ) + { + result_string_pointer = "Invalid single cube map image data"; + return 0; + } + /* face order checking */ + for( i = 0; i < 6; ++i ) + { + if( (face_order[i] != 'N') && + (face_order[i] != 'S') && + (face_order[i] != 'W') && + (face_order[i] != 'E') && + (face_order[i] != 'U') && + (face_order[i] != 'D') ) + { + result_string_pointer = "Invalid single cube map face order"; + return 0; + }; + } + /* capability checking */ + if( query_cubemap_capability() != SOIL_CAPABILITY_PRESENT ) + { + result_string_pointer = "No cube map capability present"; + return 0; + } + /* now, does this image have the right dimensions? */ + if( (width != 6*height) && + (6*width != height) ) + { + result_string_pointer = "Single cubemap image must have a 6:1 ratio"; + return 0; + } + /* which way am I stepping? */ + if( width > height ) + { + dw = height; + dh = 0; + } else + { + dw = 0; + dh = width; + } + sz = dw+dh; + sub_img = (unsigned char *)malloc( sz*sz*channels ); + /* do the splitting and uploading */ + tex_id = reuse_texture_ID; + for( i = 0; i < 6; ++i ) + { + int x, y, idx = 0; + unsigned int cubemap_target = 0; + /* copy in the sub-image */ + for( y = i*dh; y < i*dh+sz; ++y ) + { + for( x = i*dw*channels; x < (i*dw+sz)*channels; ++x ) + { + sub_img[idx++] = data[y*width*channels+x]; + } + } + /* what is my texture target? + remember, this coordinate system is + LHS if viewed from inside the cube! */ + switch( face_order[i] ) + { + case 'N': + cubemap_target = SOIL_TEXTURE_CUBE_MAP_POSITIVE_Z; + break; + case 'S': + cubemap_target = SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Z; + break; + case 'W': + cubemap_target = SOIL_TEXTURE_CUBE_MAP_NEGATIVE_X; + break; + case 'E': + cubemap_target = SOIL_TEXTURE_CUBE_MAP_POSITIVE_X; + break; + case 'U': + cubemap_target = SOIL_TEXTURE_CUBE_MAP_POSITIVE_Y; + break; + case 'D': + cubemap_target = SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Y; + break; + } + /* upload it as a texture */ + tex_id = SOIL_internal_create_OGL_texture( + sub_img, sz, sz, channels, + tex_id, flags, + SOIL_TEXTURE_CUBE_MAP, + cubemap_target, + SOIL_MAX_CUBE_MAP_TEXTURE_SIZE ); + } + /* and nuke the image and sub-image data */ + SOIL_free_image_data( sub_img ); + /* and return the handle, such as it is */ + return tex_id; +} + +unsigned int + SOIL_create_OGL_texture + ( + const unsigned char *const data, + int width, int height, int channels, + unsigned int reuse_texture_ID, + unsigned int flags + ) +{ + /* wrapper function for 2D textures */ + return SOIL_internal_create_OGL_texture( + data, width, height, channels, + reuse_texture_ID, flags, + GL_TEXTURE_2D, GL_TEXTURE_2D, + GL_MAX_TEXTURE_SIZE ); +} + +#if SOIL_CHECK_FOR_GL_ERRORS +void check_for_GL_errors( const char *calling_location ) +{ + /* check for errors */ + GLenum err_code = glGetError(); + while( GL_NO_ERROR != err_code ) + { + printf( "OpenGL Error @ %s: %i", calling_location, err_code ); + err_code = glGetError(); + } +} +#else +void check_for_GL_errors( const char *calling_location ) +{ + /* no check for errors */ +} +#endif + +unsigned int + SOIL_internal_create_OGL_texture + ( + const unsigned char *const data, + int width, int height, int channels, + unsigned int reuse_texture_ID, + unsigned int flags, + unsigned int opengl_texture_type, + unsigned int opengl_texture_target, + unsigned int texture_check_size_enum + ) +{ + /* variables */ + unsigned char* img; + unsigned int tex_id; + unsigned int internal_texture_format = 0, original_texture_format = 0; + int DXT_mode = SOIL_CAPABILITY_UNKNOWN; + int max_supported_size; + /* If the user wants to use the texture rectangle I kill a few flags */ + if( flags & SOIL_FLAG_TEXTURE_RECTANGLE ) + { + /* well, the user asked for it, can we do that? */ + if( query_tex_rectangle_capability() == SOIL_CAPABILITY_PRESENT ) + { + /* only allow this if the user in _NOT_ trying to do a cubemap! */ + if( opengl_texture_type == GL_TEXTURE_2D ) + { + /* clean out the flags that cannot be used with texture rectangles */ + flags &= ~( + SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_MIPMAPS | + SOIL_FLAG_TEXTURE_REPEATS + ); + /* and change my target */ + opengl_texture_target = SOIL_TEXTURE_RECTANGLE_ARB; + opengl_texture_type = SOIL_TEXTURE_RECTANGLE_ARB; + } else + { + /* not allowed for any other uses (yes, I'm looking at you, cubemaps!) */ + flags &= ~SOIL_FLAG_TEXTURE_RECTANGLE; + } + + } else + { + /* can't do it, and that is a breakable offense (uv coords use pixels instead of [0,1]!) */ + result_string_pointer = "Texture Rectangle extension unsupported"; + return 0; + } + } + /* create a copy the image data */ + img = (unsigned char*)malloc( width*height*channels ); + memcpy( img, data, width*height*channels ); + /* does the user want me to invert the image? */ + if( flags & SOIL_FLAG_INVERT_Y ) + { + int i, j; + for( j = 0; j*2 < height; ++j ) + { + int index1 = j * width * channels; + int index2 = (height - 1 - j) * width * channels; + for( i = width * channels; i > 0; --i ) + { + unsigned char temp = img[index1]; + img[index1] = img[index2]; + img[index2] = temp; + ++index1; + ++index2; + } + } + } + /* does the user want me to scale the colors into the NTSC safe RGB range? */ + if( flags & SOIL_FLAG_NTSC_SAFE_RGB ) + { + scale_image_RGB_to_NTSC_safe( img, width, height, channels ); + } + /* does the user want me to convert from straight to pre-multiplied alpha? + (and do we even _have_ alpha?) */ + if( flags & SOIL_FLAG_MULTIPLY_ALPHA ) + { + int i; + switch( channels ) + { + case 2: + for( i = 0; i < 2*width*height; i += 2 ) + { + img[i] = (img[i] * img[i+1] + 128) >> 8; + } + break; + case 4: + for( i = 0; i < 4*width*height; i += 4 ) + { + img[i+0] = (img[i+0] * img[i+3] + 128) >> 8; + img[i+1] = (img[i+1] * img[i+3] + 128) >> 8; + img[i+2] = (img[i+2] * img[i+3] + 128) >> 8; + } + break; + default: + /* no other number of channels contains alpha data */ + break; + } + } + /* if the user can't support NPOT textures, make sure we force the POT option */ + if( (query_NPOT_capability() == SOIL_CAPABILITY_NONE) && + !(flags & SOIL_FLAG_TEXTURE_RECTANGLE) ) + { + /* add in the POT flag */ + flags |= SOIL_FLAG_POWER_OF_TWO; + } + /* how large of a texture can this OpenGL implementation handle? */ + /* texture_check_size_enum will be GL_MAX_TEXTURE_SIZE or SOIL_MAX_CUBE_MAP_TEXTURE_SIZE */ + glGetIntegerv( texture_check_size_enum, &max_supported_size ); + /* do I need to make it a power of 2? */ + if( + (flags & SOIL_FLAG_POWER_OF_TWO) || /* user asked for it */ + (flags & SOIL_FLAG_MIPMAPS) || /* need it for the MIP-maps */ + (width > max_supported_size) || /* it's too big, (make sure it's */ + (height > max_supported_size) ) /* 2^n for later down-sampling) */ + { + int new_width = 1; + int new_height = 1; + while( new_width < width ) + { + new_width *= 2; + } + while( new_height < height ) + { + new_height *= 2; + } + /* still? */ + if( (new_width != width) || (new_height != height) ) + { + /* yep, resize */ + unsigned char *resampled = (unsigned char*)malloc( channels*new_width*new_height ); + up_scale_image( + img, width, height, channels, + resampled, new_width, new_height ); + /* OJO this is for debug only! */ + /* + SOIL_save_image( "\\showme.bmp", SOIL_SAVE_TYPE_BMP, + new_width, new_height, channels, + resampled ); + */ + /* nuke the old guy, then point it at the new guy */ + SOIL_free_image_data( img ); + img = resampled; + width = new_width; + height = new_height; + } + } + /* now, if it is too large... */ + if( (width > max_supported_size) || (height > max_supported_size) ) + { + /* I've already made it a power of two, so simply use the MIPmapping + code to reduce its size to the allowable maximum. */ + unsigned char *resampled; + int reduce_block_x = 1, reduce_block_y = 1; + int new_width, new_height; + if( width > max_supported_size ) + { + reduce_block_x = width / max_supported_size; + } + if( height > max_supported_size ) + { + reduce_block_y = height / max_supported_size; + } + new_width = width / reduce_block_x; + new_height = height / reduce_block_y; + resampled = (unsigned char*)malloc( channels*new_width*new_height ); + /* perform the actual reduction */ + mipmap_image( img, width, height, channels, + resampled, reduce_block_x, reduce_block_y ); + /* nuke the old guy, then point it at the new guy */ + SOIL_free_image_data( img ); + img = resampled; + width = new_width; + height = new_height; + } + /* does the user want us to use YCoCg color space? */ + if( flags & SOIL_FLAG_CoCg_Y ) + { + /* this will only work with RGB and RGBA images */ + convert_RGB_to_YCoCg( img, width, height, channels ); + /* + save_image_as_DDS( "CoCg_Y.dds", width, height, channels, img ); + */ + } + /* create the OpenGL texture ID handle + (note: allowing a forced texture ID lets me reload a texture) */ + tex_id = reuse_texture_ID; + if( tex_id == 0 ) + { + glGenTextures( 1, &tex_id ); + } + check_for_GL_errors( "glGenTextures" ); + /* Note: sometimes glGenTextures fails (usually no OpenGL context) */ + if( tex_id ) + { + /* and what type am I using as the internal texture format? */ + switch( channels ) + { + case 1: + original_texture_format = GL_LUMINANCE; + break; + case 2: + original_texture_format = GL_LUMINANCE_ALPHA; + break; + case 3: + original_texture_format = GL_RGB; + break; + case 4: + original_texture_format = GL_RGBA; + break; + } + internal_texture_format = original_texture_format; + /* does the user want me to, and can I, save as DXT? */ + if( flags & SOIL_FLAG_COMPRESS_TO_DXT ) + { + DXT_mode = query_DXT_capability(); + if( DXT_mode == SOIL_CAPABILITY_PRESENT ) + { + /* I can use DXT, whether I compress it or OpenGL does */ + if( (channels & 1) == 1 ) + { + /* 1 or 3 channels = DXT1 */ + internal_texture_format = SOIL_RGB_S3TC_DXT1; + } else + { + /* 2 or 4 channels = DXT5 */ + internal_texture_format = SOIL_RGBA_S3TC_DXT5; + } + } + } + /* bind an OpenGL texture ID */ + glBindTexture( opengl_texture_type, tex_id ); + check_for_GL_errors( "glBindTexture" ); + /* upload the main image */ + if( DXT_mode == SOIL_CAPABILITY_PRESENT ) + { + /* user wants me to do the DXT conversion! */ + int DDS_size; + unsigned char *DDS_data = NULL; + if( (channels & 1) == 1 ) + { + /* RGB, use DXT1 */ + DDS_data = convert_image_to_DXT1( img, width, height, channels, &DDS_size ); + } else + { + /* RGBA, use DXT5 */ + DDS_data = convert_image_to_DXT5( img, width, height, channels, &DDS_size ); + } + if( DDS_data ) + { + soilGlCompressedTexImage2D( + opengl_texture_target, 0, + internal_texture_format, width, height, 0, + DDS_size, DDS_data ); + check_for_GL_errors( "glCompressedTexImage2D" ); + SOIL_free_image_data( DDS_data ); + /* printf( "Internal DXT compressor\n" ); */ + } else + { + /* my compression failed, try the OpenGL driver's version */ + glTexImage2D( + opengl_texture_target, 0, + internal_texture_format, width, height, 0, + original_texture_format, GL_UNSIGNED_BYTE, img ); + check_for_GL_errors( "glTexImage2D" ); + /* printf( "OpenGL DXT compressor\n" ); */ + } + } else + { + /* user want OpenGL to do all the work! */ + glTexImage2D( + opengl_texture_target, 0, + internal_texture_format, width, height, 0, + original_texture_format, GL_UNSIGNED_BYTE, img ); + check_for_GL_errors( "glTexImage2D" ); + /*printf( "OpenGL DXT compressor\n" ); */ + } + /* are any MIPmaps desired? */ + if( flags & SOIL_FLAG_MIPMAPS ) + { + int MIPlevel = 1; + int MIPwidth = (width+1) / 2; + int MIPheight = (height+1) / 2; + unsigned char *resampled = (unsigned char*)malloc( channels*MIPwidth*MIPheight ); + while( ((1< 0; --i ) + { + unsigned char temp = pixel_data[index1]; + pixel_data[index1] = pixel_data[index2]; + pixel_data[index2] = temp; + ++index1; + ++index2; + } + } + + /* save the image */ + save_result = SOIL_save_image( filename, image_type, width, height, 3, pixel_data); + + /* And free the memory */ + SOIL_free_image_data( pixel_data ); + return save_result; +} + +unsigned char* + SOIL_load_image + ( + const char *filename, + int *width, int *height, int *channels, + int force_channels + ) +{ + unsigned char *result = stbi_load( filename, + width, height, channels, force_channels ); + if( result == NULL ) + { + result_string_pointer = stbi_failure_reason(); + } else + { + result_string_pointer = "Image loaded"; + } + return result; +} + +unsigned char* + SOIL_load_image_from_memory + ( + const unsigned char *const buffer, + int buffer_length, + int *width, int *height, int *channels, + int force_channels + ) +{ + unsigned char *result = stbi_load_from_memory( + buffer, buffer_length, + width, height, channels, + force_channels ); + if( result == NULL ) + { + result_string_pointer = stbi_failure_reason(); + } else + { + result_string_pointer = "Image loaded from memory"; + } + return result; +} + +int + SOIL_save_image + ( + const char *filename, + int image_type, + int width, int height, int channels, + const unsigned char *const data + ) +{ + int save_result; + + /* error check */ + if( (width < 1) || (height < 1) || + (channels < 1) || (channels > 4) || + (data == NULL) || + (filename == NULL) ) + { + return 0; + } + if( image_type == SOIL_SAVE_TYPE_BMP ) + { + save_result = stbi_write_bmp( filename, + width, height, channels, (void*)data ); + } else + if( image_type == SOIL_SAVE_TYPE_TGA ) + { + save_result = stbi_write_tga( filename, + width, height, channels, (void*)data ); + } else + if( image_type == SOIL_SAVE_TYPE_DDS ) + { + save_result = save_image_as_DDS( filename, + width, height, channels, (const unsigned char *const)data ); + } else + { + save_result = 0; + } + if( save_result == 0 ) + { + result_string_pointer = "Saving the image failed"; + } else + { + result_string_pointer = "Image saved"; + } + return save_result; +} + +void + SOIL_free_image_data + ( + unsigned char *img_data + ) +{ + free( (void*)img_data ); +} + +const char* + SOIL_last_result + ( + void + ) +{ + return result_string_pointer; +} + +unsigned int SOIL_direct_load_DDS_from_memory( + const unsigned char *const buffer, + int buffer_length, + unsigned int reuse_texture_ID, + int flags, + int loading_as_cubemap ) +{ + /* variables */ + DDS_header header; + unsigned int buffer_index = 0; + unsigned int tex_ID = 0; + /* file reading variables */ + unsigned int S3TC_type = 0; + unsigned char *DDS_data; + unsigned int DDS_main_size; + unsigned int DDS_full_size; + unsigned int width, height; + int mipmaps, cubemap, uncompressed, block_size = 16; + unsigned int flag; + unsigned int cf_target, ogl_target_start, ogl_target_end; + unsigned int opengl_texture_type; + int i; + /* 1st off, does the filename even exist? */ + if( NULL == buffer ) + { + /* we can't do it! */ + result_string_pointer = "NULL buffer"; + return 0; + } + if( buffer_length < sizeof( DDS_header ) ) + { + /* we can't do it! */ + result_string_pointer = "DDS file was too small to contain the DDS header"; + return 0; + } + /* try reading in the header */ + memcpy ( (void*)(&header), (const void *)buffer, sizeof( DDS_header ) ); + buffer_index = sizeof( DDS_header ); + /* guilty until proven innocent */ + result_string_pointer = "Failed to read a known DDS header"; + /* validate the header (warning, "goto"'s ahead, shield your eyes!!) */ + flag = ('D'<<0)|('D'<<8)|('S'<<16)|(' '<<24); + if( header.dwMagic != flag ) {goto quick_exit;} + if( header.dwSize != 124 ) {goto quick_exit;} + /* I need all of these */ + flag = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT; + if( (header.dwFlags & flag) != flag ) {goto quick_exit;} + /* According to the MSDN spec, the dwFlags should contain + DDSD_LINEARSIZE if it's compressed, or DDSD_PITCH if + uncompressed. Some DDS writers do not conform to the + spec, so I need to make my reader more tolerant */ + /* I need one of these */ + flag = DDPF_FOURCC | DDPF_RGB; + if( (header.sPixelFormat.dwFlags & flag) == 0 ) {goto quick_exit;} + if( header.sPixelFormat.dwSize != 32 ) {goto quick_exit;} + if( (header.sCaps.dwCaps1 & DDSCAPS_TEXTURE) == 0 ) {goto quick_exit;} + /* make sure it is a type we can upload */ + if( (header.sPixelFormat.dwFlags & DDPF_FOURCC) && + !( + (header.sPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('T'<<16)|('1'<<24))) || + (header.sPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('T'<<16)|('3'<<24))) || + (header.sPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('T'<<16)|('5'<<24))) + ) ) + { + goto quick_exit; + } + /* OK, validated the header, let's load the image data */ + result_string_pointer = "DDS header loaded and validated"; + width = header.dwWidth; + height = header.dwHeight; + uncompressed = 1 - (header.sPixelFormat.dwFlags & DDPF_FOURCC) / DDPF_FOURCC; + cubemap = (header.sCaps.dwCaps2 & DDSCAPS2_CUBEMAP) / DDSCAPS2_CUBEMAP; + if( uncompressed ) + { + S3TC_type = GL_RGB; + block_size = 3; + if( header.sPixelFormat.dwFlags & DDPF_ALPHAPIXELS ) + { + S3TC_type = GL_RGBA; + block_size = 4; + } + DDS_main_size = width * height * block_size; + } else + { + /* can we even handle direct uploading to OpenGL DXT compressed images? */ + if( query_DXT_capability() != SOIL_CAPABILITY_PRESENT ) + { + /* we can't do it! */ + result_string_pointer = "Direct upload of S3TC images not supported by the OpenGL driver"; + return 0; + } + /* well, we know it is DXT1/3/5, because we checked above */ + switch( (header.sPixelFormat.dwFourCC >> 24) - '0' ) + { + case 1: + S3TC_type = SOIL_RGBA_S3TC_DXT1; + block_size = 8; + break; + case 3: + S3TC_type = SOIL_RGBA_S3TC_DXT3; + block_size = 16; + break; + case 5: + S3TC_type = SOIL_RGBA_S3TC_DXT5; + block_size = 16; + break; + } + DDS_main_size = ((width+3)>>2)*((height+3)>>2)*block_size; + } + if( cubemap ) + { + /* does the user want a cubemap? */ + if( !loading_as_cubemap ) + { + /* we can't do it! */ + result_string_pointer = "DDS image was a cubemap"; + return 0; + } + /* can we even handle cubemaps with the OpenGL driver? */ + if( query_cubemap_capability() != SOIL_CAPABILITY_PRESENT ) + { + /* we can't do it! */ + result_string_pointer = "Direct upload of cubemap images not supported by the OpenGL driver"; + return 0; + } + ogl_target_start = SOIL_TEXTURE_CUBE_MAP_POSITIVE_X; + ogl_target_end = SOIL_TEXTURE_CUBE_MAP_NEGATIVE_Z; + opengl_texture_type = SOIL_TEXTURE_CUBE_MAP; + } else + { + /* does the user want a non-cubemap? */ + if( loading_as_cubemap ) + { + /* we can't do it! */ + result_string_pointer = "DDS image was not a cubemap"; + return 0; + } + ogl_target_start = GL_TEXTURE_2D; + ogl_target_end = GL_TEXTURE_2D; + opengl_texture_type = GL_TEXTURE_2D; + } + if( (header.sCaps.dwCaps1 & DDSCAPS_MIPMAP) && (header.dwMipMapCount > 1) ) + { + int shift_offset; + mipmaps = header.dwMipMapCount - 1; + DDS_full_size = DDS_main_size; + if( uncompressed ) + { + /* uncompressed DDS, simple MIPmap size calculation */ + shift_offset = 0; + } else + { + /* compressed DDS, MIPmap size calculation is block based */ + shift_offset = 2; + } + for( i = 1; i <= mipmaps; ++ i ) + { + int w, h; + w = width >> (shift_offset + i); + h = height >> (shift_offset + i); + if( w < 1 ) + { + w = 1; + } + if( h < 1 ) + { + h = 1; + } + DDS_full_size += w*h*block_size; + } + } else + { + mipmaps = 0; + DDS_full_size = DDS_main_size; + } + DDS_data = (unsigned char*)malloc( DDS_full_size ); + /* got the image data RAM, create or use an existing OpenGL texture handle */ + tex_ID = reuse_texture_ID; + if( tex_ID == 0 ) + { + glGenTextures( 1, &tex_ID ); + } + /* bind an OpenGL texture ID */ + glBindTexture( opengl_texture_type, tex_ID ); + /* do this for each face of the cubemap! */ + for( cf_target = ogl_target_start; cf_target <= ogl_target_end; ++cf_target ) + { + if( buffer_index + DDS_full_size <= buffer_length ) + { + unsigned int byte_offset = DDS_main_size; + memcpy( (void*)DDS_data, (const void*)(&buffer[buffer_index]), DDS_full_size ); + buffer_index += DDS_full_size; + /* upload the main chunk */ + if( uncompressed ) + { + /* and remember, DXT uncompressed uses BGR(A), + so swap to RGB(A) for ALL MIPmap levels */ + for( i = 0; i < DDS_full_size; i += block_size ) + { + unsigned char temp = DDS_data[i]; + DDS_data[i] = DDS_data[i+2]; + DDS_data[i+2] = temp; + } + glTexImage2D( + cf_target, 0, + S3TC_type, width, height, 0, + S3TC_type, GL_UNSIGNED_BYTE, DDS_data ); + } else + { + soilGlCompressedTexImage2D( + cf_target, 0, + S3TC_type, width, height, 0, + DDS_main_size, DDS_data ); + } + /* upload the mipmaps, if we have them */ + for( i = 1; i <= mipmaps; ++i ) + { + int w, h, mip_size; + w = width >> i; + h = height >> i; + if( w < 1 ) + { + w = 1; + } + if( h < 1 ) + { + h = 1; + } + /* upload this mipmap */ + if( uncompressed ) + { + mip_size = w*h*block_size; + glTexImage2D( + cf_target, i, + S3TC_type, w, h, 0, + S3TC_type, GL_UNSIGNED_BYTE, &DDS_data[byte_offset] ); + } else + { + mip_size = ((w+3)/4)*((h+3)/4)*block_size; + soilGlCompressedTexImage2D( + cf_target, i, + S3TC_type, w, h, 0, + mip_size, &DDS_data[byte_offset] ); + } + /* and move to the next mipmap */ + byte_offset += mip_size; + } + /* it worked! */ + result_string_pointer = "DDS file loaded"; + } else + { + glDeleteTextures( 1, & tex_ID ); + tex_ID = 0; + cf_target = ogl_target_end + 1; + result_string_pointer = "DDS file was too small for expected image data"; + } + }/* end reading each face */ + SOIL_free_image_data( DDS_data ); + if( tex_ID ) + { + /* did I have MIPmaps? */ + if( mipmaps > 0 ) + { + /* instruct OpenGL to use the MIPmaps */ + glTexParameteri( opengl_texture_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( opengl_texture_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); + } else + { + /* instruct OpenGL _NOT_ to use the MIPmaps */ + glTexParameteri( opengl_texture_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( opengl_texture_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + } + /* does the user want clamping, or wrapping? */ + if( flags & SOIL_FLAG_TEXTURE_REPEATS ) + { + glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_T, GL_REPEAT ); + glTexParameteri( opengl_texture_type, SOIL_TEXTURE_WRAP_R, GL_REPEAT ); + } else + { + /* unsigned int clamp_mode = SOIL_CLAMP_TO_EDGE; */ + unsigned int clamp_mode = GL_CLAMP; + glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_S, clamp_mode ); + glTexParameteri( opengl_texture_type, GL_TEXTURE_WRAP_T, clamp_mode ); + glTexParameteri( opengl_texture_type, SOIL_TEXTURE_WRAP_R, clamp_mode ); + } + } + +quick_exit: + /* report success or failure */ + return tex_ID; +} + +unsigned int SOIL_direct_load_DDS( + const char *filename, + unsigned int reuse_texture_ID, + int flags, + int loading_as_cubemap ) +{ + FILE *f; + unsigned char *buffer; + size_t buffer_length, bytes_read; + unsigned int tex_ID = 0; + /* error checks */ + if( NULL == filename ) + { + result_string_pointer = "NULL filename"; + return 0; + } + f = fopen( filename, "rb" ); + if( NULL == f ) + { + /* the file doesn't seem to exist (or be open-able) */ + result_string_pointer = "Can not find DDS file"; + return 0; + } + fseek( f, 0, SEEK_END ); + buffer_length = ftell( f ); + fseek( f, 0, SEEK_SET ); + buffer = (unsigned char *) malloc( buffer_length ); + if( NULL == buffer ) + { + result_string_pointer = "malloc failed"; + fclose( f ); + return 0; + } + bytes_read = fread( (void*)buffer, 1, buffer_length, f ); + fclose( f ); + if( bytes_read < buffer_length ) + { + /* huh? */ + buffer_length = bytes_read; + } + /* now try to do the loading */ + tex_ID = SOIL_direct_load_DDS_from_memory( + (const unsigned char *const)buffer, buffer_length, + reuse_texture_ID, flags, loading_as_cubemap ); + SOIL_free_image_data( buffer ); + return tex_ID; +} + +int query_NPOT_capability( void ) +{ + /* check for the capability */ + if( has_NPOT_capability == SOIL_CAPABILITY_UNKNOWN ) + { + /* we haven't yet checked for the capability, do so */ + if( + (NULL == strstr( (char const*)glGetString( GL_EXTENSIONS ), + "GL_ARB_texture_non_power_of_two" ) ) + ) + { + /* not there, flag the failure */ + has_NPOT_capability = SOIL_CAPABILITY_NONE; + } else + { + /* it's there! */ + has_NPOT_capability = SOIL_CAPABILITY_PRESENT; + } + } + /* let the user know if we can do non-power-of-two textures or not */ + return has_NPOT_capability; +} + +int query_tex_rectangle_capability( void ) +{ + /* check for the capability */ + if( has_tex_rectangle_capability == SOIL_CAPABILITY_UNKNOWN ) + { + /* we haven't yet checked for the capability, do so */ + if( + (NULL == strstr( (char const*)glGetString( GL_EXTENSIONS ), + "GL_ARB_texture_rectangle" ) ) + && + (NULL == strstr( (char const*)glGetString( GL_EXTENSIONS ), + "GL_EXT_texture_rectangle" ) ) + && + (NULL == strstr( (char const*)glGetString( GL_EXTENSIONS ), + "GL_NV_texture_rectangle" ) ) + ) + { + /* not there, flag the failure */ + has_tex_rectangle_capability = SOIL_CAPABILITY_NONE; + } else + { + /* it's there! */ + has_tex_rectangle_capability = SOIL_CAPABILITY_PRESENT; + } + } + /* let the user know if we can do texture rectangles or not */ + return has_tex_rectangle_capability; +} + +int query_cubemap_capability( void ) +{ + /* check for the capability */ + if( has_cubemap_capability == SOIL_CAPABILITY_UNKNOWN ) + { + /* we haven't yet checked for the capability, do so */ + if( + (NULL == strstr( (char const*)glGetString( GL_EXTENSIONS ), + "GL_ARB_texture_cube_map" ) ) + && + (NULL == strstr( (char const*)glGetString( GL_EXTENSIONS ), + "GL_EXT_texture_cube_map" ) ) + ) + { + /* not there, flag the failure */ + has_cubemap_capability = SOIL_CAPABILITY_NONE; + } else + { + /* it's there! */ + has_cubemap_capability = SOIL_CAPABILITY_PRESENT; + } + } + /* let the user know if we can do cubemaps or not */ + return has_cubemap_capability; +} + +int query_DXT_capability( void ) +{ + /* check for the capability */ + if( has_DXT_capability == SOIL_CAPABILITY_UNKNOWN ) + { + /* we haven't yet checked for the capability, do so */ + if( NULL == strstr( + (char const*)glGetString( GL_EXTENSIONS ), + "GL_EXT_texture_compression_s3tc" ) ) + { + /* not there, flag the failure */ + has_DXT_capability = SOIL_CAPABILITY_NONE; + } else + { + /* and find the address of the extension function */ + P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC ext_addr = NULL; + #ifdef WIN32 + ext_addr = (P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC) + wglGetProcAddress + ( + "glCompressedTexImage2DARB" + ); + #elif defined(__APPLE__) || defined(__APPLE_CC__) + /* I can't test this Apple stuff! */ + CFBundleRef bundle; + CFURLRef bundleURL = + CFURLCreateWithFileSystemPath( + kCFAllocatorDefault, + CFSTR("/System/Library/Frameworks/OpenGL.framework"), + kCFURLPOSIXPathStyle, + true ); + CFStringRef extensionName = + CFStringCreateWithCString( + kCFAllocatorDefault, + "glCompressedTexImage2DARB", + kCFStringEncodingASCII ); + bundle = CFBundleCreate( kCFAllocatorDefault, bundleURL ); + assert( bundle != NULL ); + ext_addr = (P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC) + CFBundleGetFunctionPointerForName + ( + bundle, extensionName + ); + CFRelease( bundleURL ); + CFRelease( extensionName ); + CFRelease( bundle ); + #else + ext_addr = (P_SOIL_GLCOMPRESSEDTEXIMAGE2DPROC) + glXGetProcAddressARB + ( + (const GLubyte *)"glCompressedTexImage2DARB" + ); + #endif + /* Flag it so no checks needed later */ + if( NULL == ext_addr ) + { + /* hmm, not good!! This should not happen, but does on my + laptop's VIA chipset. The GL_EXT_texture_compression_s3tc + spec requires that ARB_texture_compression be present too. + this means I can upload and have the OpenGL drive do the + conversion, but I can't use my own routines or load DDS files + from disk and upload them directly [8^( */ + has_DXT_capability = SOIL_CAPABILITY_NONE; + } else + { + /* all's well! */ + soilGlCompressedTexImage2D = ext_addr; + has_DXT_capability = SOIL_CAPABILITY_PRESENT; + } + } + } + /* let the user know if we can do DXT or not */ + return has_DXT_capability; +} diff --git a/cw_8/src/SOIL/SOIL.h b/cw_8/src/SOIL/SOIL.h new file mode 100644 index 0000000..43f634f --- /dev/null +++ b/cw_8/src/SOIL/SOIL.h @@ -0,0 +1,433 @@ +/** + @mainpage SOIL + + Jonathan Dummer + 2007-07-26-10.36 + + Simple OpenGL Image Library + + A tiny c library for uploading images as + textures into OpenGL. Also saving and + loading of images is supported. + + I'm using Sean's Tool Box image loader as a base: + http://www.nothings.org/ + + I'm upgrading it to load TGA and DDS files, and a direct + path for loading DDS files straight into OpenGL textures, + when applicable. + + Image Formats: + - BMP load & save + - TGA load & save + - DDS load & save + - PNG load + - JPG load + + OpenGL Texture Features: + - resample to power-of-two sizes + - MIPmap generation + - compressed texture S3TC formats (if supported) + - can pre-multiply alpha for you, for better compositing + - can flip image about the y-axis (except pre-compressed DDS files) + + Thanks to: + * Sean Barret - for the awesome stb_image + * Dan Venkitachalam - for finding some non-compliant DDS files, and patching some explicit casts + * everybody at gamedev.net +**/ + +#ifndef HEADER_SIMPLE_OPENGL_IMAGE_LIBRARY +#define HEADER_SIMPLE_OPENGL_IMAGE_LIBRARY + +#ifdef __cplusplus +extern "C" { +#endif + +/** + The format of images that may be loaded (force_channels). + SOIL_LOAD_AUTO leaves the image in whatever format it was found. + SOIL_LOAD_L forces the image to load as Luminous (greyscale) + SOIL_LOAD_LA forces the image to load as Luminous with Alpha + SOIL_LOAD_RGB forces the image to load as Red Green Blue + SOIL_LOAD_RGBA forces the image to load as Red Green Blue Alpha +**/ +enum +{ + SOIL_LOAD_AUTO = 0, + SOIL_LOAD_L = 1, + SOIL_LOAD_LA = 2, + SOIL_LOAD_RGB = 3, + SOIL_LOAD_RGBA = 4 +}; + +/** + Passed in as reuse_texture_ID, will cause SOIL to + register a new texture ID using glGenTextures(). + If the value passed into reuse_texture_ID > 0 then + SOIL will just re-use that texture ID (great for + reloading image assets in-game!) +**/ +enum +{ + SOIL_CREATE_NEW_ID = 0 +}; + +/** + flags you can pass into SOIL_load_OGL_texture() + and SOIL_create_OGL_texture(). + (note that if SOIL_FLAG_DDS_LOAD_DIRECT is used + the rest of the flags with the exception of + SOIL_FLAG_TEXTURE_REPEATS will be ignored while + loading already-compressed DDS files.) + + SOIL_FLAG_POWER_OF_TWO: force the image to be POT + SOIL_FLAG_MIPMAPS: generate mipmaps for the texture + SOIL_FLAG_TEXTURE_REPEATS: otherwise will clamp + SOIL_FLAG_MULTIPLY_ALPHA: for using (GL_ONE,GL_ONE_MINUS_SRC_ALPHA) blending + SOIL_FLAG_INVERT_Y: flip the image vertically + SOIL_FLAG_COMPRESS_TO_DXT: if the card can display them, will convert RGB to DXT1, RGBA to DXT5 + SOIL_FLAG_DDS_LOAD_DIRECT: will load DDS files directly without _ANY_ additional processing + SOIL_FLAG_NTSC_SAFE_RGB: clamps RGB components to the range [16,235] + SOIL_FLAG_CoCg_Y: Google YCoCg; RGB=>CoYCg, RGBA=>CoCgAY + SOIL_FLAG_TEXTURE_RECTANGE: uses ARB_texture_rectangle ; pixel indexed & no repeat or MIPmaps or cubemaps +**/ +enum +{ + SOIL_FLAG_POWER_OF_TWO = 1, + SOIL_FLAG_MIPMAPS = 2, + SOIL_FLAG_TEXTURE_REPEATS = 4, + SOIL_FLAG_MULTIPLY_ALPHA = 8, + SOIL_FLAG_INVERT_Y = 16, + SOIL_FLAG_COMPRESS_TO_DXT = 32, + SOIL_FLAG_DDS_LOAD_DIRECT = 64, + SOIL_FLAG_NTSC_SAFE_RGB = 128, + SOIL_FLAG_CoCg_Y = 256, + SOIL_FLAG_TEXTURE_RECTANGLE = 512 +}; + +/** + The types of images that may be saved. + (TGA supports uncompressed RGB / RGBA) + (BMP supports uncompressed RGB) + (DDS supports DXT1 and DXT5) +**/ +enum +{ + SOIL_SAVE_TYPE_TGA = 0, + SOIL_SAVE_TYPE_BMP = 1, + SOIL_SAVE_TYPE_DDS = 2 +}; + +/** + Defines the order of faces in a DDS cubemap. + I recommend that you use the same order in single + image cubemap files, so they will be interchangeable + with DDS cubemaps when using SOIL. +**/ +#define SOIL_DDS_CUBEMAP_FACE_ORDER "EWUDNS" + +/** + The types of internal fake HDR representations + + SOIL_HDR_RGBE: RGB * pow( 2.0, A - 128.0 ) + SOIL_HDR_RGBdivA: RGB / A + SOIL_HDR_RGBdivA2: RGB / (A*A) +**/ +enum +{ + SOIL_HDR_RGBE = 0, + SOIL_HDR_RGBdivA = 1, + SOIL_HDR_RGBdivA2 = 2 +}; + +/** + Loads an image from disk into an OpenGL texture. + \param filename the name of the file to upload as a texture + \param force_channels 0-image format, 1-luminous, 2-luminous/alpha, 3-RGB, 4-RGBA + \param reuse_texture_ID 0-generate a new texture ID, otherwise reuse the texture ID (overwriting the old texture) + \param flags can be any of SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_MIPMAPS | SOIL_FLAG_TEXTURE_REPEATS | SOIL_FLAG_MULTIPLY_ALPHA | SOIL_FLAG_INVERT_Y | SOIL_FLAG_COMPRESS_TO_DXT | SOIL_FLAG_DDS_LOAD_DIRECT + \return 0-failed, otherwise returns the OpenGL texture handle +**/ +unsigned int + SOIL_load_OGL_texture + ( + const char *filename, + int force_channels, + unsigned int reuse_texture_ID, + unsigned int flags + ); + +/** + Loads 6 images from disk into an OpenGL cubemap texture. + \param x_pos_file the name of the file to upload as the +x cube face + \param x_neg_file the name of the file to upload as the -x cube face + \param y_pos_file the name of the file to upload as the +y cube face + \param y_neg_file the name of the file to upload as the -y cube face + \param z_pos_file the name of the file to upload as the +z cube face + \param z_neg_file the name of the file to upload as the -z cube face + \param force_channels 0-image format, 1-luminous, 2-luminous/alpha, 3-RGB, 4-RGBA + \param reuse_texture_ID 0-generate a new texture ID, otherwise reuse the texture ID (overwriting the old texture) + \param flags can be any of SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_MIPMAPS | SOIL_FLAG_TEXTURE_REPEATS | SOIL_FLAG_MULTIPLY_ALPHA | SOIL_FLAG_INVERT_Y | SOIL_FLAG_COMPRESS_TO_DXT | SOIL_FLAG_DDS_LOAD_DIRECT + \return 0-failed, otherwise returns the OpenGL texture handle +**/ +unsigned int + SOIL_load_OGL_cubemap + ( + const char *x_pos_file, + const char *x_neg_file, + const char *y_pos_file, + const char *y_neg_file, + const char *z_pos_file, + const char *z_neg_file, + int force_channels, + unsigned int reuse_texture_ID, + unsigned int flags + ); + +/** + Loads 1 image from disk and splits it into an OpenGL cubemap texture. + \param filename the name of the file to upload as a texture + \param face_order the order of the faces in the file, any combination of NSWEUD, for North, South, Up, etc. + \param force_channels 0-image format, 1-luminous, 2-luminous/alpha, 3-RGB, 4-RGBA + \param reuse_texture_ID 0-generate a new texture ID, otherwise reuse the texture ID (overwriting the old texture) + \param flags can be any of SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_MIPMAPS | SOIL_FLAG_TEXTURE_REPEATS | SOIL_FLAG_MULTIPLY_ALPHA | SOIL_FLAG_INVERT_Y | SOIL_FLAG_COMPRESS_TO_DXT | SOIL_FLAG_DDS_LOAD_DIRECT + \return 0-failed, otherwise returns the OpenGL texture handle +**/ +unsigned int + SOIL_load_OGL_single_cubemap + ( + const char *filename, + const char face_order[6], + int force_channels, + unsigned int reuse_texture_ID, + unsigned int flags + ); + +/** + Loads an HDR image from disk into an OpenGL texture. + \param filename the name of the file to upload as a texture + \param fake_HDR_format SOIL_HDR_RGBE, SOIL_HDR_RGBdivA, SOIL_HDR_RGBdivA2 + \param reuse_texture_ID 0-generate a new texture ID, otherwise reuse the texture ID (overwriting the old texture) + \param flags can be any of SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_MIPMAPS | SOIL_FLAG_TEXTURE_REPEATS | SOIL_FLAG_MULTIPLY_ALPHA | SOIL_FLAG_INVERT_Y | SOIL_FLAG_COMPRESS_TO_DXT + \return 0-failed, otherwise returns the OpenGL texture handle +**/ +unsigned int + SOIL_load_OGL_HDR_texture + ( + const char *filename, + int fake_HDR_format, + int rescale_to_max, + unsigned int reuse_texture_ID, + unsigned int flags + ); + +/** + Loads an image from RAM into an OpenGL texture. + \param buffer the image data in RAM just as if it were still in a file + \param buffer_length the size of the buffer in bytes + \param force_channels 0-image format, 1-luminous, 2-luminous/alpha, 3-RGB, 4-RGBA + \param reuse_texture_ID 0-generate a new texture ID, otherwise reuse the texture ID (overwriting the old texture) + \param flags can be any of SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_MIPMAPS | SOIL_FLAG_TEXTURE_REPEATS | SOIL_FLAG_MULTIPLY_ALPHA | SOIL_FLAG_INVERT_Y | SOIL_FLAG_COMPRESS_TO_DXT | SOIL_FLAG_DDS_LOAD_DIRECT + \return 0-failed, otherwise returns the OpenGL texture handle +**/ +unsigned int + SOIL_load_OGL_texture_from_memory + ( + const unsigned char *const buffer, + int buffer_length, + int force_channels, + unsigned int reuse_texture_ID, + unsigned int flags + ); + +/** + Loads 6 images from memory into an OpenGL cubemap texture. + \param x_pos_buffer the image data in RAM to upload as the +x cube face + \param x_pos_buffer_length the size of the above buffer + \param x_neg_buffer the image data in RAM to upload as the +x cube face + \param x_neg_buffer_length the size of the above buffer + \param y_pos_buffer the image data in RAM to upload as the +x cube face + \param y_pos_buffer_length the size of the above buffer + \param y_neg_buffer the image data in RAM to upload as the +x cube face + \param y_neg_buffer_length the size of the above buffer + \param z_pos_buffer the image data in RAM to upload as the +x cube face + \param z_pos_buffer_length the size of the above buffer + \param z_neg_buffer the image data in RAM to upload as the +x cube face + \param z_neg_buffer_length the size of the above buffer + \param force_channels 0-image format, 1-luminous, 2-luminous/alpha, 3-RGB, 4-RGBA + \param reuse_texture_ID 0-generate a new texture ID, otherwise reuse the texture ID (overwriting the old texture) + \param flags can be any of SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_MIPMAPS | SOIL_FLAG_TEXTURE_REPEATS | SOIL_FLAG_MULTIPLY_ALPHA | SOIL_FLAG_INVERT_Y | SOIL_FLAG_COMPRESS_TO_DXT | SOIL_FLAG_DDS_LOAD_DIRECT + \return 0-failed, otherwise returns the OpenGL texture handle +**/ +unsigned int + SOIL_load_OGL_cubemap_from_memory + ( + const unsigned char *const x_pos_buffer, + int x_pos_buffer_length, + const unsigned char *const x_neg_buffer, + int x_neg_buffer_length, + const unsigned char *const y_pos_buffer, + int y_pos_buffer_length, + const unsigned char *const y_neg_buffer, + int y_neg_buffer_length, + const unsigned char *const z_pos_buffer, + int z_pos_buffer_length, + const unsigned char *const z_neg_buffer, + int z_neg_buffer_length, + int force_channels, + unsigned int reuse_texture_ID, + unsigned int flags + ); + +/** + Loads 1 image from RAM and splits it into an OpenGL cubemap texture. + \param buffer the image data in RAM just as if it were still in a file + \param buffer_length the size of the buffer in bytes + \param face_order the order of the faces in the file, any combination of NSWEUD, for North, South, Up, etc. + \param force_channels 0-image format, 1-luminous, 2-luminous/alpha, 3-RGB, 4-RGBA + \param reuse_texture_ID 0-generate a new texture ID, otherwise reuse the texture ID (overwriting the old texture) + \param flags can be any of SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_MIPMAPS | SOIL_FLAG_TEXTURE_REPEATS | SOIL_FLAG_MULTIPLY_ALPHA | SOIL_FLAG_INVERT_Y | SOIL_FLAG_COMPRESS_TO_DXT | SOIL_FLAG_DDS_LOAD_DIRECT + \return 0-failed, otherwise returns the OpenGL texture handle +**/ +unsigned int + SOIL_load_OGL_single_cubemap_from_memory + ( + const unsigned char *const buffer, + int buffer_length, + const char face_order[6], + int force_channels, + unsigned int reuse_texture_ID, + unsigned int flags + ); + +/** + Creates a 2D OpenGL texture from raw image data. Note that the raw data is + _NOT_ freed after the upload (so the user can load various versions). + \param data the raw data to be uploaded as an OpenGL texture + \param width the width of the image in pixels + \param height the height of the image in pixels + \param channels the number of channels: 1-luminous, 2-luminous/alpha, 3-RGB, 4-RGBA + \param reuse_texture_ID 0-generate a new texture ID, otherwise reuse the texture ID (overwriting the old texture) + \param flags can be any of SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_MIPMAPS | SOIL_FLAG_TEXTURE_REPEATS | SOIL_FLAG_MULTIPLY_ALPHA | SOIL_FLAG_INVERT_Y | SOIL_FLAG_COMPRESS_TO_DXT + \return 0-failed, otherwise returns the OpenGL texture handle +**/ +unsigned int + SOIL_create_OGL_texture + ( + const unsigned char *const data, + int width, int height, int channels, + unsigned int reuse_texture_ID, + unsigned int flags + ); + +/** + Creates an OpenGL cubemap texture by splitting up 1 image into 6 parts. + \param data the raw data to be uploaded as an OpenGL texture + \param width the width of the image in pixels + \param height the height of the image in pixels + \param channels the number of channels: 1-luminous, 2-luminous/alpha, 3-RGB, 4-RGBA + \param face_order the order of the faces in the file, and combination of NSWEUD, for North, South, Up, etc. + \param reuse_texture_ID 0-generate a new texture ID, otherwise reuse the texture ID (overwriting the old texture) + \param flags can be any of SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_MIPMAPS | SOIL_FLAG_TEXTURE_REPEATS | SOIL_FLAG_MULTIPLY_ALPHA | SOIL_FLAG_INVERT_Y | SOIL_FLAG_COMPRESS_TO_DXT | SOIL_FLAG_DDS_LOAD_DIRECT + \return 0-failed, otherwise returns the OpenGL texture handle +**/ +unsigned int + SOIL_create_OGL_single_cubemap + ( + const unsigned char *const data, + int width, int height, int channels, + const char face_order[6], + unsigned int reuse_texture_ID, + unsigned int flags + ); + +/** + Captures the OpenGL window (RGB) and saves it to disk + \return 0 if it failed, otherwise returns 1 +**/ +int + SOIL_save_screenshot + ( + const char *filename, + int image_type, + int x, int y, + int width, int height + ); + +/** + Loads an image from disk into an array of unsigned chars. + Note that *channels return the original channel count of the + image. If force_channels was other than SOIL_LOAD_AUTO, + the resulting image has force_channels, but *channels may be + different (if the original image had a different channel + count). + \return 0 if failed, otherwise returns 1 +**/ +unsigned char* + SOIL_load_image + ( + const char *filename, + int *width, int *height, int *channels, + int force_channels + ); + +/** + Loads an image from memory into an array of unsigned chars. + Note that *channels return the original channel count of the + image. If force_channels was other than SOIL_LOAD_AUTO, + the resulting image has force_channels, but *channels may be + different (if the original image had a different channel + count). + \return 0 if failed, otherwise returns 1 +**/ +unsigned char* + SOIL_load_image_from_memory + ( + const unsigned char *const buffer, + int buffer_length, + int *width, int *height, int *channels, + int force_channels + ); + +/** + Saves an image from an array of unsigned chars (RGBA) to disk + \return 0 if failed, otherwise returns 1 +**/ +int + SOIL_save_image + ( + const char *filename, + int image_type, + int width, int height, int channels, + const unsigned char *const data + ); + +/** + Frees the image data (note, this is just C's "free()"...this function is + present mostly so C++ programmers don't forget to use "free()" and call + "delete []" instead [8^) +**/ +void + SOIL_free_image_data + ( + unsigned char *img_data + ); + +/** + This function resturn a pointer to a string describing the last thing + that happened inside SOIL. It can be used to determine why an image + failed to load. +**/ +const char* + SOIL_last_result + ( + void + ); + + +#ifdef __cplusplus +} +#endif + +#endif /* HEADER_SIMPLE_OPENGL_IMAGE_LIBRARY */ diff --git a/cw_8/src/SOIL/image_DXT.c b/cw_8/src/SOIL/image_DXT.c new file mode 100644 index 0000000..4206a1b --- /dev/null +++ b/cw_8/src/SOIL/image_DXT.c @@ -0,0 +1,632 @@ +/* + Jonathan Dummer + 2007-07-31-10.32 + + simple DXT compression / decompression code + + public domain +*/ + +#include "image_DXT.h" +#include +#include +#include +#include + +/* set this =1 if you want to use the covarince matrix method... + which is better than my method of using standard deviations + overall, except on the infintesimal chance that the power + method fails for finding the largest eigenvector */ +#define USE_COV_MAT 1 + +/********* Function Prototypes *********/ +/* + Takes a 4x4 block of pixels and compresses it into 8 bytes + in DXT1 format (color only, no alpha). Speed is valued + over prettyness, at least for now. +*/ +void compress_DDS_color_block( + int channels, + const unsigned char *const uncompressed, + unsigned char compressed[8] ); +/* + Takes a 4x4 block of pixels and compresses the alpha + component it into 8 bytes for use in DXT5 DDS files. + Speed is valued over prettyness, at least for now. +*/ +void compress_DDS_alpha_block( + const unsigned char *const uncompressed, + unsigned char compressed[8] ); + +/********* Actual Exposed Functions *********/ +int + save_image_as_DDS + ( + const char *filename, + int width, int height, int channels, + const unsigned char *const data + ) +{ + /* variables */ + FILE *fout; + unsigned char *DDS_data; + DDS_header header; + int DDS_size; + /* error check */ + if( (NULL == filename) || + (width < 1) || (height < 1) || + (channels < 1) || (channels > 4) || + (data == NULL ) ) + { + return 0; + } + /* Convert the image */ + if( (channels & 1) == 1 ) + { + /* no alpha, just use DXT1 */ + DDS_data = convert_image_to_DXT1( data, width, height, channels, &DDS_size ); + } else + { + /* has alpha, so use DXT5 */ + DDS_data = convert_image_to_DXT5( data, width, height, channels, &DDS_size ); + } + /* save it */ + memset( &header, 0, sizeof( DDS_header ) ); + header.dwMagic = ('D' << 0) | ('D' << 8) | ('S' << 16) | (' ' << 24); + header.dwSize = 124; + header.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT | DDSD_LINEARSIZE; + header.dwWidth = width; + header.dwHeight = height; + header.dwPitchOrLinearSize = DDS_size; + header.sPixelFormat.dwSize = 32; + header.sPixelFormat.dwFlags = DDPF_FOURCC; + if( (channels & 1) == 1 ) + { + header.sPixelFormat.dwFourCC = ('D' << 0) | ('X' << 8) | ('T' << 16) | ('1' << 24); + } else + { + header.sPixelFormat.dwFourCC = ('D' << 0) | ('X' << 8) | ('T' << 16) | ('5' << 24); + } + header.sCaps.dwCaps1 = DDSCAPS_TEXTURE; + /* write it out */ + fout = fopen( filename, "wb"); + fwrite( &header, sizeof( DDS_header ), 1, fout ); + fwrite( DDS_data, 1, DDS_size, fout ); + fclose( fout ); + /* done */ + free( DDS_data ); + return 1; +} + +unsigned char* convert_image_to_DXT1( + const unsigned char *const uncompressed, + int width, int height, int channels, + int *out_size ) +{ + unsigned char *compressed; + int i, j, x, y; + unsigned char ublock[16*3]; + unsigned char cblock[8]; + int index = 0, chan_step = 1; + int block_count = 0; + /* error check */ + *out_size = 0; + if( (width < 1) || (height < 1) || + (NULL == uncompressed) || + (channels < 1) || (channels > 4) ) + { + return NULL; + } + /* for channels == 1 or 2, I do not step forward for R,G,B values */ + if( channels < 3 ) + { + chan_step = 0; + } + /* get the RAM for the compressed image + (8 bytes per 4x4 pixel block) */ + *out_size = ((width+3) >> 2) * ((height+3) >> 2) * 8; + compressed = (unsigned char*)malloc( *out_size ); + /* go through each block */ + for( j = 0; j < height; j += 4 ) + { + for( i = 0; i < width; i += 4 ) + { + /* copy this block into a new one */ + int idx = 0; + int mx = 4, my = 4; + if( j+4 >= height ) + { + my = height - j; + } + if( i+4 >= width ) + { + mx = width - i; + } + for( y = 0; y < my; ++y ) + { + for( x = 0; x < mx; ++x ) + { + ublock[idx++] = uncompressed[(j+y)*width*channels+(i+x)*channels]; + ublock[idx++] = uncompressed[(j+y)*width*channels+(i+x)*channels+chan_step]; + ublock[idx++] = uncompressed[(j+y)*width*channels+(i+x)*channels+chan_step+chan_step]; + } + for( x = mx; x < 4; ++x ) + { + ublock[idx++] = ublock[0]; + ublock[idx++] = ublock[1]; + ublock[idx++] = ublock[2]; + } + } + for( y = my; y < 4; ++y ) + { + for( x = 0; x < 4; ++x ) + { + ublock[idx++] = ublock[0]; + ublock[idx++] = ublock[1]; + ublock[idx++] = ublock[2]; + } + } + /* compress the block */ + ++block_count; + compress_DDS_color_block( 3, ublock, cblock ); + /* copy the data from the block into the main block */ + for( x = 0; x < 8; ++x ) + { + compressed[index++] = cblock[x]; + } + } + } + return compressed; +} + +unsigned char* convert_image_to_DXT5( + const unsigned char *const uncompressed, + int width, int height, int channels, + int *out_size ) +{ + unsigned char *compressed; + int i, j, x, y; + unsigned char ublock[16*4]; + unsigned char cblock[8]; + int index = 0, chan_step = 1; + int block_count = 0, has_alpha; + /* error check */ + *out_size = 0; + if( (width < 1) || (height < 1) || + (NULL == uncompressed) || + (channels < 1) || ( channels > 4) ) + { + return NULL; + } + /* for channels == 1 or 2, I do not step forward for R,G,B vales */ + if( channels < 3 ) + { + chan_step = 0; + } + /* # channels = 1 or 3 have no alpha, 2 & 4 do have alpha */ + has_alpha = 1 - (channels & 1); + /* get the RAM for the compressed image + (16 bytes per 4x4 pixel block) */ + *out_size = ((width+3) >> 2) * ((height+3) >> 2) * 16; + compressed = (unsigned char*)malloc( *out_size ); + /* go through each block */ + for( j = 0; j < height; j += 4 ) + { + for( i = 0; i < width; i += 4 ) + { + /* local variables, and my block counter */ + int idx = 0; + int mx = 4, my = 4; + if( j+4 >= height ) + { + my = height - j; + } + if( i+4 >= width ) + { + mx = width - i; + } + for( y = 0; y < my; ++y ) + { + for( x = 0; x < mx; ++x ) + { + ublock[idx++] = uncompressed[(j+y)*width*channels+(i+x)*channels]; + ublock[idx++] = uncompressed[(j+y)*width*channels+(i+x)*channels+chan_step]; + ublock[idx++] = uncompressed[(j+y)*width*channels+(i+x)*channels+chan_step+chan_step]; + ublock[idx++] = + has_alpha * uncompressed[(j+y)*width*channels+(i+x)*channels+channels-1] + + (1-has_alpha)*255; + } + for( x = mx; x < 4; ++x ) + { + ublock[idx++] = ublock[0]; + ublock[idx++] = ublock[1]; + ublock[idx++] = ublock[2]; + ublock[idx++] = ublock[3]; + } + } + for( y = my; y < 4; ++y ) + { + for( x = 0; x < 4; ++x ) + { + ublock[idx++] = ublock[0]; + ublock[idx++] = ublock[1]; + ublock[idx++] = ublock[2]; + ublock[idx++] = ublock[3]; + } + } + /* now compress the alpha block */ + compress_DDS_alpha_block( ublock, cblock ); + /* copy the data from the compressed alpha block into the main buffer */ + for( x = 0; x < 8; ++x ) + { + compressed[index++] = cblock[x]; + } + /* then compress the color block */ + ++block_count; + compress_DDS_color_block( 4, ublock, cblock ); + /* copy the data from the compressed color block into the main buffer */ + for( x = 0; x < 8; ++x ) + { + compressed[index++] = cblock[x]; + } + } + } + return compressed; +} + +/********* Helper Functions *********/ +int convert_bit_range( int c, int from_bits, int to_bits ) +{ + int b = (1 << (from_bits - 1)) + c * ((1 << to_bits) - 1); + return (b + (b >> from_bits)) >> from_bits; +} + +int rgb_to_565( int r, int g, int b ) +{ + return + (convert_bit_range( r, 8, 5 ) << 11) | + (convert_bit_range( g, 8, 6 ) << 05) | + (convert_bit_range( b, 8, 5 ) << 00); +} + +void rgb_888_from_565( unsigned int c, int *r, int *g, int *b ) +{ + *r = convert_bit_range( (c >> 11) & 31, 5, 8 ); + *g = convert_bit_range( (c >> 05) & 63, 6, 8 ); + *b = convert_bit_range( (c >> 00) & 31, 5, 8 ); +} + +void compute_color_line_STDEV( + const unsigned char *const uncompressed, + int channels, + float point[3], float direction[3] ) +{ + const float inv_16 = 1.0f / 16.0f; + int i; + float sum_r = 0.0f, sum_g = 0.0f, sum_b = 0.0f; + float sum_rr = 0.0f, sum_gg = 0.0f, sum_bb = 0.0f; + float sum_rg = 0.0f, sum_rb = 0.0f, sum_gb = 0.0f; + /* calculate all data needed for the covariance matrix + ( to compare with _rygdxt code) */ + for( i = 0; i < 16*channels; i += channels ) + { + sum_r += uncompressed[i+0]; + sum_rr += uncompressed[i+0] * uncompressed[i+0]; + sum_g += uncompressed[i+1]; + sum_gg += uncompressed[i+1] * uncompressed[i+1]; + sum_b += uncompressed[i+2]; + sum_bb += uncompressed[i+2] * uncompressed[i+2]; + sum_rg += uncompressed[i+0] * uncompressed[i+1]; + sum_rb += uncompressed[i+0] * uncompressed[i+2]; + sum_gb += uncompressed[i+1] * uncompressed[i+2]; + } + /* convert the sums to averages */ + sum_r *= inv_16; + sum_g *= inv_16; + sum_b *= inv_16; + /* and convert the squares to the squares of the value - avg_value */ + sum_rr -= 16.0f * sum_r * sum_r; + sum_gg -= 16.0f * sum_g * sum_g; + sum_bb -= 16.0f * sum_b * sum_b; + sum_rg -= 16.0f * sum_r * sum_g; + sum_rb -= 16.0f * sum_r * sum_b; + sum_gb -= 16.0f * sum_g * sum_b; + /* the point on the color line is the average */ + point[0] = sum_r; + point[1] = sum_g; + point[2] = sum_b; + #if USE_COV_MAT + /* + The following idea was from ryg. + (https://mollyrocket.com/forums/viewtopic.php?t=392) + The method worked great (less RMSE than mine) most of + the time, but had some issues handling some simple + boundary cases, like full green next to full red, + which would generate a covariance matrix like this: + + | 1 -1 0 | + | -1 1 0 | + | 0 0 0 | + + For a given starting vector, the power method can + generate all zeros! So no starting with {1,1,1} + as I was doing! This kind of error is still a + slight posibillity, but will be very rare. + */ + /* use the covariance matrix directly + (1st iteration, don't use all 1.0 values!) */ + sum_r = 1.0f; + sum_g = 2.718281828f; + sum_b = 3.141592654f; + direction[0] = sum_r*sum_rr + sum_g*sum_rg + sum_b*sum_rb; + direction[1] = sum_r*sum_rg + sum_g*sum_gg + sum_b*sum_gb; + direction[2] = sum_r*sum_rb + sum_g*sum_gb + sum_b*sum_bb; + /* 2nd iteration, use results from the 1st guy */ + sum_r = direction[0]; + sum_g = direction[1]; + sum_b = direction[2]; + direction[0] = sum_r*sum_rr + sum_g*sum_rg + sum_b*sum_rb; + direction[1] = sum_r*sum_rg + sum_g*sum_gg + sum_b*sum_gb; + direction[2] = sum_r*sum_rb + sum_g*sum_gb + sum_b*sum_bb; + /* 3rd iteration, use results from the 2nd guy */ + sum_r = direction[0]; + sum_g = direction[1]; + sum_b = direction[2]; + direction[0] = sum_r*sum_rr + sum_g*sum_rg + sum_b*sum_rb; + direction[1] = sum_r*sum_rg + sum_g*sum_gg + sum_b*sum_gb; + direction[2] = sum_r*sum_rb + sum_g*sum_gb + sum_b*sum_bb; + #else + /* use my standard deviation method + (very robust, a tiny bit slower and less accurate) */ + direction[0] = sqrt( sum_rr ); + direction[1] = sqrt( sum_gg ); + direction[2] = sqrt( sum_bb ); + /* which has a greater component */ + if( sum_gg > sum_rr ) + { + /* green has greater component, so base the other signs off of green */ + if( sum_rg < 0.0f ) + { + direction[0] = -direction[0]; + } + if( sum_gb < 0.0f ) + { + direction[2] = -direction[2]; + } + } else + { + /* red has a greater component */ + if( sum_rg < 0.0f ) + { + direction[1] = -direction[1]; + } + if( sum_rb < 0.0f ) + { + direction[2] = -direction[2]; + } + } + #endif +} + +void LSE_master_colors_max_min( + int *cmax, int *cmin, + int channels, + const unsigned char *const uncompressed ) +{ + int i, j; + /* the master colors */ + int c0[3], c1[3]; + /* used for fitting the line */ + float sum_x[] = { 0.0f, 0.0f, 0.0f }; + float sum_x2[] = { 0.0f, 0.0f, 0.0f }; + float dot_max = 1.0f, dot_min = -1.0f; + float vec_len2 = 0.0f; + float dot; + /* error check */ + if( (channels < 3) || (channels > 4) ) + { + return; + } + compute_color_line_STDEV( uncompressed, channels, sum_x, sum_x2 ); + vec_len2 = 1.0f / ( 0.00001f + + sum_x2[0]*sum_x2[0] + sum_x2[1]*sum_x2[1] + sum_x2[2]*sum_x2[2] ); + /* finding the max and min vector values */ + dot_max = + ( + sum_x2[0] * uncompressed[0] + + sum_x2[1] * uncompressed[1] + + sum_x2[2] * uncompressed[2] + ); + dot_min = dot_max; + for( i = 1; i < 16; ++i ) + { + dot = + ( + sum_x2[0] * uncompressed[i*channels+0] + + sum_x2[1] * uncompressed[i*channels+1] + + sum_x2[2] * uncompressed[i*channels+2] + ); + if( dot < dot_min ) + { + dot_min = dot; + } else if( dot > dot_max ) + { + dot_max = dot; + } + } + /* and the offset (from the average location) */ + dot = sum_x2[0]*sum_x[0] + sum_x2[1]*sum_x[1] + sum_x2[2]*sum_x[2]; + dot_min -= dot; + dot_max -= dot; + /* post multiply by the scaling factor */ + dot_min *= vec_len2; + dot_max *= vec_len2; + /* OK, build the master colors */ + for( i = 0; i < 3; ++i ) + { + /* color 0 */ + c0[i] = (int)(0.5f + sum_x[i] + dot_max * sum_x2[i]); + if( c0[i] < 0 ) + { + c0[i] = 0; + } else if( c0[i] > 255 ) + { + c0[i] = 255; + } + /* color 1 */ + c1[i] = (int)(0.5f + sum_x[i] + dot_min * sum_x2[i]); + if( c1[i] < 0 ) + { + c1[i] = 0; + } else if( c1[i] > 255 ) + { + c1[i] = 255; + } + } + /* down_sample (with rounding?) */ + i = rgb_to_565( c0[0], c0[1], c0[2] ); + j = rgb_to_565( c1[0], c1[1], c1[2] ); + if( i > j ) + { + *cmax = i; + *cmin = j; + } else + { + *cmax = j; + *cmin = i; + } +} + +void + compress_DDS_color_block + ( + int channels, + const unsigned char *const uncompressed, + unsigned char compressed[8] + ) +{ + /* variables */ + int i; + int next_bit; + int enc_c0, enc_c1; + int c0[4], c1[4]; + float color_line[] = { 0.0f, 0.0f, 0.0f, 0.0f }; + float vec_len2 = 0.0f, dot_offset = 0.0f; + /* stupid order */ + int swizzle4[] = { 0, 2, 3, 1 }; + /* get the master colors */ + LSE_master_colors_max_min( &enc_c0, &enc_c1, channels, uncompressed ); + /* store the 565 color 0 and color 1 */ + compressed[0] = (enc_c0 >> 0) & 255; + compressed[1] = (enc_c0 >> 8) & 255; + compressed[2] = (enc_c1 >> 0) & 255; + compressed[3] = (enc_c1 >> 8) & 255; + /* zero out the compressed data */ + compressed[4] = 0; + compressed[5] = 0; + compressed[6] = 0; + compressed[7] = 0; + /* reconstitute the master color vectors */ + rgb_888_from_565( enc_c0, &c0[0], &c0[1], &c0[2] ); + rgb_888_from_565( enc_c1, &c1[0], &c1[1], &c1[2] ); + /* the new vector */ + vec_len2 = 0.0f; + for( i = 0; i < 3; ++i ) + { + color_line[i] = (float)(c1[i] - c0[i]); + vec_len2 += color_line[i] * color_line[i]; + } + if( vec_len2 > 0.0f ) + { + vec_len2 = 1.0f / vec_len2; + } + /* pre-proform the scaling */ + color_line[0] *= vec_len2; + color_line[1] *= vec_len2; + color_line[2] *= vec_len2; + /* compute the offset (constant) portion of the dot product */ + dot_offset = color_line[0]*c0[0] + color_line[1]*c0[1] + color_line[2]*c0[2]; + /* store the rest of the bits */ + next_bit = 8*4; + for( i = 0; i < 16; ++i ) + { + /* find the dot product of this color, to place it on the line + (should be [-1,1]) */ + int next_value = 0; + float dot_product = + color_line[0] * uncompressed[i*channels+0] + + color_line[1] * uncompressed[i*channels+1] + + color_line[2] * uncompressed[i*channels+2] - + dot_offset; + /* map to [0,3] */ + next_value = (int)( dot_product * 3.0f + 0.5f ); + if( next_value > 3 ) + { + next_value = 3; + } else if( next_value < 0 ) + { + next_value = 0; + } + /* OK, store this value */ + compressed[next_bit >> 3] |= swizzle4[ next_value ] << (next_bit & 7); + next_bit += 2; + } + /* done compressing to DXT1 */ +} + +void + compress_DDS_alpha_block + ( + const unsigned char *const uncompressed, + unsigned char compressed[8] + ) +{ + /* variables */ + int i; + int next_bit; + int a0, a1; + float scale_me; + /* stupid order */ + int swizzle8[] = { 1, 7, 6, 5, 4, 3, 2, 0 }; + /* get the alpha limits (a0 > a1) */ + a0 = a1 = uncompressed[3]; + for( i = 4+3; i < 16*4; i += 4 ) + { + if( uncompressed[i] > a0 ) + { + a0 = uncompressed[i]; + } else if( uncompressed[i] < a1 ) + { + a1 = uncompressed[i]; + } + } + /* store those limits, and zero the rest of the compressed dataset */ + compressed[0] = a0; + compressed[1] = a1; + /* zero out the compressed data */ + compressed[2] = 0; + compressed[3] = 0; + compressed[4] = 0; + compressed[5] = 0; + compressed[6] = 0; + compressed[7] = 0; + /* store the all of the alpha values */ + next_bit = 8*2; + scale_me = 7.9999f / (a0 - a1); + for( i = 3; i < 16*4; i += 4 ) + { + /* convert this alpha value to a 3 bit number */ + int svalue; + int value = (int)((uncompressed[i] - a1) * scale_me); + svalue = swizzle8[ value&7 ]; + /* OK, store this value, start with the 1st byte */ + compressed[next_bit >> 3] |= svalue << (next_bit & 7); + if( (next_bit & 7) > 5 ) + { + /* spans 2 bytes, fill in the start of the 2nd byte */ + compressed[1 + (next_bit >> 3)] |= svalue >> (8 - (next_bit & 7) ); + } + next_bit += 3; + } + /* done compressing to DXT1 */ +} diff --git a/cw_8/src/SOIL/image_DXT.h b/cw_8/src/SOIL/image_DXT.h new file mode 100644 index 0000000..75f604f --- /dev/null +++ b/cw_8/src/SOIL/image_DXT.h @@ -0,0 +1,123 @@ +/* + Jonathan Dummer + 2007-07-31-10.32 + + simple DXT compression / decompression code + + public domain +*/ + +#ifndef HEADER_IMAGE_DXT +#define HEADER_IMAGE_DXT + +/** + Converts an image from an array of unsigned chars (RGB or RGBA) to + DXT1 or DXT5, then saves the converted image to disk. + \return 0 if failed, otherwise returns 1 +**/ +int +save_image_as_DDS +( + const char *filename, + int width, int height, int channels, + const unsigned char *const data +); + +/** + take an image and convert it to DXT1 (no alpha) +**/ +unsigned char* +convert_image_to_DXT1 +( + const unsigned char *const uncompressed, + int width, int height, int channels, + int *out_size +); + +/** + take an image and convert it to DXT5 (with alpha) +**/ +unsigned char* +convert_image_to_DXT5 +( + const unsigned char *const uncompressed, + int width, int height, int channels, + int *out_size +); + +/** A bunch of DirectDraw Surface structures and flags **/ +typedef struct +{ + unsigned int dwMagic; + unsigned int dwSize; + unsigned int dwFlags; + unsigned int dwHeight; + unsigned int dwWidth; + unsigned int dwPitchOrLinearSize; + unsigned int dwDepth; + unsigned int dwMipMapCount; + unsigned int dwReserved1[ 11 ]; + + /* DDPIXELFORMAT */ + struct + { + unsigned int dwSize; + unsigned int dwFlags; + unsigned int dwFourCC; + unsigned int dwRGBBitCount; + unsigned int dwRBitMask; + unsigned int dwGBitMask; + unsigned int dwBBitMask; + unsigned int dwAlphaBitMask; + } + sPixelFormat; + + /* DDCAPS2 */ + struct + { + unsigned int dwCaps1; + unsigned int dwCaps2; + unsigned int dwDDSX; + unsigned int dwReserved; + } + sCaps; + unsigned int dwReserved2; +} +DDS_header ; + +/* the following constants were copied directly off the MSDN website */ + +/* The dwFlags member of the original DDSURFACEDESC2 structure + can be set to one or more of the following values. */ +#define DDSD_CAPS 0x00000001 +#define DDSD_HEIGHT 0x00000002 +#define DDSD_WIDTH 0x00000004 +#define DDSD_PITCH 0x00000008 +#define DDSD_PIXELFORMAT 0x00001000 +#define DDSD_MIPMAPCOUNT 0x00020000 +#define DDSD_LINEARSIZE 0x00080000 +#define DDSD_DEPTH 0x00800000 + +/* DirectDraw Pixel Format */ +#define DDPF_ALPHAPIXELS 0x00000001 +#define DDPF_FOURCC 0x00000004 +#define DDPF_RGB 0x00000040 + +/* The dwCaps1 member of the DDSCAPS2 structure can be + set to one or more of the following values. */ +#define DDSCAPS_COMPLEX 0x00000008 +#define DDSCAPS_TEXTURE 0x00001000 +#define DDSCAPS_MIPMAP 0x00400000 + +/* The dwCaps2 member of the DDSCAPS2 structure can be + set to one or more of the following values. */ +#define DDSCAPS2_CUBEMAP 0x00000200 +#define DDSCAPS2_CUBEMAP_POSITIVEX 0x00000400 +#define DDSCAPS2_CUBEMAP_NEGATIVEX 0x00000800 +#define DDSCAPS2_CUBEMAP_POSITIVEY 0x00001000 +#define DDSCAPS2_CUBEMAP_NEGATIVEY 0x00002000 +#define DDSCAPS2_CUBEMAP_POSITIVEZ 0x00004000 +#define DDSCAPS2_CUBEMAP_NEGATIVEZ 0x00008000 +#define DDSCAPS2_VOLUME 0x00200000 + +#endif /* HEADER_IMAGE_DXT */ diff --git a/cw_8/src/SOIL/image_helper.c b/cw_8/src/SOIL/image_helper.c new file mode 100644 index 0000000..d22340f --- /dev/null +++ b/cw_8/src/SOIL/image_helper.c @@ -0,0 +1,435 @@ +/* + Jonathan Dummer + + image helper functions + + MIT license +*/ + +#include "image_helper.h" +#include +#include + +/* Upscaling the image uses simple bilinear interpolation */ +int + up_scale_image + ( + const unsigned char* const orig, + int width, int height, int channels, + unsigned char* resampled, + int resampled_width, int resampled_height + ) +{ + float dx, dy; + int x, y, c; + + /* error(s) check */ + if ( (width < 1) || (height < 1) || + (resampled_width < 2) || (resampled_height < 2) || + (channels < 1) || + (NULL == orig) || (NULL == resampled) ) + { + /* signify badness */ + return 0; + } + /* + for each given pixel in the new map, find the exact location + from the original map which would contribute to this guy + */ + dx = (width - 1.0f) / (resampled_width - 1.0f); + dy = (height - 1.0f) / (resampled_height - 1.0f); + for ( y = 0; y < resampled_height; ++y ) + { + /* find the base y index and fractional offset from that */ + float sampley = y * dy; + int inty = (int)sampley; + /* if( inty < 0 ) { inty = 0; } else */ + if( inty > height - 2 ) { inty = height - 2; } + sampley -= inty; + for ( x = 0; x < resampled_width; ++x ) + { + float samplex = x * dx; + int intx = (int)samplex; + int base_index; + /* find the base x index and fractional offset from that */ + /* if( intx < 0 ) { intx = 0; } else */ + if( intx > width - 2 ) { intx = width - 2; } + samplex -= intx; + /* base index into the original image */ + base_index = (inty * width + intx) * channels; + for ( c = 0; c < channels; ++c ) + { + /* do the sampling */ + float value = 0.5f; + value += orig[base_index] + *(1.0f-samplex)*(1.0f-sampley); + value += orig[base_index+channels] + *(samplex)*(1.0f-sampley); + value += orig[base_index+width*channels] + *(1.0f-samplex)*(sampley); + value += orig[base_index+width*channels+channels] + *(samplex)*(sampley); + /* move to the next channel */ + ++base_index; + /* save the new value */ + resampled[y*resampled_width*channels+x*channels+c] = + (unsigned char)(value); + } + } + } + /* done */ + return 1; +} + +int + mipmap_image + ( + const unsigned char* const orig, + int width, int height, int channels, + unsigned char* resampled, + int block_size_x, int block_size_y + ) +{ + int mip_width, mip_height; + int i, j, c; + + /* error check */ + if( (width < 1) || (height < 1) || + (channels < 1) || (orig == NULL) || + (resampled == NULL) || + (block_size_x < 1) || (block_size_y < 1) ) + { + /* nothing to do */ + return 0; + } + mip_width = width / block_size_x; + mip_height = height / block_size_y; + if( mip_width < 1 ) + { + mip_width = 1; + } + if( mip_height < 1 ) + { + mip_height = 1; + } + for( j = 0; j < mip_height; ++j ) + { + for( i = 0; i < mip_width; ++i ) + { + for( c = 0; c < channels; ++c ) + { + const int index = (j*block_size_y)*width*channels + (i*block_size_x)*channels + c; + int sum_value; + int u,v; + int u_block = block_size_x; + int v_block = block_size_y; + int block_area; + /* do a bit of checking so we don't over-run the boundaries + (necessary for non-square textures!) */ + if( block_size_x * (i+1) > width ) + { + u_block = width - i*block_size_y; + } + if( block_size_y * (j+1) > height ) + { + v_block = height - j*block_size_y; + } + block_area = u_block*v_block; + /* for this pixel, see what the average + of all the values in the block are. + note: start the sum at the rounding value, not at 0 */ + sum_value = block_area >> 1; + for( v = 0; v < v_block; ++v ) + for( u = 0; u < u_block; ++u ) + { + sum_value += orig[index + v*width*channels + u*channels]; + } + resampled[j*mip_width*channels + i*channels + c] = sum_value / block_area; + } + } + } + return 1; +} + +int + scale_image_RGB_to_NTSC_safe + ( + unsigned char* orig, + int width, int height, int channels + ) +{ + const float scale_lo = 16.0f - 0.499f; + const float scale_hi = 235.0f + 0.499f; + int i, j; + int nc = channels; + unsigned char scale_LUT[256]; + /* error check */ + if( (width < 1) || (height < 1) || + (channels < 1) || (orig == NULL) ) + { + /* nothing to do */ + return 0; + } + /* set up the scaling Look Up Table */ + for( i = 0; i < 256; ++i ) + { + scale_LUT[i] = (unsigned char)((scale_hi - scale_lo) * i / 255.0f + scale_lo); + } + /* for channels = 2 or 4, ignore the alpha component */ + nc -= 1 - (channels & 1); + /* OK, go through the image and scale any non-alpha components */ + for( i = 0; i < width*height*channels; i += channels ) + { + for( j = 0; j < nc; ++j ) + { + orig[i+j] = scale_LUT[orig[i+j]]; + } + } + return 1; +} + +unsigned char clamp_byte( int x ) { return ( (x) < 0 ? (0) : ( (x) > 255 ? 255 : (x) ) ); } + +/* + This function takes the RGB components of the image + and converts them into YCoCg. 3 components will be + re-ordered to CoYCg (for optimum DXT1 compression), + while 4 components will be ordered CoCgAY (for DXT5 + compression). +*/ +int + convert_RGB_to_YCoCg + ( + unsigned char* orig, + int width, int height, int channels + ) +{ + int i; + /* error check */ + if( (width < 1) || (height < 1) || + (channels < 3) || (channels > 4) || + (orig == NULL) ) + { + /* nothing to do */ + return -1; + } + /* do the conversion */ + if( channels == 3 ) + { + for( i = 0; i < width*height*3; i += 3 ) + { + int r = orig[i+0]; + int g = (orig[i+1] + 1) >> 1; + int b = orig[i+2]; + int tmp = (2 + r + b) >> 2; + /* Co */ + orig[i+0] = clamp_byte( 128 + ((r - b + 1) >> 1) ); + /* Y */ + orig[i+1] = clamp_byte( g + tmp ); + /* Cg */ + orig[i+2] = clamp_byte( 128 + g - tmp ); + } + } else + { + for( i = 0; i < width*height*4; i += 4 ) + { + int r = orig[i+0]; + int g = (orig[i+1] + 1) >> 1; + int b = orig[i+2]; + unsigned char a = orig[i+3]; + int tmp = (2 + r + b) >> 2; + /* Co */ + orig[i+0] = clamp_byte( 128 + ((r - b + 1) >> 1) ); + /* Cg */ + orig[i+1] = clamp_byte( 128 + g - tmp ); + /* Alpha */ + orig[i+2] = a; + /* Y */ + orig[i+3] = clamp_byte( g + tmp ); + } + } + /* done */ + return 0; +} + +/* + This function takes the YCoCg components of the image + and converts them into RGB. See above. +*/ +int + convert_YCoCg_to_RGB + ( + unsigned char* orig, + int width, int height, int channels + ) +{ + int i; + /* error check */ + if( (width < 1) || (height < 1) || + (channels < 3) || (channels > 4) || + (orig == NULL) ) + { + /* nothing to do */ + return -1; + } + /* do the conversion */ + if( channels == 3 ) + { + for( i = 0; i < width*height*3; i += 3 ) + { + int co = orig[i+0] - 128; + int y = orig[i+1]; + int cg = orig[i+2] - 128; + /* R */ + orig[i+0] = clamp_byte( y + co - cg ); + /* G */ + orig[i+1] = clamp_byte( y + cg ); + /* B */ + orig[i+2] = clamp_byte( y - co - cg ); + } + } else + { + for( i = 0; i < width*height*4; i += 4 ) + { + int co = orig[i+0] - 128; + int cg = orig[i+1] - 128; + unsigned char a = orig[i+2]; + int y = orig[i+3]; + /* R */ + orig[i+0] = clamp_byte( y + co - cg ); + /* G */ + orig[i+1] = clamp_byte( y + cg ); + /* B */ + orig[i+2] = clamp_byte( y - co - cg ); + /* A */ + orig[i+3] = a; + } + } + /* done */ + return 0; +} + +float +find_max_RGBE +( + unsigned char *image, + int width, int height +) +{ + float max_val = 0.0f; + unsigned char *img = image; + int i, j; + for( i = width * height; i > 0; --i ) + { + /* float scale = powf( 2.0f, img[3] - 128.0f ) / 255.0f; */ + float scale = ldexp( 1.0f / 255.0f, (int)(img[3]) - 128 ); + for( j = 0; j < 3; ++j ) + { + if( img[j] * scale > max_val ) + { + max_val = img[j] * scale; + } + } + /* next pixel */ + img += 4; + } + return max_val; +} + +int +RGBE_to_RGBdivA +( + unsigned char *image, + int width, int height, + int rescale_to_max +) +{ + /* local variables */ + int i, iv; + unsigned char *img = image; + float scale = 1.0f; + /* error check */ + if( (!image) || (width < 1) || (height < 1) ) + { + return 0; + } + /* convert (note: no negative numbers, but 0.0 is possible) */ + if( rescale_to_max ) + { + scale = 255.0f / find_max_RGBE( image, width, height ); + } + for( i = width * height; i > 0; --i ) + { + /* decode this pixel, and find the max */ + float r,g,b,e, m; + /* e = scale * powf( 2.0f, img[3] - 128.0f ) / 255.0f; */ + e = scale * ldexp( 1.0f / 255.0f, (int)(img[3]) - 128 ); + r = e * img[0]; + g = e * img[1]; + b = e * img[2]; + m = (r > g) ? r : g; + m = (b > m) ? b : m; + /* and encode it into RGBdivA */ + iv = (m != 0.0f) ? (int)(255.0f / m) : 1.0f; + iv = (iv < 1) ? 1 : iv; + img[3] = (iv > 255) ? 255 : iv; + iv = (int)(img[3] * r + 0.5f); + img[0] = (iv > 255) ? 255 : iv; + iv = (int)(img[3] * g + 0.5f); + img[1] = (iv > 255) ? 255 : iv; + iv = (int)(img[3] * b + 0.5f); + img[2] = (iv > 255) ? 255 : iv; + /* and on to the next pixel */ + img += 4; + } + return 1; +} + +int +RGBE_to_RGBdivA2 +( + unsigned char *image, + int width, int height, + int rescale_to_max +) +{ + /* local variables */ + int i, iv; + unsigned char *img = image; + float scale = 1.0f; + /* error check */ + if( (!image) || (width < 1) || (height < 1) ) + { + return 0; + } + /* convert (note: no negative numbers, but 0.0 is possible) */ + if( rescale_to_max ) + { + scale = 255.0f * 255.0f / find_max_RGBE( image, width, height ); + } + for( i = width * height; i > 0; --i ) + { + /* decode this pixel, and find the max */ + float r,g,b,e, m; + /* e = scale * powf( 2.0f, img[3] - 128.0f ) / 255.0f; */ + e = scale * ldexp( 1.0f / 255.0f, (int)(img[3]) - 128 ); + r = e * img[0]; + g = e * img[1]; + b = e * img[2]; + m = (r > g) ? r : g; + m = (b > m) ? b : m; + /* and encode it into RGBdivA */ + iv = (m != 0.0f) ? (int)sqrtf( 255.0f * 255.0f / m ) : 1.0f; + iv = (iv < 1) ? 1 : iv; + img[3] = (iv > 255) ? 255 : iv; + iv = (int)(img[3] * img[3] * r / 255.0f + 0.5f); + img[0] = (iv > 255) ? 255 : iv; + iv = (int)(img[3] * img[3] * g / 255.0f + 0.5f); + img[1] = (iv > 255) ? 255 : iv; + iv = (int)(img[3] * img[3] * b / 255.0f + 0.5f); + img[2] = (iv > 255) ? 255 : iv; + /* and on to the next pixel */ + img += 4; + } + return 1; +} diff --git a/cw_8/src/SOIL/image_helper.h b/cw_8/src/SOIL/image_helper.h new file mode 100644 index 0000000..3fa2662 --- /dev/null +++ b/cw_8/src/SOIL/image_helper.h @@ -0,0 +1,115 @@ +/* + Jonathan Dummer + + Image helper functions + + MIT license +*/ + +#ifndef HEADER_IMAGE_HELPER +#define HEADER_IMAGE_HELPER + +#ifdef __cplusplus +extern "C" { +#endif + +/** + This function upscales an image. + Not to be used to create MIPmaps, + but to make it square, + or to make it a power-of-two sized. +**/ +int + up_scale_image + ( + const unsigned char* const orig, + int width, int height, int channels, + unsigned char* resampled, + int resampled_width, int resampled_height + ); + +/** + This function downscales an image. + Used for creating MIPmaps, + the incoming image should be a + power-of-two sized. +**/ +int + mipmap_image + ( + const unsigned char* const orig, + int width, int height, int channels, + unsigned char* resampled, + int block_size_x, int block_size_y + ); + +/** + This function takes the RGB components of the image + and scales each channel from [0,255] to [16,235]. + This makes the colors "Safe" for display on NTSC + displays. Note that this is _NOT_ a good idea for + loading images like normal- or height-maps! +**/ +int + scale_image_RGB_to_NTSC_safe + ( + unsigned char* orig, + int width, int height, int channels + ); + +/** + This function takes the RGB components of the image + and converts them into YCoCg. 3 components will be + re-ordered to CoYCg (for optimum DXT1 compression), + while 4 components will be ordered CoCgAY (for DXT5 + compression). +**/ +int + convert_RGB_to_YCoCg + ( + unsigned char* orig, + int width, int height, int channels + ); + +/** + This function takes the YCoCg components of the image + and converts them into RGB. See above. +**/ +int + convert_YCoCg_to_RGB + ( + unsigned char* orig, + int width, int height, int channels + ); + +/** + Converts an HDR image from an array + of unsigned chars (RGBE) to RGBdivA + \return 0 if failed, otherwise returns 1 +**/ +int + RGBE_to_RGBdivA + ( + unsigned char *image, + int width, int height, + int rescale_to_max + ); + +/** + Converts an HDR image from an array + of unsigned chars (RGBE) to RGBdivA2 + \return 0 if failed, otherwise returns 1 +**/ +int + RGBE_to_RGBdivA2 + ( + unsigned char *image, + int width, int height, + int rescale_to_max + ); + +#ifdef __cplusplus +} +#endif + +#endif /* HEADER_IMAGE_HELPER */ diff --git a/cw_8/src/SOIL/stb_image_aug.c b/cw_8/src/SOIL/stb_image_aug.c new file mode 100644 index 0000000..bb088fc --- /dev/null +++ b/cw_8/src/SOIL/stb_image_aug.c @@ -0,0 +1,3682 @@ +/* stbi-1.16 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c + when you control the images you're loading + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline (no JPEG progressive, no oddball channel decimations) + PNG non-interlaced + BMP non-1bpp, non-RLE + TGA (not sure what subset, if a subset) + PSD (composited view only, no extra channels) + HDR (radiance rgbE format) + writes BMP,TGA (define STBI_NO_WRITE to remove code) + decoded from memory or through stdio FILE (define STBI_NO_STDIO to remove code) + supports installable dequantizing-IDCT, YCbCr-to-RGB conversion (define STBI_SIMD) + + TODO: + stbi_info_* + + history: + 1.16 major bugfix - convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug; header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi_bmp_load() and stbi_tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less + than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant +*/ + +#include "stb_image_aug.h" + +#ifndef STBI_NO_HDR +#include // ldexp +#include // strcmp +#endif + +#ifndef STBI_NO_STDIO +#include +#endif +#include +#include +#include +#include + +#ifndef _MSC_VER + #ifdef __cplusplus + #define __forceinline inline + #else + #define __forceinline + #endif +#endif + + +// implementation: +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef signed short int16; +typedef unsigned int uint32; +typedef signed int int32; +typedef unsigned int uint; + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(uint32)==4]; + +#if defined(STBI_NO_STDIO) && !defined(STBI_NO_WRITE) +#define STBI_NO_WRITE +#endif + +#ifndef STBI_NO_DDS +#include "stbi_DDS_aug.h" +#endif + +// I (JLD) want full messages for SOIL +#define STBI_FAILURE_USERMSG 1 + +////////////////////////////////////////////////////////////////////////////// +// +// Generic API that works on all image types +// + +// this is not threadsafe +static char *failure_reason; + +char *stbi_failure_reason(void) +{ + return failure_reason; +} + +static int e(char *str) +{ + failure_reason = str; + return 0; +} + +#ifdef STBI_NO_FAILURE_STRINGS + #define e(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define e(x,y) e(y) +#else + #define e(x,y) e(x) +#endif + +#define epf(x,y) ((float *) (e(x,y)?NULL:NULL)) +#define epuc(x,y) ((unsigned char *) (e(x,y)?NULL:NULL)) + +void stbi_image_free(void *retval_from_stbi_load) +{ + free(retval_from_stbi_load); +} + +#define MAX_LOADERS 32 +stbi_loader *loaders[MAX_LOADERS]; +static int max_loaders = 0; + +int stbi_register_loader(stbi_loader *loader) +{ + int i; + for (i=0; i < MAX_LOADERS; ++i) { + // already present? + if (loaders[i] == loader) + return 1; + // end of the list? + if (loaders[i] == NULL) { + loaders[i] = loader; + max_loaders = i+1; + return 1; + } + } + // no room for it + return 0; +} + +#ifndef STBI_NO_HDR +static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_STDIO +unsigned char *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = fopen(filename, "rb"); + unsigned char *result; + if (!f) return epuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +unsigned char *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + int i; + if (stbi_jpeg_test_file(f)) + return stbi_jpeg_load_from_file(f,x,y,comp,req_comp); + if (stbi_png_test_file(f)) + return stbi_png_load_from_file(f,x,y,comp,req_comp); + if (stbi_bmp_test_file(f)) + return stbi_bmp_load_from_file(f,x,y,comp,req_comp); + if (stbi_psd_test_file(f)) + return stbi_psd_load_from_file(f,x,y,comp,req_comp); + #ifndef STBI_NO_DDS + if (stbi_dds_test_file(f)) + return stbi_dds_load_from_file(f,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_HDR + if (stbi_hdr_test_file(f)) { + float *hdr = stbi_hdr_load_from_file(f, x,y,comp,req_comp); + return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + for (i=0; i < max_loaders; ++i) + if (loaders[i]->test_file(f)) + return loaders[i]->load_from_file(f,x,y,comp,req_comp); + // test tga last because it's a crappy test! + if (stbi_tga_test_file(f)) + return stbi_tga_load_from_file(f,x,y,comp,req_comp); + return epuc("unknown image type", "Image not of any known type, or corrupt"); +} +#endif + +unsigned char *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + int i; + if (stbi_jpeg_test_memory(buffer,len)) + return stbi_jpeg_load_from_memory(buffer,len,x,y,comp,req_comp); + if (stbi_png_test_memory(buffer,len)) + return stbi_png_load_from_memory(buffer,len,x,y,comp,req_comp); + if (stbi_bmp_test_memory(buffer,len)) + return stbi_bmp_load_from_memory(buffer,len,x,y,comp,req_comp); + if (stbi_psd_test_memory(buffer,len)) + return stbi_psd_load_from_memory(buffer,len,x,y,comp,req_comp); + #ifndef STBI_NO_DDS + if (stbi_dds_test_memory(buffer,len)) + return stbi_dds_load_from_memory(buffer,len,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_HDR + if (stbi_hdr_test_memory(buffer, len)) { + float *hdr = stbi_hdr_load_from_memory(buffer, len,x,y,comp,req_comp); + return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + for (i=0; i < max_loaders; ++i) + if (loaders[i]->test_memory(buffer,len)) + return loaders[i]->load_from_memory(buffer,len,x,y,comp,req_comp); + // test tga last because it's a crappy test! + if (stbi_tga_test_memory(buffer,len)) + return stbi_tga_load_from_memory(buffer,len,x,y,comp,req_comp); + return epuc("unknown image type", "Image not of any known type, or corrupt"); +} + +#ifndef STBI_NO_HDR + +#ifndef STBI_NO_STDIO +float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = fopen(filename, "rb"); + float *result; + if (!f) return epf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi_hdr_test_file(f)) + return stbi_hdr_load_from_file(f,x,y,comp,req_comp); + #endif + data = stbi_load_from_file(f, x, y, comp, req_comp); + if (data) + return ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return epf("unknown image type", "Image not of any known type, or corrupt"); +} +#endif + +float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *data; + #ifndef STBI_NO_HDR + if (stbi_hdr_test_memory(buffer, len)) + return stbi_hdr_load_from_memory(buffer, len,x,y,comp,req_comp); + #endif + data = stbi_load_from_memory(buffer, len, x, y, comp, req_comp); + if (data) + return ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return epf("unknown image type", "Image not of any known type, or corrupt"); +} +#endif + +// these is-hdr-or-not is defined independent of whether STBI_NO_HDR is +// defined, for API simplicity; if STBI_NO_HDR is defined, it always +// reports false! + +int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + return stbi_hdr_test_memory(buffer, len); + #else + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +extern int stbi_is_hdr (char const *filename) +{ + FILE *f = fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +extern int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + return stbi_hdr_test_file(f); + #else + return 0; + #endif +} + +#endif + +// @TODO: get image dimensions & components without fully decoding +#ifndef STBI_NO_STDIO +extern int stbi_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); +#endif +extern int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); + +#ifndef STBI_NO_HDR +static float h2l_gamma_i=1.0f/2.2f, h2l_scale_i=1.0f; +static float l2h_gamma=2.2f, l2h_scale=1.0f; + +void stbi_hdr_to_ldr_gamma(float gamma) { h2l_gamma_i = 1/gamma; } +void stbi_hdr_to_ldr_scale(float scale) { h2l_scale_i = 1/scale; } + +void stbi_ldr_to_hdr_gamma(float gamma) { l2h_gamma = gamma; } +void stbi_ldr_to_hdr_scale(float scale) { l2h_scale = scale; } +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + SCAN_load=0, + SCAN_type, + SCAN_header, +}; + +typedef struct +{ + uint32 img_x, img_y; + int img_n, img_out_n; + + #ifndef STBI_NO_STDIO + FILE *img_file; + #endif + uint8 *img_buffer, *img_buffer_end; +} stbi; + +#ifndef STBI_NO_STDIO +static void start_file(stbi *s, FILE *f) +{ + s->img_file = f; +} +#endif + +static void start_mem(stbi *s, uint8 const *buffer, int len) +{ +#ifndef STBI_NO_STDIO + s->img_file = NULL; +#endif + s->img_buffer = (uint8 *) buffer; + s->img_buffer_end = (uint8 *) buffer+len; +} + +__forceinline static int get8(stbi *s) +{ +#ifndef STBI_NO_STDIO + if (s->img_file) { + int c = fgetc(s->img_file); + return c == EOF ? 0 : c; + } +#endif + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + return 0; +} + +__forceinline static int at_eof(stbi *s) +{ +#ifndef STBI_NO_STDIO + if (s->img_file) + return feof(s->img_file); +#endif + return s->img_buffer >= s->img_buffer_end; +} + +__forceinline static uint8 get8u(stbi *s) +{ + return (uint8) get8(s); +} + +static void skip(stbi *s, int n) +{ +#ifndef STBI_NO_STDIO + if (s->img_file) + fseek(s->img_file, n, SEEK_CUR); + else +#endif + s->img_buffer += n; +} + +static int get16(stbi *s) +{ + int z = get8(s); + return (z << 8) + get8(s); +} + +static uint32 get32(stbi *s) +{ + uint32 z = get16(s); + return (z << 16) + get16(s); +} + +static int get16le(stbi *s) +{ + int z = get8(s); + return z + (get8(s) << 8); +} + +static uint32 get32le(stbi *s) +{ + uint32 z = get16le(s); + return z + (get16le(s) << 16); +} + +static void getn(stbi *s, stbi_uc *buffer, int n) +{ +#ifndef STBI_NO_STDIO + if (s->img_file) { + fread(buffer, 1, n, s->img_file); + return; + } +#endif + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; +} + +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static uint8 compute_y(int r, int g, int b) +{ + return (uint8) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static unsigned char *convert_format(unsigned char *data, int img_n, int req_comp, uint x, uint y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + assert(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) malloc(req_comp * x * y); + if (good == NULL) { + free(data); + return epuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define COMBO(a,b) ((a)*8+(b)) + #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch(COMBO(img_n, req_comp)) { + CASE(1,2) dest[0]=src[0], dest[1]=255; break; + CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break; + CASE(2,1) dest[0]=src[0]; break; + CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break; + CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break; + CASE(3,1) dest[0]=compute_y(src[0],src[1],src[2]); break; + CASE(3,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = 255; break; + CASE(4,1) dest[0]=compute_y(src[0],src[1],src[2]); break; + CASE(4,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break; + CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break; + default: assert(0); + } + #undef CASE + } + + free(data); + return good; +} + +#ifndef STBI_NO_HDR +static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output = (float *) malloc(x * y * comp * sizeof(float)); + if (output == NULL) { free(data); return epf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) pow(data[i*comp+k]/255.0f, l2h_gamma) * l2h_scale; + } + if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; + } + free(data); + return output; +} + +#define float2int(x) ((int) (x)) +static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output = (stbi_uc *) malloc(x * y * comp); + if (output == NULL) { free(data); return epuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*h2l_scale_i, h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = float2int(z); + } + } + free(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder (not actually fully baseline implementation) +// +// simple implementation +// - channel subsampling of at most 2 in each dimension +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - uses a lot of intermediate memory, could cache poorly +// - load http://nothings.org/remote/anemones.jpg 3 times on 2.8Ghz P4 +// stb_jpeg: 1.34 seconds (MSVC6, default release build) +// stb_jpeg: 1.06 seconds (MSVC6, processor = Pentium Pro) +// IJL11.dll: 1.08 seconds (compiled by intel) +// IJG 1998: 0.98 seconds (MSVC6, makefile provided by IJG) +// IJG 1998: 0.95 seconds (MSVC6, makefile + proc=PPro) + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + uint8 fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + uint16 code[256]; + uint8 values[256]; + uint8 size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} huffman; + +typedef struct +{ + #if STBI_SIMD + unsigned short dequant2[4][64]; + #endif + stbi s; + huffman huff_dc[4]; + huffman huff_ac[4]; + uint8 dequant[4][64]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + uint8 *data; + void *raw_data; + uint8 *linebuf; + } img_comp[4]; + + uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int scan_n, order[4]; + int restart_interval, todo; +} jpeg; + +static int build_huffman(huffman *h, int *count) +{ + int i,j,k=0,code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (uint8) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (uint16) (code++); + if (code-1 >= (1 << j)) return e("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (uint8) i; + } + } + } + return 1; +} + +static void grow_buffer_unsafe(jpeg *j) +{ + do { + int b = j->nomore ? 0 : get8(&j->s); + if (b == 0xff) { + int c = get8(&j->s); + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer = (j->code_buffer << 8) | b; + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static uint32 bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +__forceinline static int decode(jpeg *j, huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (j->code_bits - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + if (h->size[k] > j->code_bits) + return -1; + j->code_bits -= h->size[k]; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + if (j->code_bits < 16) + temp = (j->code_buffer << (16 - j->code_bits)) & 0xffff; + else + temp = (j->code_buffer >> (j->code_bits - 16)) & 0xffff; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (j->code_bits - k)) & bmask[k]) + h->delta[k]; + assert((((j->code_buffer) >> (j->code_bits - h->size[c])) & bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + return h->values[c]; +} + +// combined JPEG 'receive' and JPEG 'extend', since baseline +// always extends everything it receives. +__forceinline static int extend_receive(jpeg *j, int n) +{ + unsigned int m = 1 << (n-1); + unsigned int k; + if (j->code_bits < n) grow_buffer_unsafe(j); + k = (j->code_buffer >> (j->code_bits - n)) & bmask[n]; + j->code_bits -= n; + // the following test is probably a random branch that won't + // predict well. I tried to table accelerate it but failed. + // maybe it's compiling as a conditional move? + if (k < m) + return (-1 << n) + k + 1; + else + return k; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static uint8 dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int decode_block(jpeg *j, short data[64], huffman *hdc, huffman *hac, int b) +{ + int diff,dc,k; + int t = decode(j, hdc); + if (t < 0) return e("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) dc; + + // decode AC components, see JPEG spec + k = 1; + do { + int r,s; + int rs = decode(j, hac); + if (rs < 0) return e("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + data[dezigzag[k++]] = (short) extend_receive(j,s); + } + } while (k < 64); + return 1; +} + +// take a -128..127 value and clamp it and convert to 0..255 +__forceinline static uint8 clamp(int x) +{ + x += 128; + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (uint8) x; +} + +#define f2f(x) (int) (((x) * 4096 + 0.5)) +#define fsh(x) ((x) << 12) + +// derived from jidctint -- DCT_ISLOW +#define IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * f2f(0.5411961f); \ + t2 = p1 + p3*f2f(-1.847759065f); \ + t3 = p1 + p2*f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = fsh(p2+p3); \ + t1 = fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*f2f( 1.175875602f); \ + t0 = t0*f2f( 0.298631336f); \ + t1 = t1*f2f( 2.053119869f); \ + t2 = t2*f2f( 3.072711026f); \ + t3 = t3*f2f( 1.501321110f); \ + p1 = p5 + p1*f2f(-0.899976223f); \ + p2 = p5 + p2*f2f(-2.562915447f); \ + p3 = p3*f2f(-1.961570560f); \ + p4 = p4*f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +#if !STBI_SIMD +// .344 seconds on 3*anemones.jpg +static void idct_block(uint8 *out, int out_stride, short data[64], uint8 *dequantize) +{ + int i,val[64],*v=val; + uint8 *o,*dq = dequantize; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d,++dq, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0] * dq[0] << 2; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + IDCT_1D(d[ 0]*dq[ 0],d[ 8]*dq[ 8],d[16]*dq[16],d[24]*dq[24], + d[32]*dq[32],d[40]*dq[40],d[48]*dq[48],d[56]*dq[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + x0 += 65536; x1 += 65536; x2 += 65536; x3 += 65536; + o[0] = clamp((x0+t3) >> 17); + o[7] = clamp((x0-t3) >> 17); + o[1] = clamp((x1+t2) >> 17); + o[6] = clamp((x1-t2) >> 17); + o[2] = clamp((x2+t1) >> 17); + o[5] = clamp((x2-t1) >> 17); + o[3] = clamp((x3+t0) >> 17); + o[4] = clamp((x3-t0) >> 17); + } +} +#else +static void idct_block(uint8 *out, int out_stride, short data[64], unsigned short *dequantize) +{ + int i,val[64],*v=val; + uint8 *o; + unsigned short *dq = dequantize; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d,++dq, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0] * dq[0] << 2; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + IDCT_1D(d[ 0]*dq[ 0],d[ 8]*dq[ 8],d[16]*dq[16],d[24]*dq[24], + d[32]*dq[32],d[40]*dq[40],d[48]*dq[48],d[56]*dq[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + x0 += 65536; x1 += 65536; x2 += 65536; x3 += 65536; + o[0] = clamp((x0+t3) >> 17); + o[7] = clamp((x0-t3) >> 17); + o[1] = clamp((x1+t2) >> 17); + o[6] = clamp((x1-t2) >> 17); + o[2] = clamp((x2+t1) >> 17); + o[5] = clamp((x2-t1) >> 17); + o[3] = clamp((x3+t0) >> 17); + o[4] = clamp((x3-t0) >> 17); + } +} +static stbi_idct_8x8 stbi_idct_installed = idct_block; + +extern void stbi_install_idct(stbi_idct_8x8 func) +{ + stbi_idct_installed = func; +} +#endif + +#define MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static uint8 get_marker(jpeg *j) +{ + uint8 x; + if (j->marker != MARKER_none) { x = j->marker; j->marker = MARKER_none; return x; } + x = get8u(&j->s); + if (x != 0xff) return MARKER_none; + while (x == 0xff) + x = get8u(&j->s); + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, reset the entropy decoder and +// the dc prediction +static void reset(jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0; + j->marker = MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int parse_entropy_coded_data(jpeg *z) +{ + reset(z); + if (z->scan_n == 1) { + int i,j; + #if STBI_SIMD + __declspec(align(16)) + #endif + short data[64]; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + if (!decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+z->img_comp[n].ha, n)) return 0; + #if STBI_SIMD + stbi_idct_installed(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data, z->dequant2[z->img_comp[n].tq]); + #else + idct_block(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data, z->dequant[z->img_comp[n].tq]); + #endif + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!RESTART(z->marker)) return 1; + reset(z); + } + } + } + } else { // interleaved! + int i,j,k,x,y; + short data[64]; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + if (!decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+z->img_comp[n].ha, n)) return 0; + #if STBI_SIMD + stbi_idct_installed(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data, z->dequant2[z->img_comp[n].tq]); + #else + idct_block(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data, z->dequant[z->img_comp[n].tq]); + #endif + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!RESTART(z->marker)) return 1; + reset(z); + } + } + } + } + return 1; +} + +static int process_marker(jpeg *z, int m) +{ + int L; + switch (m) { + case MARKER_none: // no marker found + return e("expected marker","Corrupt JPEG"); + + case 0xC2: // SOF - progressive + return e("progressive jpeg","JPEG format not supported (progressive)"); + + case 0xDD: // DRI - specify restart interval + if (get16(&z->s) != 4) return e("bad DRI len","Corrupt JPEG"); + z->restart_interval = get16(&z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = get16(&z->s)-2; + while (L > 0) { + int q = get8(&z->s); + int p = q >> 4; + int t = q & 15,i; + if (p != 0) return e("bad DQT type","Corrupt JPEG"); + if (t > 3) return e("bad DQT table","Corrupt JPEG"); + for (i=0; i < 64; ++i) + z->dequant[t][dezigzag[i]] = get8u(&z->s); + #if STBI_SIMD + for (i=0; i < 64; ++i) + z->dequant2[t][i] = dequant[t][i]; + #endif + L -= 65; + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = get16(&z->s)-2; + while (L > 0) { + uint8 *v; + int sizes[16],i,m=0; + int q = get8(&z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return e("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = get8(&z->s); + m += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < m; ++i) + v[i] = get8u(&z->s); + L -= m; + } + return L==0; + } + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + skip(&z->s, get16(&z->s)-2); + return 1; + } + return 0; +} + +// after we see SOS +static int process_scan_header(jpeg *z) +{ + int i; + int Ls = get16(&z->s); + z->scan_n = get8(&z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s.img_n) return e("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return e("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = get8(&z->s), which; + int q = get8(&z->s); + for (which = 0; which < z->s.img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s.img_n) return 0; + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return e("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return e("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + if (get8(&z->s) != 0) return e("bad SOS","Corrupt JPEG"); + get8(&z->s); // should be 63, but might be 0 + if (get8(&z->s) != 0) return e("bad SOS","Corrupt JPEG"); + + return 1; +} + +static int process_frame_header(jpeg *z, int scan) +{ + stbi *s = &z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = get16(s); if (Lf < 11) return e("bad SOF len","Corrupt JPEG"); // JPEG + p = get8(s); if (p != 8) return e("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = get16(s); if (s->img_y == 0) return e("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = get16(s); if (s->img_x == 0) return e("0 width","Corrupt JPEG"); // JPEG requires + c = get8(s); + if (c != 3 && c != 1) return e("bad component count","Corrupt JPEG"); // JFIF requires + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return e("bad SOF len","Corrupt JPEG"); + + for (i=0; i < s->img_n; ++i) { + z->img_comp[i].id = get8(s); + if (z->img_comp[i].id != i+1) // JFIF requires + if (z->img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files! + return e("bad component ID","Corrupt JPEG"); + q = get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return e("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return e("bad V","Corrupt JPEG"); + z->img_comp[i].tq = get8(s); if (z->img_comp[i].tq > 3) return e("bad TQ","Corrupt JPEG"); + } + + if (scan != SCAN_load) return 1; + + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return e("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].raw_data = malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15); + if (z->img_comp[i].raw_data == NULL) { + for(--i; i >= 0; --i) { + free(z->img_comp[i].raw_data); + z->img_comp[i].data = NULL; + } + return e("outofmem", "Out of memory"); + } + // align blocks for installable-idct using mmx/sse + z->img_comp[i].data = (uint8*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + z->img_comp[i].linebuf = NULL; + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define DNL(x) ((x) == 0xdc) +#define SOI(x) ((x) == 0xd8) +#define EOI(x) ((x) == 0xd9) +#define SOF(x) ((x) == 0xc0 || (x) == 0xc1) +#define SOS(x) ((x) == 0xda) + +static int decode_jpeg_header(jpeg *z, int scan) +{ + int m; + z->marker = MARKER_none; // initialize cached marker to empty + m = get_marker(z); + if (!SOI(m)) return e("no SOI","Corrupt JPEG"); + if (scan == SCAN_type) return 1; + m = get_marker(z); + while (!SOF(m)) { + if (!process_marker(z,m)) return 0; + m = get_marker(z); + while (m == MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (at_eof(&z->s)) return e("no SOF", "Corrupt JPEG"); + m = get_marker(z); + } + } + if (!process_frame_header(z, scan)) return 0; + return 1; +} + +static int decode_jpeg_image(jpeg *j) +{ + int m; + j->restart_interval = 0; + if (!decode_jpeg_header(j, SCAN_load)) return 0; + m = get_marker(j); + while (!EOI(m)) { + if (SOS(m)) { + if (!process_scan_header(j)) return 0; + if (!parse_entropy_coded_data(j)) return 0; + } else { + if (!process_marker(j, m)) return 0; + } + m = get_marker(j); + } + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef uint8 *(*resample_row_func)(uint8 *out, uint8 *in0, uint8 *in1, + int w, int hs); + +#define div4(x) ((uint8) ((x) >> 2)) + +static uint8 *resample_row_1(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + return in_near; +} + +static uint8* resample_row_v_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + for (i=0; i < w; ++i) + out[i] = div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static uint8* resample_row_h_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + uint8 *input = in_near; + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = div4(n+input[i-1]); + out[i*2+1] = div4(n+input[i+1]); + } + out[i*2+0] = div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + return out; +} + +#define div16(x) ((uint8) ((x) >> 4)) + +static uint8 *resample_row_hv_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = div16(3*t0 + t1 + 8); + out[i*2 ] = div16(3*t1 + t0 + 8); + } + out[w*2-1] = div4(t1+2); + return out; +} + +static uint8 *resample_row_generic(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +#define float2fixed(x) ((int) ((x) * 65536 + 0.5)) + +// 0.38 seconds on 3*anemones.jpg (0.25 with processor = Pro) +// VC6 without processor=Pro is generating multiple LEAs per multiply! +static void YCbCr_to_RGB_row(uint8 *out, uint8 *y, uint8 *pcb, uint8 *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 16) + 32768; // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr*float2fixed(1.40200f); + g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); + b = y_fixed + cb*float2fixed(1.77200f); + r >>= 16; + g >>= 16; + b >>= 16; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (uint8)r; + out[1] = (uint8)g; + out[2] = (uint8)b; + out[3] = 255; + out += step; + } +} + +#if STBI_SIMD +static stbi_YCbCr_to_RGB_run stbi_YCbCr_installed = YCbCr_to_RGB_row; + +void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func) +{ + stbi_YCbCr_installed = func; +} +#endif + + +// clean up the temporary component buffers +static void cleanup_jpeg(jpeg *j) +{ + int i; + for (i=0; i < j->s.img_n; ++i) { + if (j->img_comp[i].data) { + free(j->img_comp[i].raw_data); + j->img_comp[i].data = NULL; + } + if (j->img_comp[i].linebuf) { + free(j->img_comp[i].linebuf); + j->img_comp[i].linebuf = NULL; + } + } +} + +typedef struct +{ + resample_row_func resample; + uint8 *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi_resample; + +static uint8 *load_jpeg_image(jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n; + // validate req_comp + if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error"); + z->s.img_n = 0; + + // load a jpeg image from whichever source + if (!decode_jpeg_image(z)) { cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s.img_n; + + if (z->s.img_n == 3 && n < 3) + decode_n = 1; + else + decode_n = z->s.img_n; + + // resample and color-convert + { + int k; + uint i,j; + uint8 *output; + uint8 *coutput[4]; + + stbi_resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi_resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (uint8 *) malloc(z->s.img_x + 3); + if (!z->img_comp[k].linebuf) { cleanup_jpeg(z); return epuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s.img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = resample_row_hv_2; + else r->resample = resample_row_generic; + } + + // can't error after this so, this is safe + output = (uint8 *) malloc(n * z->s.img_x * z->s.img_y + 1); + if (!output) { cleanup_jpeg(z); return epuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s.img_y; ++j) { + uint8 *out = output + n * z->s.img_x * j; + for (k=0; k < decode_n; ++k) { + stbi_resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + uint8 *y = coutput[0]; + if (z->s.img_n == 3) { + #if STBI_SIMD + stbi_YCbCr_installed(out, y, coutput[1], coutput[2], z->s.img_x, n); + #else + YCbCr_to_RGB_row(out, y, coutput[1], coutput[2], z->s.img_x, n); + #endif + } else + for (i=0; i < z->s.img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + uint8 *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s.img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s.img_x; ++i) *out++ = y[i], *out++ = 255; + } + } + cleanup_jpeg(z); + *out_x = z->s.img_x; + *out_y = z->s.img_y; + if (comp) *comp = z->s.img_n; // report original components, not output + return output; + } +} + +#ifndef STBI_NO_STDIO +unsigned char *stbi_jpeg_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + jpeg j; + start_file(&j.s, f); + return load_jpeg_image(&j, x,y,comp,req_comp); +} + +unsigned char *stbi_jpeg_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + FILE *f = fopen(filename, "rb"); + if (!f) return NULL; + data = stbi_jpeg_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return data; +} +#endif + +unsigned char *stbi_jpeg_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + jpeg j; + start_mem(&j.s, buffer,len); + return load_jpeg_image(&j, x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +int stbi_jpeg_test_file(FILE *f) +{ + int n,r; + jpeg j; + n = ftell(f); + start_file(&j.s, f); + r = decode_jpeg_header(&j, SCAN_type); + fseek(f,n,SEEK_SET); + return r; +} +#endif + +int stbi_jpeg_test_memory(stbi_uc const *buffer, int len) +{ + jpeg j; + start_mem(&j.s, buffer,len); + return decode_jpeg_header(&j, SCAN_type); +} + +// @TODO: +#ifndef STBI_NO_STDIO +extern int stbi_jpeg_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_jpeg_info_from_file (FILE *f, int *x, int *y, int *comp); +#endif +extern int stbi_jpeg_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define ZFAST_BITS 9 // accelerate all cases in default tables +#define ZFAST_MASK ((1 << ZFAST_BITS) - 1) + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + uint16 fast[1 << ZFAST_BITS]; + uint16 firstcode[16]; + int maxcode[17]; + uint16 firstsymbol[16]; + uint8 size[288]; + uint16 value[288]; +} zhuffman; + +__forceinline static int bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +__forceinline static int bit_reverse(int v, int bits) +{ + assert(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return bitreverse16(v) >> (16-bits); +} + +static int zbuild_huffman(zhuffman *z, uint8 *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 255, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + assert(sizes[i] <= (1 << i)); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (uint16) code; + z->firstsymbol[i] = (uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return e("bad codelengths","Corrupt JPEG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + z->size[c] = (uint8)s; + z->value[c] = (uint16)i; + if (s <= ZFAST_BITS) { + int k = bit_reverse(next_code[s],s); + while (k < (1 << ZFAST_BITS)) { + z->fast[k] = (uint16) c; + k += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + uint8 *zbuffer, *zbuffer_end; + int num_bits; + uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + zhuffman z_length, z_distance; +} zbuf; + +__forceinline static int zget8(zbuf *z) +{ + if (z->zbuffer >= z->zbuffer_end) return 0; + return *z->zbuffer++; +} + +static void fill_bits(zbuf *z) +{ + do { + assert(z->code_buffer < (1U << z->num_bits)); + z->code_buffer |= zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +__forceinline static unsigned int zreceive(zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +__forceinline static int zhuffman_decode(zbuf *a, zhuffman *z) +{ + int b,s,k; + if (a->num_bits < 16) fill_bits(a); + b = z->fast[a->code_buffer & ZFAST_MASK]; + if (b < 0xffff) { + s = z->size[b]; + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; + } + + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = bit_reverse(a->code_buffer, 16); + for (s=ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s == 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + assert(z->size[b] == s); + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +static int expand(zbuf *z, int n) // need to make room for n bytes +{ + char *q; + int cur, limit; + if (!z->z_expandable) return e("output buffer limit","Corrupt PNG"); + cur = (int) (z->zout - z->zout_start); + limit = (int) (z->zout_end - z->zout_start); + while (cur + n > limit) + limit *= 2; + q = (char *) realloc(z->zout_start, limit); + if (q == NULL) return e("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static int length_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static int length_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static int dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static int dist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int parse_huffman_block(zbuf *a) +{ + for(;;) { + int z = zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return e("bad huffman code","Corrupt PNG"); // error in huffman codes + if (a->zout >= a->zout_end) if (!expand(a, 1)) return 0; + *a->zout++ = (char) z; + } else { + uint8 *p; + int len,dist; + if (z == 256) return 1; + z -= 257; + len = length_base[z]; + if (length_extra[z]) len += zreceive(a, length_extra[z]); + z = zhuffman_decode(a, &a->z_distance); + if (z < 0) return e("bad huffman code","Corrupt PNG"); + dist = dist_base[z]; + if (dist_extra[z]) dist += zreceive(a, dist_extra[z]); + if (a->zout - a->zout_start < dist) return e("bad dist","Corrupt PNG"); + if (a->zout + len > a->zout_end) if (!expand(a, len)) return 0; + p = (uint8 *) (a->zout - dist); + while (len--) + *a->zout++ = *p++; + } + } +} + +static int compute_huffman_codes(zbuf *a) +{ + static uint8 length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + static zhuffman z_codelength; // static just to save stack space + uint8 lencodes[286+32+137];//padding for maximum single op + uint8 codelength_sizes[19]; + int i,n; + + int hlit = zreceive(a,5) + 257; + int hdist = zreceive(a,5) + 1; + int hclen = zreceive(a,4) + 4; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (uint8) s; + } + if (!zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < hlit + hdist) { + int c = zhuffman_decode(a, &z_codelength); + assert(c >= 0 && c < 19); + if (c < 16) + lencodes[n++] = (uint8) c; + else if (c == 16) { + c = zreceive(a,2)+3; + memset(lencodes+n, lencodes[n-1], c); + n += c; + } else if (c == 17) { + c = zreceive(a,3)+3; + memset(lencodes+n, 0, c); + n += c; + } else { + assert(c == 18); + c = zreceive(a,7)+11; + memset(lencodes+n, 0, c); + n += c; + } + } + if (n != hlit+hdist) return e("bad codelengths","Corrupt PNG"); + if (!zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int parse_uncompressed_block(zbuf *a) +{ + uint8 header[4]; + int len,nlen,k; + if (a->num_bits & 7) + zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (uint8) (a->code_buffer & 255); // wtf this warns? + a->code_buffer >>= 8; + a->num_bits -= 8; + } + assert(a->num_bits == 0); + // now fill header the normal way + while (k < 4) + header[k++] = (uint8) zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return e("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return e("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!expand(a, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int parse_zlib_header(zbuf *a) +{ + int cmf = zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = zget8(a); + if ((cmf*256+flg) % 31 != 0) return e("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return e("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return e("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +// @TODO: should statically initialize these for optimal thread safety +static uint8 default_length[288], default_distance[32]; +static void init_defaults(void) +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) default_length[i] = 8; + for ( ; i <= 255; ++i) default_length[i] = 9; + for ( ; i <= 279; ++i) default_length[i] = 7; + for ( ; i <= 287; ++i) default_length[i] = 8; + + for (i=0; i <= 31; ++i) default_distance[i] = 5; +} + +static int parse_zlib(zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = zreceive(a,1); + type = zreceive(a,2); + if (type == 0) { + if (!parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!default_distance[31]) init_defaults(); + if (!zbuild_huffman(&a->z_length , default_length , 288)) return 0; + if (!zbuild_huffman(&a->z_distance, default_distance, 32)) return 0; + } else { + if (!compute_huffman_codes(a)) return 0; + } + if (!parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int do_zlib(zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return parse_zlib(a, parse_header); +} + +char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + zbuf a; + char *p = (char *) malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (uint8 *) buffer; + a.zbuffer_end = (uint8 *) buffer + len; + if (do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + free(a.zout_start); + return NULL; + } +} + +char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + zbuf a; + a.zbuffer = (uint8 *) ibuffer; + a.zbuffer_end = (uint8 *) ibuffer + ilen; + if (do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + zbuf a; + char *p = (char *) malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (uint8 *) buffer; + a.zbuffer_end = (uint8 *) buffer+len; + if (do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + free(a.zout_start); + return NULL; + } +} + +int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + zbuf a; + a.zbuffer = (uint8 *) ibuffer; + a.zbuffer_end = (uint8 *) ibuffer + ilen; + if (do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + + +typedef struct +{ + uint32 length; + uint32 type; +} chunk; + +#define PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) + +static chunk get_chunk_header(stbi *s) +{ + chunk c; + c.length = get32(s); + c.type = get32(s); + return c; +} + +static int check_png_header(stbi *s) +{ + static uint8 png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (get8(s) != png_sig[i]) return e("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi s; + uint8 *idata, *expanded, *out; +} png; + + +enum { + F_none=0, F_sub=1, F_up=2, F_avg=3, F_paeth=4, + F_avg_first, F_paeth_first, +}; + +static uint8 first_row_filter[5] = +{ + F_none, F_sub, F_none, F_avg_first, F_paeth_first +}; + +static int paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +// create the png data from post-deflated data +static int create_png_image(png *a, uint8 *raw, uint32 raw_len, int out_n) +{ + stbi *s = &a->s; + uint32 i,j,stride = s->img_x*out_n; + int k; + int img_n = s->img_n; // copy it into a local for later + assert(out_n == s->img_n || out_n == s->img_n+1); + a->out = (uint8 *) malloc(s->img_x * s->img_y * out_n); + if (!a->out) return e("outofmem", "Out of memory"); + if (raw_len != (img_n * s->img_x + 1) * s->img_y) return e("not enough pixels","Corrupt PNG"); + for (j=0; j < s->img_y; ++j) { + uint8 *cur = a->out + stride*j; + uint8 *prior = cur - stride; + int filter = *raw++; + if (filter > 4) return e("invalid filter","Corrupt PNG"); + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + // handle first pixel explicitly + for (k=0; k < img_n; ++k) { + switch(filter) { + case F_none : cur[k] = raw[k]; break; + case F_sub : cur[k] = raw[k]; break; + case F_up : cur[k] = raw[k] + prior[k]; break; + case F_avg : cur[k] = raw[k] + (prior[k]>>1); break; + case F_paeth : cur[k] = (uint8) (raw[k] + paeth(0,prior[k],0)); break; + case F_avg_first : cur[k] = raw[k]; break; + case F_paeth_first: cur[k] = raw[k]; break; + } + } + if (img_n != out_n) cur[img_n] = 255; + raw += img_n; + cur += out_n; + prior += out_n; + // this is a little gross, so that we don't switch per-pixel or per-component + if (img_n == out_n) { + #define CASE(f) \ + case f: \ + for (i=s->img_x-1; i >= 1; --i, raw+=img_n,cur+=img_n,prior+=img_n) \ + for (k=0; k < img_n; ++k) + switch(filter) { + CASE(F_none) cur[k] = raw[k]; break; + CASE(F_sub) cur[k] = raw[k] + cur[k-img_n]; break; + CASE(F_up) cur[k] = raw[k] + prior[k]; break; + CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-img_n])>>1); break; + CASE(F_paeth) cur[k] = (uint8) (raw[k] + paeth(cur[k-img_n],prior[k],prior[k-img_n])); break; + CASE(F_avg_first) cur[k] = raw[k] + (cur[k-img_n] >> 1); break; + CASE(F_paeth_first) cur[k] = (uint8) (raw[k] + paeth(cur[k-img_n],0,0)); break; + } + #undef CASE + } else { + assert(img_n+1 == out_n); + #define CASE(f) \ + case f: \ + for (i=s->img_x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \ + for (k=0; k < img_n; ++k) + switch(filter) { + CASE(F_none) cur[k] = raw[k]; break; + CASE(F_sub) cur[k] = raw[k] + cur[k-out_n]; break; + CASE(F_up) cur[k] = raw[k] + prior[k]; break; + CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-out_n])>>1); break; + CASE(F_paeth) cur[k] = (uint8) (raw[k] + paeth(cur[k-out_n],prior[k],prior[k-out_n])); break; + CASE(F_avg_first) cur[k] = raw[k] + (cur[k-out_n] >> 1); break; + CASE(F_paeth_first) cur[k] = (uint8) (raw[k] + paeth(cur[k-out_n],0,0)); break; + } + #undef CASE + } + } + return 1; +} + +static int compute_transparency(png *z, uint8 tc[3], int out_n) +{ + stbi *s = &z->s; + uint32 i, pixel_count = s->img_x * s->img_y; + uint8 *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + assert(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int expand_palette(png *a, uint8 *palette, int len, int pal_img_n) +{ + uint32 i, pixel_count = a->s.img_x * a->s.img_y; + uint8 *p, *temp_out, *orig = a->out; + + p = (uint8 *) malloc(pixel_count * pal_img_n); + if (p == NULL) return e("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + free(a->out); + a->out = temp_out; + return 1; +} + +static int parse_png_file(png *z, int scan, int req_comp) +{ + uint8 palette[1024], pal_img_n=0; + uint8 has_trans=0, tc[3]; + uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k; + stbi *s = &z->s; + + if (!check_png_header(s)) return 0; + + if (scan == SCAN_type) return 1; + + for(;;first=0) { + chunk c = get_chunk_header(s); + if (first && c.type != PNG_TYPE('I','H','D','R')) + return e("first not IHDR","Corrupt PNG"); + switch (c.type) { + case PNG_TYPE('I','H','D','R'): { + int depth,color,interlace,comp,filter; + if (!first) return e("multiple IHDR","Corrupt PNG"); + if (c.length != 13) return e("bad IHDR len","Corrupt PNG"); + s->img_x = get32(s); if (s->img_x > (1 << 24)) return e("too large","Very large image (corrupt?)"); + s->img_y = get32(s); if (s->img_y > (1 << 24)) return e("too large","Very large image (corrupt?)"); + depth = get8(s); if (depth != 8) return e("8bit only","PNG not supported: 8-bit only"); + color = get8(s); if (color > 6) return e("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return e("bad ctype","Corrupt PNG"); + comp = get8(s); if (comp) return e("bad comp method","Corrupt PNG"); + filter= get8(s); if (filter) return e("bad filter method","Corrupt PNG"); + interlace = get8(s); if (interlace) return e("interlaced","PNG not supported: interlaced mode"); + if (!s->img_x || !s->img_y) return e("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return e("too large", "Image too large to decode"); + if (scan == SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return e("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case PNG_TYPE('P','L','T','E'): { + if (c.length > 256*3) return e("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return e("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = get8u(s); + palette[i*4+1] = get8u(s); + palette[i*4+2] = get8u(s); + palette[i*4+3] = 255; + } + break; + } + + case PNG_TYPE('t','R','N','S'): { + if (z->idata) return e("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return e("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return e("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = get8u(s); + } else { + if (!(s->img_n & 1)) return e("tRNS with alpha","Corrupt PNG"); + if (c.length != (uint32) s->img_n*2) return e("bad tRNS len","Corrupt PNG"); + has_trans = 1; + for (k=0; k < s->img_n; ++k) + tc[k] = (uint8) get16(s); // non 8-bit images will be larger + } + break; + } + + case PNG_TYPE('I','D','A','T'): { + if (pal_img_n && !pal_len) return e("no PLTE","Corrupt PNG"); + if (scan == SCAN_header) { s->img_n = pal_img_n; return 1; } + if (ioff + c.length > idata_limit) { + uint8 *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + p = (uint8 *) realloc(z->idata, idata_limit); if (p == NULL) return e("outofmem", "Out of memory"); + z->idata = p; + } + #ifndef STBI_NO_STDIO + if (s->img_file) + { + if (fread(z->idata+ioff,1,c.length,s->img_file) != c.length) return e("outofdata","Corrupt PNG"); + } + else + #endif + { + memcpy(z->idata+ioff, s->img_buffer, c.length); + s->img_buffer += c.length; + } + ioff += c.length; + break; + } + + case PNG_TYPE('I','E','N','D'): { + uint32 raw_len; + if (scan != SCAN_load) return 1; + if (z->idata == NULL) return e("no IDAT","Corrupt PNG"); + z->expanded = (uint8 *) stbi_zlib_decode_malloc((char *) z->idata, ioff, (int *) &raw_len); + if (z->expanded == NULL) return 0; // zlib should set error + free(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!create_png_image(z, z->expanded, raw_len, s->img_out_n)) return 0; + if (has_trans) + if (!compute_transparency(z, tc, s->img_out_n)) return 0; + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!expand_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } + free(z->expanded); z->expanded = NULL; + return 1; + } + + default: + // if critical, fail + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX chunk not known"; + invalid_chunk[0] = (uint8) (c.type >> 24); + invalid_chunk[1] = (uint8) (c.type >> 16); + invalid_chunk[2] = (uint8) (c.type >> 8); + invalid_chunk[3] = (uint8) (c.type >> 0); + #endif + return e(invalid_chunk, "PNG not supported: unknown chunk type"); + } + skip(s, c.length); + break; + } + // end of chunk, read and skip CRC + get32(s); + } +} + +static unsigned char *do_png(png *p, int *x, int *y, int *n, int req_comp) +{ + unsigned char *result=NULL; + p->expanded = NULL; + p->idata = NULL; + p->out = NULL; + if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error"); + if (parse_png_file(p, SCAN_load, req_comp)) { + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s.img_out_n) { + result = convert_format(result, p->s.img_out_n, req_comp, p->s.img_x, p->s.img_y); + p->s.img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s.img_x; + *y = p->s.img_y; + if (n) *n = p->s.img_n; + } + free(p->out); p->out = NULL; + free(p->expanded); p->expanded = NULL; + free(p->idata); p->idata = NULL; + + return result; +} + +#ifndef STBI_NO_STDIO +unsigned char *stbi_png_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + png p; + start_file(&p.s, f); + return do_png(&p, x,y,comp,req_comp); +} + +unsigned char *stbi_png_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + FILE *f = fopen(filename, "rb"); + if (!f) return NULL; + data = stbi_png_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return data; +} +#endif + +unsigned char *stbi_png_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + png p; + start_mem(&p.s, buffer,len); + return do_png(&p, x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +int stbi_png_test_file(FILE *f) +{ + png p; + int n,r; + n = ftell(f); + start_file(&p.s, f); + r = parse_png_file(&p, SCAN_type,STBI_default); + fseek(f,n,SEEK_SET); + return r; +} +#endif + +int stbi_png_test_memory(stbi_uc const *buffer, int len) +{ + png p; + start_mem(&p.s, buffer, len); + return parse_png_file(&p, SCAN_type,STBI_default); +} + +// TODO: load header from png +#ifndef STBI_NO_STDIO +extern int stbi_png_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_png_info_from_file (FILE *f, int *x, int *y, int *comp); +#endif +extern int stbi_png_info_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp); + +// Microsoft/Windows BMP image + +static int bmp_test(stbi *s) +{ + int sz; + if (get8(s) != 'B') return 0; + if (get8(s) != 'M') return 0; + get32le(s); // discard filesize + get16le(s); // discard reserved + get16le(s); // discard reserved + get32le(s); // discard data offset + sz = get32le(s); + if (sz == 12 || sz == 40 || sz == 56 || sz == 108) return 1; + return 0; +} + +#ifndef STBI_NO_STDIO +int stbi_bmp_test_file (FILE *f) +{ + stbi s; + int r,n = ftell(f); + start_file(&s,f); + r = bmp_test(&s); + fseek(f,n,SEEK_SET); + return r; +} +#endif + +int stbi_bmp_test_memory (stbi_uc const *buffer, int len) +{ + stbi s; + start_mem(&s, buffer, len); + return bmp_test(&s); +} + +// returns 0..31 for the highest set bit +static int high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) n += 16, z >>= 16; + if (z >= 0x00100) n += 8, z >>= 8; + if (z >= 0x00010) n += 4, z >>= 4; + if (z >= 0x00004) n += 2, z >>= 2; + if (z >= 0x00002) n += 1, z >>= 1; + return n; +} + +static int bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +static int shiftsigned(int v, int shift, int bits) +{ + int result; + int z=0; + + if (shift < 0) v <<= -shift; + else v >>= shift; + result = v; + + z = bits; + while (z < 8) { + result += v >> z; + z += bits; + } + return result; +} + +static stbi_uc *bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + uint8 *out; + unsigned int mr=0,mg=0,mb=0,ma=0; + stbi_uc pal[256][4]; + int psize=0,i,j,compress=0,width; + int bpp, flip_vertically, pad, target, offset, hsz; + if (get8(s) != 'B' || get8(s) != 'M') return epuc("not BMP", "Corrupt BMP"); + get32le(s); // discard filesize + get16le(s); // discard reserved + get16le(s); // discard reserved + offset = get32le(s); + hsz = get32le(s); + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) return epuc("unknown BMP", "BMP type not supported: unknown"); + failure_reason = "bad BMP"; + if (hsz == 12) { + s->img_x = get16le(s); + s->img_y = get16le(s); + } else { + s->img_x = get32le(s); + s->img_y = get32le(s); + } + if (get16le(s) != 1) return 0; + bpp = get16le(s); + if (bpp == 1) return epuc("monochrome", "BMP type not supported: 1-bit"); + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + if (hsz == 12) { + if (bpp < 24) + psize = (offset - 14 - 24) / 3; + } else { + compress = get32le(s); + if (compress == 1 || compress == 2) return epuc("BMP RLE", "BMP type not supported: RLE"); + get32le(s); // discard sizeof + get32le(s); // discard hres + get32le(s); // discard vres + get32le(s); // discard colorsused + get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + get32le(s); + get32le(s); + get32le(s); + get32le(s); + } + if (bpp == 16 || bpp == 32) { + mr = mg = mb = 0; + if (compress == 0) { + if (bpp == 32) { + mr = 0xff << 16; + mg = 0xff << 8; + mb = 0xff << 0; + } else { + mr = 31 << 10; + mg = 31 << 5; + mb = 31 << 0; + } + } else if (compress == 3) { + mr = get32le(s); + mg = get32le(s); + mb = get32le(s); + // not documented, but generated by photoshop and handled by mspaint + if (mr == mg && mg == mb) { + // ?!?!? + return NULL; + } + } else + return NULL; + } + } else { + assert(hsz == 108); + mr = get32le(s); + mg = get32le(s); + mb = get32le(s); + ma = get32le(s); + get32le(s); // discard color space + for (i=0; i < 12; ++i) + get32le(s); // discard color space parameters + } + if (bpp < 16) + psize = (offset - 14 - hsz) >> 2; + } + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + out = (stbi_uc *) malloc(target * s->img_x * s->img_y); + if (!out) return epuc("outofmem", "Out of memory"); + if (bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { free(out); return epuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = get8(s); + pal[i][1] = get8(s); + pal[i][0] = get8(s); + if (hsz != 12) get8(s); + pal[i][3] = 255; + } + skip(s, offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4)); + if (bpp == 4) width = (s->img_x + 1) >> 1; + else if (bpp == 8) width = s->img_x; + else { free(out); return epuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=get8(s),v2=0; + if (bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (bpp == 8) ? get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + skip(s, pad); + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + skip(s, offset - 14 - hsz); + if (bpp == 24) width = 3 * s->img_x; + else if (bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (bpp == 24) { + easy = 1; + } else if (bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0xff000000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) return epuc("bad masks", "Corrupt BMP"); + // right shift amt to put high bit in position #7 + rshift = high_bit(mr)-7; rcount = bitcount(mr); + gshift = high_bit(mg)-7; gcount = bitcount(mr); + bshift = high_bit(mb)-7; bcount = bitcount(mr); + ashift = high_bit(ma)-7; acount = bitcount(mr); + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + int a; + out[z+2] = get8(s); + out[z+1] = get8(s); + out[z+0] = get8(s); + z += 3; + a = (easy == 2 ? get8(s) : 255); + if (target == 4) out[z++] = a; + } + } else { + for (i=0; i < (int) s->img_x; ++i) { + uint32 v = (bpp == 16 ? get16le(s) : get32le(s)); + int a; + out[z++] = shiftsigned(v & mr, rshift, rcount); + out[z++] = shiftsigned(v & mg, gshift, gcount); + out[z++] = shiftsigned(v & mb, bshift, bcount); + a = (ma ? shiftsigned(v & ma, ashift, acount) : 255); + if (target == 4) out[z++] = a; + } + } + skip(s, pad); + } + } + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i], p1[i] = p2[i], p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = target; + return out; +} + +#ifndef STBI_NO_STDIO +stbi_uc *stbi_bmp_load (char const *filename, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *data; + FILE *f = fopen(filename, "rb"); + if (!f) return NULL; + data = stbi_bmp_load_from_file(f, x,y,comp,req_comp); + fclose(f); + return data; +} + +stbi_uc *stbi_bmp_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_file(&s, f); + return bmp_load(&s, x,y,comp,req_comp); +} +#endif + +stbi_uc *stbi_bmp_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_mem(&s, buffer, len); + return bmp_load(&s, x,y,comp,req_comp); +} + +// Targa Truevision - TGA +// by Jonathan Dummer + +static int tga_test(stbi *s) +{ + int sz; + get8u(s); // discard Offset + sz = get8u(s); // color type + if( sz > 1 ) return 0; // only RGB or indexed allowed + sz = get8u(s); // image type + if( (sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11) ) return 0; // only RGB or grey allowed, +/- RLE + get16(s); // discard palette start + get16(s); // discard palette length + get8(s); // discard bits per palette color entry + get16(s); // discard x origin + get16(s); // discard y origin + if( get16(s) < 1 ) return 0; // test width + if( get16(s) < 1 ) return 0; // test height + sz = get8(s); // bits per pixel + if( (sz != 8) && (sz != 16) && (sz != 24) && (sz != 32) ) return 0; // only RGB or RGBA or grey allowed + return 1; // seems to have passed everything +} + +#ifndef STBI_NO_STDIO +int stbi_tga_test_file (FILE *f) +{ + stbi s; + int r,n = ftell(f); + start_file(&s, f); + r = tga_test(&s); + fseek(f,n,SEEK_SET); + return r; +} +#endif + +int stbi_tga_test_memory (stbi_uc const *buffer, int len) +{ + stbi s; + start_mem(&s, buffer, len); + return tga_test(&s); +} + +static stbi_uc *tga_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + // read in the TGA header stuff + int tga_offset = get8u(s); + int tga_indexed = get8u(s); + int tga_image_type = get8u(s); + int tga_is_RLE = 0; + int tga_palette_start = get16le(s); + int tga_palette_len = get16le(s); + int tga_palette_bits = get8u(s); + int tga_x_origin = get16le(s); + int tga_y_origin = get16le(s); + int tga_width = get16le(s); + int tga_height = get16le(s); + int tga_bits_per_pixel = get8u(s); + int tga_inverted = get8u(s); + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4]; + unsigned char trans_data[] = { 0,0,0,0 }; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + // do a tiny bit of precessing + if( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + /* int tga_alpha_bits = tga_inverted & 15; */ + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // error check + if( //(tga_indexed) || + (tga_width < 1) || (tga_height < 1) || + (tga_image_type < 1) || (tga_image_type > 3) || + ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) && + (tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32)) + ) + { + return NULL; + } + + // If I'm paletted, then I'll use the number of bits from the palette + if( tga_indexed ) + { + tga_bits_per_pixel = tga_palette_bits; + } + + // tga info + *x = tga_width; + *y = tga_height; + if( (req_comp < 1) || (req_comp > 4) ) + { + // just use whatever the file was + req_comp = tga_bits_per_pixel / 8; + *comp = req_comp; + } else + { + // force a new number of components + *comp = tga_bits_per_pixel/8; + } + tga_data = (unsigned char*)malloc( tga_width * tga_height * req_comp ); + + // skip to the data's starting position (offset usually = 0) + skip(s, tga_offset ); + // do I need to load a palette? + if( tga_indexed ) + { + // any data to skip? (offset usually = 0) + skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)malloc( tga_palette_len * tga_palette_bits / 8 ); + getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8 ); + } + // load the data + for( i = 0; i < tga_width * tga_height; ++i ) + { + // if I'm in RLE mode, do I need to get a RLE chunk? + if( tga_is_RLE ) + { + if( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = get8u(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if( read_next_pixel ) + { + // load however much data we did have + if( tga_indexed ) + { + // read in 1 byte, then perform the lookup + int pal_idx = get8u(s); + if( pal_idx >= tga_palette_len ) + { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_bits_per_pixel / 8; + for( j = 0; j*8 < tga_bits_per_pixel; ++j ) + { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else + { + // read in the data raw + for( j = 0; j*8 < tga_bits_per_pixel; ++j ) + { + raw_data[j] = get8u(s); + } + } + // convert raw to the intermediate format + switch( tga_bits_per_pixel ) + { + case 8: + // Luminous => RGBA + trans_data[0] = raw_data[0]; + trans_data[1] = raw_data[0]; + trans_data[2] = raw_data[0]; + trans_data[3] = 255; + break; + case 16: + // Luminous,Alpha => RGBA + trans_data[0] = raw_data[0]; + trans_data[1] = raw_data[0]; + trans_data[2] = raw_data[0]; + trans_data[3] = raw_data[1]; + break; + case 24: + // BGR => RGBA + trans_data[0] = raw_data[2]; + trans_data[1] = raw_data[1]; + trans_data[2] = raw_data[0]; + trans_data[3] = 255; + break; + case 32: + // BGRA => RGBA + trans_data[0] = raw_data[2]; + trans_data[1] = raw_data[1]; + trans_data[2] = raw_data[0]; + trans_data[3] = raw_data[3]; + break; + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + // convert to final format + switch( req_comp ) + { + case 1: + // RGBA => Luminance + tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]); + break; + case 2: + // RGBA => Luminance,Alpha + tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]); + tga_data[i*req_comp+1] = trans_data[3]; + break; + case 3: + // RGBA => RGB + tga_data[i*req_comp+0] = trans_data[0]; + tga_data[i*req_comp+1] = trans_data[1]; + tga_data[i*req_comp+2] = trans_data[2]; + break; + case 4: + // RGBA => RGBA + tga_data[i*req_comp+0] = trans_data[0]; + tga_data[i*req_comp+1] = trans_data[1]; + tga_data[i*req_comp+2] = trans_data[2]; + tga_data[i*req_comp+3] = trans_data[3]; + break; + } + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if( tga_inverted ) + { + for( j = 0; j*2 < tga_height; ++j ) + { + int index1 = j * tga_width * req_comp; + int index2 = (tga_height - 1 - j) * tga_width * req_comp; + for( i = tga_width * req_comp; i > 0; --i ) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if( tga_palette != NULL ) + { + free( tga_palette ); + } + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + // OK, done + return tga_data; +} + +#ifndef STBI_NO_STDIO +stbi_uc *stbi_tga_load (char const *filename, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *data; + FILE *f = fopen(filename, "rb"); + if (!f) return NULL; + data = stbi_tga_load_from_file(f, x,y,comp,req_comp); + fclose(f); + return data; +} + +stbi_uc *stbi_tga_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_file(&s, f); + return tga_load(&s, x,y,comp,req_comp); +} +#endif + +stbi_uc *stbi_tga_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_mem(&s, buffer, len); + return tga_load(&s, x,y,comp,req_comp); +} + + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicholas Schulz, tweaked by STB + +static int psd_test(stbi *s) +{ + if (get32(s) != 0x38425053) return 0; // "8BPS" + else return 1; +} + +#ifndef STBI_NO_STDIO +int stbi_psd_test_file(FILE *f) +{ + stbi s; + int r,n = ftell(f); + start_file(&s, f); + r = psd_test(&s); + fseek(f,n,SEEK_SET); + return r; +} +#endif + +int stbi_psd_test_memory(stbi_uc const *buffer, int len) +{ + stbi s; + start_mem(&s, buffer, len); + return psd_test(&s); +} + +static stbi_uc *psd_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + int pixelCount; + int channelCount, compression; + int channel, i, count, len; + int w,h; + uint8 *out; + + // Check identifier + if (get32(s) != 0x38425053) // "8BPS" + return epuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (get16(s) != 1) + return epuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = get16(s); + if (channelCount < 0 || channelCount > 16) + return epuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = get32(s); + w = get32(s); + + // Make sure the depth is 8 bits. + if (get16(s) != 8) + return epuc("unsupported bit depth", "PSD bit depth is not 8 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (get16(s) != 3) + return epuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + skip(s,get32(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + skip(s, get32(s) ); + + // Skip the reserved data. + skip(s, get32(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = get16(s); + if (compression > 1) + return epuc("bad compression", "PSD has an unknown compression format"); + + // Create the destination image. + out = (stbi_uc *) malloc(4 * w*h); + if (!out) return epuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, + // which we're going to just skip. + skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + uint8 *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++) *p = (channel == 3 ? 255 : 0), p += 4; + } else { + // Read the RLE data. + count = 0; + while (count < pixelCount) { + len = get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + count += len; + while (len) { + *p = get8(s); + p += 4; + len--; + } + } else if (len > 128) { + uint32 val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len ^= 0x0FF; + len += 2; + val = get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + uint8 *p; + + p = out + channel; + if (channel > channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++) *p = channel == 3 ? 255 : 0, p += 4; + } else { + // Read the data. + count = 0; + for (i = 0; i < pixelCount; i++) + *p = get8(s), p += 4; + } + } + } + + if (req_comp && req_comp != 4) { + out = convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // convert_format frees input on failure + } + + if (comp) *comp = channelCount; + *y = h; + *x = w; + + return out; +} + +#ifndef STBI_NO_STDIO +stbi_uc *stbi_psd_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *data; + FILE *f = fopen(filename, "rb"); + if (!f) return NULL; + data = stbi_psd_load_from_file(f, x,y,comp,req_comp); + fclose(f); + return data; +} + +stbi_uc *stbi_psd_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_file(&s, f); + return psd_load(&s, x,y,comp,req_comp); +} +#endif + +stbi_uc *stbi_psd_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_mem(&s, buffer, len); + return psd_load(&s, x,y,comp,req_comp); +} + + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int hdr_test(stbi *s) +{ + char *signature = "#?RADIANCE\n"; + int i; + for (i=0; signature[i]; ++i) + if (get8(s) != signature[i]) + return 0; + return 1; +} + +int stbi_hdr_test_memory(stbi_uc const *buffer, int len) +{ + stbi s; + start_mem(&s, buffer, len); + return hdr_test(&s); +} + +#ifndef STBI_NO_STDIO +int stbi_hdr_test_file(FILE *f) +{ + stbi s; + int r,n = ftell(f); + start_file(&s, f); + r = hdr_test(&s); + fseek(f,n,SEEK_SET); + return r; +} +#endif + +#define HDR_BUFLEN 1024 +static char *hdr_gettoken(stbi *z, char *buffer) +{ + int len=0; + //char *s = buffer, + char c = '\0'; + + c = get8(z); + + while (!at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == HDR_BUFLEN-1) { + // flush to end of line + while (!at_eof(z) && get8(z) != '\n') + ; + break; + } + c = get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + + +static float *hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + char buffer[HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + + + // Check identifier + if (strcmp(hdr_gettoken(s,buffer), "#?RADIANCE") != 0) + return epf("not HDR", "Corrupt HDR image"); + + // Parse header + while(1) { + token = hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return epf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return epf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return epf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = strtol(token, NULL, 10); + + *x = width; + *y = height; + + *comp = 3; + if (req_comp == 0) req_comp = 3; + + // Read data + hdr_data = (float *) malloc(height * width * req_comp * sizeof(float)); + + // Load image data + // image data is stored as some number of sca + if( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + getn(s, rgbe, 4); + hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = get8(s); + c2 = get8(s); + len = get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4] = { c1,c2,len, get8(s) }; + hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + free(scanline); + goto main_decode_loop; // yes, this is fucking insane; blame the fucking insane format + } + len <<= 8; + len |= get8(s); + if (len != width) { free(hdr_data); free(scanline); return epf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) scanline = (stbi_uc *) malloc(width * 4); + + for (k = 0; k < 4; ++k) { + i = 0; + while (i < width) { + count = get8(s); + if (count > 128) { + // Run + value = get8(s); + count -= 128; + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = get8(s); + } + } + } + for (i=0; i < width; ++i) + hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + free(scanline); + } + + return hdr_data; +} + +static stbi_uc *hdr_load_rgbe(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + char buffer[HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + stbi_uc *rgbe_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + + + // Check identifier + if (strcmp(hdr_gettoken(s,buffer), "#?RADIANCE") != 0) + return epuc("not HDR", "Corrupt HDR image"); + + // Parse header + while(1) { + token = hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return epuc("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return epuc("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return epuc("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = strtol(token, NULL, 10); + + *x = width; + *y = height; + + // RGBE _MUST_ come out as 4 components + *comp = 4; + req_comp = 4; + + // Read data + rgbe_data = (stbi_uc *) malloc(height * width * req_comp * sizeof(stbi_uc)); + // point to the beginning + scanline = rgbe_data; + + // Load image data + // image data is stored as some number of scan lines + if( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + main_decode_loop: + //getn(rgbe, 4); + getn(s,scanline, 4); + scanline += 4; + } + } + } else { + // Read RLE-encoded data + for (j = 0; j < height; ++j) { + c1 = get8(s); + c2 = get8(s); + len = get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + scanline[0] = c1; + scanline[1] = c2; + scanline[2] = len; + scanline[3] = get8(s); + scanline += 4; + i = 1; + j = 0; + goto main_decode_loop; // yes, this is insane; blame the insane format + } + len <<= 8; + len |= get8(s); + if (len != width) { free(rgbe_data); return epuc("invalid decoded scanline length", "corrupt HDR"); } + for (k = 0; k < 4; ++k) { + i = 0; + while (i < width) { + count = get8(s); + if (count > 128) { + // Run + value = get8(s); + count -= 128; + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = get8(s); + } + } + } + // move the scanline on + scanline += 4 * width; + } + } + + return rgbe_data; +} + +#ifndef STBI_NO_STDIO +float *stbi_hdr_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_file(&s,f); + return hdr_load(&s,x,y,comp,req_comp); +} + +stbi_uc *stbi_hdr_load_rgbe_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_file(&s,f); + return hdr_load_rgbe(&s,x,y,comp,req_comp); +} + +stbi_uc *stbi_hdr_load_rgbe (char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = fopen(filename, "rb"); + unsigned char *result; + if (!f) return epuc("can't fopen", "Unable to open file"); + result = stbi_hdr_load_rgbe_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} +#endif + +float *stbi_hdr_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_mem(&s,buffer, len); + return hdr_load(&s,x,y,comp,req_comp); +} + +stbi_uc *stbi_hdr_load_rgbe_memory(stbi_uc *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_mem(&s,buffer, len); + return hdr_load_rgbe(&s,x,y,comp,req_comp); +} + +#endif // STBI_NO_HDR + +/////////////////////// write image /////////////////////// + +#ifndef STBI_NO_WRITE + +static void write8(FILE *f, int x) { uint8 z = (uint8) x; fwrite(&z,1,1,f); } + +static void writefv(FILE *f, char *fmt, va_list v) +{ + while (*fmt) { + switch (*fmt++) { + case ' ': break; + case '1': { uint8 x = va_arg(v, int); write8(f,x); break; } + case '2': { int16 x = va_arg(v, int); write8(f,x); write8(f,x>>8); break; } + case '4': { int32 x = va_arg(v, int); write8(f,x); write8(f,x>>8); write8(f,x>>16); write8(f,x>>24); break; } + default: + assert(0); + va_end(v); + return; + } + } +} + +static void writef(FILE *f, char *fmt, ...) +{ + va_list v; + va_start(v, fmt); + writefv(f,fmt,v); + va_end(v); +} + +static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad) +{ + uint8 bg[3] = { 255, 0, 255}, px[3]; + uint32 zero = 0; + int i,j,k, j_end; + + if (vdir < 0) + j_end = -1, j = y-1; + else + j_end = y, j = 0; + + for (; j != j_end; j += vdir) { + for (i=0; i < x; ++i) { + uint8 *d = (uint8 *) data + (j*x+i)*comp; + if (write_alpha < 0) + fwrite(&d[comp-1], 1, 1, f); + switch (comp) { + case 1: + case 2: writef(f, "111", d[0],d[0],d[0]); + break; + case 4: + if (!write_alpha) { + for (k=0; k < 3; ++k) + px[k] = bg[k] + ((d[k] - bg[k]) * d[3])/255; + writef(f, "111", px[1-rgb_dir],px[1],px[1+rgb_dir]); + break; + } + /* FALLTHROUGH */ + case 3: + writef(f, "111", d[1-rgb_dir],d[1],d[1+rgb_dir]); + break; + } + if (write_alpha > 0) + fwrite(&d[comp-1], 1, 1, f); + } + fwrite(&zero,scanline_pad,1,f); + } +} + +static int outfile(char const *filename, int rgb_dir, int vdir, int x, int y, int comp, void *data, int alpha, int pad, char *fmt, ...) +{ + FILE *f = fopen(filename, "wb"); + if (f) { + va_list v; + va_start(v, fmt); + writefv(f, fmt, v); + va_end(v); + write_pixels(f,rgb_dir,vdir,x,y,comp,data,alpha,pad); + fclose(f); + } + return f != NULL; +} + +int stbi_write_bmp(char const *filename, int x, int y, int comp, void *data) +{ + int pad = (-x*3) & 3; + return outfile(filename,-1,-1,x,y,comp,data,0,pad, + "11 4 22 4" "4 44 22 444444", + 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header + 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header +} + +int stbi_write_tga(char const *filename, int x, int y, int comp, void *data) +{ + int has_alpha = !(comp & 1); + return outfile(filename, -1,-1, x, y, comp, data, has_alpha, 0, + "111 221 2222 11", 0,0,2, 0,0,0, 0,0,x,y, 24+8*has_alpha, 8*has_alpha); +} + +// any other image formats that do interleaved rgb data? +// PNG: requires adler32,crc32 -- significant amount of code +// PSD: no, channels output separately +// TIFF: no, stripwise-interleaved... i think + +#endif // STBI_NO_WRITE + +// add in my DDS loading support +#ifndef STBI_NO_DDS +#include "stbi_DDS_aug_c.h" +#endif diff --git a/cw_8/src/SOIL/stb_image_aug.h b/cw_8/src/SOIL/stb_image_aug.h new file mode 100644 index 0000000..e59f2eb --- /dev/null +++ b/cw_8/src/SOIL/stb_image_aug.h @@ -0,0 +1,354 @@ +/* stbi-1.16 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c + when you control the images you're loading + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline (no JPEG progressive, no oddball channel decimations) + PNG non-interlaced + BMP non-1bpp, non-RLE + TGA (not sure what subset, if a subset) + PSD (composited view only, no extra channels) + HDR (radiance rgbE format) + writes BMP,TGA (define STBI_NO_WRITE to remove code) + decoded from memory or through stdio FILE (define STBI_NO_STDIO to remove code) + supports installable dequantizing-IDCT, YCbCr-to-RGB conversion (define STBI_SIMD) + + TODO: + stbi_info_* + + history: + 1.16 major bugfix - convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug; header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi_bmp_load() and stbi_tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less + than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant +*/ + +#ifndef HEADER_STB_IMAGE_AUGMENTED +#define HEADER_STB_IMAGE_AUGMENTED + +//// begin header file //////////////////////////////////////////////////// +// +// Limitations: +// - no progressive/interlaced support (jpeg, png) +// - 8-bit samples only (jpeg, png) +// - not threadsafe +// - channel subsampling of at most 2 in each dimension (jpeg) +// - no delayed line count (jpeg) -- IJG doesn't support either +// +// Basic usage (see HDR discussion below): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// stbi_image_free(data) +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *comp -- outputs # of image components in image file +// int req_comp -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise. +// If req_comp is non-zero, *comp has the number of components that _would_ +// have been output otherwise. E.g. if you set req_comp to 4, you will always +// get RGBA output, but you can check *comp to easily see if it's opaque. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *comp will be unchanged. The function stbi_failure_reason() +// can be queried for an extremely brief, end-user unfriendly explanation +// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid +// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG and BMP images are automatically depalettized. +// +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image now supports loading HDR images in general, and currently +// the Radiance .HDR file format, although the support is provided +// generically. You can still load any file through the existing interface; +// if you attempt to load an HDR file, it will be automatically remapped to +// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); + +#ifndef STBI_NO_STDIO +#include +#endif + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for req_comp + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4, +}; + +typedef unsigned char stbi_uc; + +#ifdef __cplusplus +extern "C" { +#endif + +// WRITING API + +#if !defined(STBI_NO_WRITE) && !defined(STBI_NO_STDIO) +// write a BMP/TGA file given tightly packed 'comp' channels (no padding, nor bmp-stride-padding) +// (you must include the appropriate extension in the filename). +// returns TRUE on success, FALSE if couldn't open file, error writing file +extern int stbi_write_bmp (char const *filename, int x, int y, int comp, void *data); +extern int stbi_write_tga (char const *filename, int x, int y, int comp, void *data); +#endif + +// PRIMARY API - works on images of any type + +// load image by filename, open file, or memory buffer +#ifndef STBI_NO_STDIO +extern stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +extern int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); +#endif +extern stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +// for stbi_load_from_file, file pointer is left pointing immediately after image + +#ifndef STBI_NO_HDR +#ifndef STBI_NO_STDIO +extern float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); +extern float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +#endif +extern float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + +extern void stbi_hdr_to_ldr_gamma(float gamma); +extern void stbi_hdr_to_ldr_scale(float scale); + +extern void stbi_ldr_to_hdr_gamma(float gamma); +extern void stbi_ldr_to_hdr_scale(float scale); + +#endif // STBI_NO_HDR + +// get a VERY brief reason for failure +// NOT THREADSAFE +extern char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +extern void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +extern int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +extern int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +extern int stbi_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_is_hdr (char const *filename); +extern int stbi_is_hdr_from_file(FILE *f); +#endif + +// ZLIB client - used by PNG, available for other purposes + +extern char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +extern char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +extern int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +extern char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +extern int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +// TYPE-SPECIFIC ACCESS + +// is it a jpeg? +extern int stbi_jpeg_test_memory (stbi_uc const *buffer, int len); +extern stbi_uc *stbi_jpeg_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +extern int stbi_jpeg_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); + +#ifndef STBI_NO_STDIO +extern stbi_uc *stbi_jpeg_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern int stbi_jpeg_test_file (FILE *f); +extern stbi_uc *stbi_jpeg_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); + +extern int stbi_jpeg_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_jpeg_info_from_file (FILE *f, int *x, int *y, int *comp); +#endif + +// is it a png? +extern int stbi_png_test_memory (stbi_uc const *buffer, int len); +extern stbi_uc *stbi_png_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +extern int stbi_png_info_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp); + +#ifndef STBI_NO_STDIO +extern stbi_uc *stbi_png_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern int stbi_png_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_png_test_file (FILE *f); +extern stbi_uc *stbi_png_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +extern int stbi_png_info_from_file (FILE *f, int *x, int *y, int *comp); +#endif + +// is it a bmp? +extern int stbi_bmp_test_memory (stbi_uc const *buffer, int len); + +extern stbi_uc *stbi_bmp_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_bmp_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +#ifndef STBI_NO_STDIO +extern int stbi_bmp_test_file (FILE *f); +extern stbi_uc *stbi_bmp_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +#endif + +// is it a tga? +extern int stbi_tga_test_memory (stbi_uc const *buffer, int len); + +extern stbi_uc *stbi_tga_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_tga_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +#ifndef STBI_NO_STDIO +extern int stbi_tga_test_file (FILE *f); +extern stbi_uc *stbi_tga_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +#endif + +// is it a psd? +extern int stbi_psd_test_memory (stbi_uc const *buffer, int len); + +extern stbi_uc *stbi_psd_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_psd_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +#ifndef STBI_NO_STDIO +extern int stbi_psd_test_file (FILE *f); +extern stbi_uc *stbi_psd_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +#endif + +// is it an hdr? +extern int stbi_hdr_test_memory (stbi_uc const *buffer, int len); + +extern float * stbi_hdr_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern float * stbi_hdr_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_hdr_load_rgbe (char const *filename, int *x, int *y, int *comp, int req_comp); +extern float * stbi_hdr_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +#ifndef STBI_NO_STDIO +extern int stbi_hdr_test_file (FILE *f); +extern float * stbi_hdr_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_hdr_load_rgbe_file (FILE *f, int *x, int *y, int *comp, int req_comp); +#endif + +// define new loaders +typedef struct +{ + int (*test_memory)(stbi_uc const *buffer, int len); + stbi_uc * (*load_from_memory)(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + #ifndef STBI_NO_STDIO + int (*test_file)(FILE *f); + stbi_uc * (*load_from_file)(FILE *f, int *x, int *y, int *comp, int req_comp); + #endif +} stbi_loader; + +// register a loader by filling out the above structure (you must defined ALL functions) +// returns 1 if added or already added, 0 if not added (too many loaders) +// NOT THREADSAFE +extern int stbi_register_loader(stbi_loader *loader); + +// define faster low-level operations (typically SIMD support) +#if STBI_SIMD +typedef void (*stbi_idct_8x8)(uint8 *out, int out_stride, short data[64], unsigned short *dequantize); +// compute an integer IDCT on "input" +// input[x] = data[x] * dequantize[x] +// write results to 'out': 64 samples, each run of 8 spaced by 'out_stride' +// CLAMP results to 0..255 +typedef void (*stbi_YCbCr_to_RGB_run)(uint8 *output, uint8 const *y, uint8 const *cb, uint8 const *cr, int count, int step); +// compute a conversion from YCbCr to RGB +// 'count' pixels +// write pixels to 'output'; each pixel is 'step' bytes (either 3 or 4; if 4, write '255' as 4th), order R,G,B +// y: Y input channel +// cb: Cb input channel; scale/biased to be 0..255 +// cr: Cr input channel; scale/biased to be 0..255 + +extern void stbi_install_idct(stbi_idct_8x8 func); +extern void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func); +#endif // STBI_SIMD + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H diff --git a/cw_8/src/SOIL/stbi_DDS_aug.h b/cw_8/src/SOIL/stbi_DDS_aug.h new file mode 100644 index 0000000..c7da9f7 --- /dev/null +++ b/cw_8/src/SOIL/stbi_DDS_aug.h @@ -0,0 +1,21 @@ +/* + adding DDS loading support to stbi +*/ + +#ifndef HEADER_STB_IMAGE_DDS_AUGMENTATION +#define HEADER_STB_IMAGE_DDS_AUGMENTATION + +// is it a DDS file? +extern int stbi_dds_test_memory (stbi_uc const *buffer, int len); + +extern stbi_uc *stbi_dds_load (char *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_dds_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +#ifndef STBI_NO_STDIO +extern int stbi_dds_test_file (FILE *f); +extern stbi_uc *stbi_dds_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // HEADER_STB_IMAGE_DDS_AUGMENTATION diff --git a/cw_8/src/SOIL/stbi_DDS_aug_c.h b/cw_8/src/SOIL/stbi_DDS_aug_c.h new file mode 100644 index 0000000..f49407a --- /dev/null +++ b/cw_8/src/SOIL/stbi_DDS_aug_c.h @@ -0,0 +1,511 @@ + +/// DDS file support, does decoding, _not_ direct uploading +/// (use SOIL for that ;-) + +/// A bunch of DirectDraw Surface structures and flags +typedef struct { + unsigned int dwMagic; + unsigned int dwSize; + unsigned int dwFlags; + unsigned int dwHeight; + unsigned int dwWidth; + unsigned int dwPitchOrLinearSize; + unsigned int dwDepth; + unsigned int dwMipMapCount; + unsigned int dwReserved1[ 11 ]; + + // DDPIXELFORMAT + struct { + unsigned int dwSize; + unsigned int dwFlags; + unsigned int dwFourCC; + unsigned int dwRGBBitCount; + unsigned int dwRBitMask; + unsigned int dwGBitMask; + unsigned int dwBBitMask; + unsigned int dwAlphaBitMask; + } sPixelFormat; + + // DDCAPS2 + struct { + unsigned int dwCaps1; + unsigned int dwCaps2; + unsigned int dwDDSX; + unsigned int dwReserved; + } sCaps; + unsigned int dwReserved2; +} DDS_header ; + +// the following constants were copied directly off the MSDN website + +// The dwFlags member of the original DDSURFACEDESC2 structure +// can be set to one or more of the following values. +#define DDSD_CAPS 0x00000001 +#define DDSD_HEIGHT 0x00000002 +#define DDSD_WIDTH 0x00000004 +#define DDSD_PITCH 0x00000008 +#define DDSD_PIXELFORMAT 0x00001000 +#define DDSD_MIPMAPCOUNT 0x00020000 +#define DDSD_LINEARSIZE 0x00080000 +#define DDSD_DEPTH 0x00800000 + +// DirectDraw Pixel Format +#define DDPF_ALPHAPIXELS 0x00000001 +#define DDPF_FOURCC 0x00000004 +#define DDPF_RGB 0x00000040 + +// The dwCaps1 member of the DDSCAPS2 structure can be +// set to one or more of the following values. +#define DDSCAPS_COMPLEX 0x00000008 +#define DDSCAPS_TEXTURE 0x00001000 +#define DDSCAPS_MIPMAP 0x00400000 + +// The dwCaps2 member of the DDSCAPS2 structure can be +// set to one or more of the following values. +#define DDSCAPS2_CUBEMAP 0x00000200 +#define DDSCAPS2_CUBEMAP_POSITIVEX 0x00000400 +#define DDSCAPS2_CUBEMAP_NEGATIVEX 0x00000800 +#define DDSCAPS2_CUBEMAP_POSITIVEY 0x00001000 +#define DDSCAPS2_CUBEMAP_NEGATIVEY 0x00002000 +#define DDSCAPS2_CUBEMAP_POSITIVEZ 0x00004000 +#define DDSCAPS2_CUBEMAP_NEGATIVEZ 0x00008000 +#define DDSCAPS2_VOLUME 0x00200000 + +static int dds_test(stbi *s) +{ + // check the magic number + if (get8(s) != 'D') return 0; + if (get8(s) != 'D') return 0; + if (get8(s) != 'S') return 0; + if (get8(s) != ' ') return 0; + // check header size + if (get32le(s) != 124) return 0; + return 1; +} +#ifndef STBI_NO_STDIO +int stbi_dds_test_file (FILE *f) +{ + stbi s; + int r,n = ftell(f); + start_file(&s,f); + r = dds_test(&s); + fseek(f,n,SEEK_SET); + return r; +} +#endif + +int stbi_dds_test_memory (stbi_uc const *buffer, int len) +{ + stbi s; + start_mem(&s,buffer, len); + return dds_test(&s); +} + +// helper functions +int stbi_convert_bit_range( int c, int from_bits, int to_bits ) +{ + int b = (1 << (from_bits - 1)) + c * ((1 << to_bits) - 1); + return (b + (b >> from_bits)) >> from_bits; +} +void stbi_rgb_888_from_565( unsigned int c, int *r, int *g, int *b ) +{ + *r = stbi_convert_bit_range( (c >> 11) & 31, 5, 8 ); + *g = stbi_convert_bit_range( (c >> 05) & 63, 6, 8 ); + *b = stbi_convert_bit_range( (c >> 00) & 31, 5, 8 ); +} +void stbi_decode_DXT1_block( + unsigned char uncompressed[16*4], + unsigned char compressed[8] ) +{ + int next_bit = 4*8; + int i, r, g, b; + int c0, c1; + unsigned char decode_colors[4*4]; + // find the 2 primary colors + c0 = compressed[0] + (compressed[1] << 8); + c1 = compressed[2] + (compressed[3] << 8); + stbi_rgb_888_from_565( c0, &r, &g, &b ); + decode_colors[0] = r; + decode_colors[1] = g; + decode_colors[2] = b; + decode_colors[3] = 255; + stbi_rgb_888_from_565( c1, &r, &g, &b ); + decode_colors[4] = r; + decode_colors[5] = g; + decode_colors[6] = b; + decode_colors[7] = 255; + if( c0 > c1 ) + { + // no alpha, 2 interpolated colors + decode_colors[8] = (2*decode_colors[0] + decode_colors[4]) / 3; + decode_colors[9] = (2*decode_colors[1] + decode_colors[5]) / 3; + decode_colors[10] = (2*decode_colors[2] + decode_colors[6]) / 3; + decode_colors[11] = 255; + decode_colors[12] = (decode_colors[0] + 2*decode_colors[4]) / 3; + decode_colors[13] = (decode_colors[1] + 2*decode_colors[5]) / 3; + decode_colors[14] = (decode_colors[2] + 2*decode_colors[6]) / 3; + decode_colors[15] = 255; + } else + { + // 1 interpolated color, alpha + decode_colors[8] = (decode_colors[0] + decode_colors[4]) / 2; + decode_colors[9] = (decode_colors[1] + decode_colors[5]) / 2; + decode_colors[10] = (decode_colors[2] + decode_colors[6]) / 2; + decode_colors[11] = 255; + decode_colors[12] = 0; + decode_colors[13] = 0; + decode_colors[14] = 0; + decode_colors[15] = 0; + } + // decode the block + for( i = 0; i < 16*4; i += 4 ) + { + int idx = ((compressed[next_bit>>3] >> (next_bit & 7)) & 3) * 4; + next_bit += 2; + uncompressed[i+0] = decode_colors[idx+0]; + uncompressed[i+1] = decode_colors[idx+1]; + uncompressed[i+2] = decode_colors[idx+2]; + uncompressed[i+3] = decode_colors[idx+3]; + } + // done +} +void stbi_decode_DXT23_alpha_block( + unsigned char uncompressed[16*4], + unsigned char compressed[8] ) +{ + int i, next_bit = 0; + // each alpha value gets 4 bits + for( i = 3; i < 16*4; i += 4 ) + { + uncompressed[i] = stbi_convert_bit_range( + (compressed[next_bit>>3] >> (next_bit&7)) & 15, + 4, 8 ); + next_bit += 4; + } +} +void stbi_decode_DXT45_alpha_block( + unsigned char uncompressed[16*4], + unsigned char compressed[8] ) +{ + int i, next_bit = 8*2; + unsigned char decode_alpha[8]; + // each alpha value gets 3 bits, and the 1st 2 bytes are the range + decode_alpha[0] = compressed[0]; + decode_alpha[1] = compressed[1]; + if( decode_alpha[0] > decode_alpha[1] ) + { + // 6 step intermediate + decode_alpha[2] = (6*decode_alpha[0] + 1*decode_alpha[1]) / 7; + decode_alpha[3] = (5*decode_alpha[0] + 2*decode_alpha[1]) / 7; + decode_alpha[4] = (4*decode_alpha[0] + 3*decode_alpha[1]) / 7; + decode_alpha[5] = (3*decode_alpha[0] + 4*decode_alpha[1]) / 7; + decode_alpha[6] = (2*decode_alpha[0] + 5*decode_alpha[1]) / 7; + decode_alpha[7] = (1*decode_alpha[0] + 6*decode_alpha[1]) / 7; + } else + { + // 4 step intermediate, pluss full and none + decode_alpha[2] = (4*decode_alpha[0] + 1*decode_alpha[1]) / 5; + decode_alpha[3] = (3*decode_alpha[0] + 2*decode_alpha[1]) / 5; + decode_alpha[4] = (2*decode_alpha[0] + 3*decode_alpha[1]) / 5; + decode_alpha[5] = (1*decode_alpha[0] + 4*decode_alpha[1]) / 5; + decode_alpha[6] = 0; + decode_alpha[7] = 255; + } + for( i = 3; i < 16*4; i += 4 ) + { + int idx = 0, bit; + bit = (compressed[next_bit>>3] >> (next_bit&7)) & 1; + idx += bit << 0; + ++next_bit; + bit = (compressed[next_bit>>3] >> (next_bit&7)) & 1; + idx += bit << 1; + ++next_bit; + bit = (compressed[next_bit>>3] >> (next_bit&7)) & 1; + idx += bit << 2; + ++next_bit; + uncompressed[i] = decode_alpha[idx & 7]; + } + // done +} +void stbi_decode_DXT_color_block( + unsigned char uncompressed[16*4], + unsigned char compressed[8] ) +{ + int next_bit = 4*8; + int i, r, g, b; + int c0, c1; + unsigned char decode_colors[4*3]; + // find the 2 primary colors + c0 = compressed[0] + (compressed[1] << 8); + c1 = compressed[2] + (compressed[3] << 8); + stbi_rgb_888_from_565( c0, &r, &g, &b ); + decode_colors[0] = r; + decode_colors[1] = g; + decode_colors[2] = b; + stbi_rgb_888_from_565( c1, &r, &g, &b ); + decode_colors[3] = r; + decode_colors[4] = g; + decode_colors[5] = b; + // Like DXT1, but no choicees: + // no alpha, 2 interpolated colors + decode_colors[6] = (2*decode_colors[0] + decode_colors[3]) / 3; + decode_colors[7] = (2*decode_colors[1] + decode_colors[4]) / 3; + decode_colors[8] = (2*decode_colors[2] + decode_colors[5]) / 3; + decode_colors[9] = (decode_colors[0] + 2*decode_colors[3]) / 3; + decode_colors[10] = (decode_colors[1] + 2*decode_colors[4]) / 3; + decode_colors[11] = (decode_colors[2] + 2*decode_colors[5]) / 3; + // decode the block + for( i = 0; i < 16*4; i += 4 ) + { + int idx = ((compressed[next_bit>>3] >> (next_bit & 7)) & 3) * 3; + next_bit += 2; + uncompressed[i+0] = decode_colors[idx+0]; + uncompressed[i+1] = decode_colors[idx+1]; + uncompressed[i+2] = decode_colors[idx+2]; + } + // done +} +static stbi_uc *dds_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + // all variables go up front + stbi_uc *dds_data = NULL; + stbi_uc block[16*4]; + stbi_uc compressed[8]; + int flags, DXT_family; + int has_alpha, has_mipmap; + int is_compressed, cubemap_faces; + int block_pitch, num_blocks; + DDS_header header; + int i, sz, cf; + // load the header + if( sizeof( DDS_header ) != 128 ) + { + return NULL; + } + getn( s, (stbi_uc*)(&header), 128 ); + // and do some checking + if( header.dwMagic != (('D' << 0) | ('D' << 8) | ('S' << 16) | (' ' << 24)) ) return NULL; + if( header.dwSize != 124 ) return NULL; + flags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT; + if( (header.dwFlags & flags) != flags ) return NULL; + /* According to the MSDN spec, the dwFlags should contain + DDSD_LINEARSIZE if it's compressed, or DDSD_PITCH if + uncompressed. Some DDS writers do not conform to the + spec, so I need to make my reader more tolerant */ + if( header.sPixelFormat.dwSize != 32 ) return NULL; + flags = DDPF_FOURCC | DDPF_RGB; + if( (header.sPixelFormat.dwFlags & flags) == 0 ) return NULL; + if( (header.sCaps.dwCaps1 & DDSCAPS_TEXTURE) == 0 ) return NULL; + // get the image data + s->img_x = header.dwWidth; + s->img_y = header.dwHeight; + s->img_n = 4; + is_compressed = (header.sPixelFormat.dwFlags & DDPF_FOURCC) / DDPF_FOURCC; + has_alpha = (header.sPixelFormat.dwFlags & DDPF_ALPHAPIXELS) / DDPF_ALPHAPIXELS; + has_mipmap = (header.sCaps.dwCaps1 & DDSCAPS_MIPMAP) && (header.dwMipMapCount > 1); + cubemap_faces = (header.sCaps.dwCaps2 & DDSCAPS2_CUBEMAP) / DDSCAPS2_CUBEMAP; + /* I need cubemaps to have square faces */ + cubemap_faces &= (s->img_x == s->img_y); + cubemap_faces *= 5; + cubemap_faces += 1; + block_pitch = (s->img_x+3) >> 2; + num_blocks = block_pitch * ((s->img_y+3) >> 2); + /* let the user know what's going on */ + *x = s->img_x; + *y = s->img_y; + *comp = s->img_n; + /* is this uncompressed? */ + if( is_compressed ) + { + /* compressed */ + // note: header.sPixelFormat.dwFourCC is something like (('D'<<0)|('X'<<8)|('T'<<16)|('1'<<24)) + DXT_family = 1 + (header.sPixelFormat.dwFourCC >> 24) - '1'; + if( (DXT_family < 1) || (DXT_family > 5) ) return NULL; + /* check the expected size...oops, nevermind... + those non-compliant writers leave + dwPitchOrLinearSize == 0 */ + // passed all the tests, get the RAM for decoding + sz = (s->img_x)*(s->img_y)*4*cubemap_faces; + dds_data = (unsigned char*)malloc( sz ); + /* do this once for each face */ + for( cf = 0; cf < cubemap_faces; ++ cf ) + { + // now read and decode all the blocks + for( i = 0; i < num_blocks; ++i ) + { + // where are we? + int bx, by, bw=4, bh=4; + int ref_x = 4 * (i % block_pitch); + int ref_y = 4 * (i / block_pitch); + // get the next block's worth of compressed data, and decompress it + if( DXT_family == 1 ) + { + // DXT1 + getn( s, compressed, 8 ); + stbi_decode_DXT1_block( block, compressed ); + } else if( DXT_family < 4 ) + { + // DXT2/3 + getn( s, compressed, 8 ); + stbi_decode_DXT23_alpha_block ( block, compressed ); + getn( s, compressed, 8 ); + stbi_decode_DXT_color_block ( block, compressed ); + } else + { + // DXT4/5 + getn( s, compressed, 8 ); + stbi_decode_DXT45_alpha_block ( block, compressed ); + getn( s, compressed, 8 ); + stbi_decode_DXT_color_block ( block, compressed ); + } + // is this a partial block? + if( ref_x + 4 > s->img_x ) + { + bw = s->img_x - ref_x; + } + if( ref_y + 4 > s->img_y ) + { + bh = s->img_y - ref_y; + } + // now drop our decompressed data into the buffer + for( by = 0; by < bh; ++by ) + { + int idx = 4*((ref_y+by+cf*s->img_x)*s->img_x + ref_x); + for( bx = 0; bx < bw*4; ++bx ) + { + + dds_data[idx+bx] = block[by*16+bx]; + } + } + } + /* done reading and decoding the main image... + skip MIPmaps if present */ + if( has_mipmap ) + { + int block_size = 16; + if( DXT_family == 1 ) + { + block_size = 8; + } + for( i = 1; i < header.dwMipMapCount; ++i ) + { + int mx = s->img_x >> (i + 2); + int my = s->img_y >> (i + 2); + if( mx < 1 ) + { + mx = 1; + } + if( my < 1 ) + { + my = 1; + } + skip( s, mx*my*block_size ); + } + } + }/* per cubemap face */ + } else + { + /* uncompressed */ + DXT_family = 0; + s->img_n = 3; + if( has_alpha ) + { + s->img_n = 4; + } + *comp = s->img_n; + sz = s->img_x*s->img_y*s->img_n*cubemap_faces; + dds_data = (unsigned char*)malloc( sz ); + /* do this once for each face */ + for( cf = 0; cf < cubemap_faces; ++ cf ) + { + /* read the main image for this face */ + getn( s, &dds_data[cf*s->img_x*s->img_y*s->img_n], s->img_x*s->img_y*s->img_n ); + /* done reading and decoding the main image... + skip MIPmaps if present */ + if( has_mipmap ) + { + for( i = 1; i < header.dwMipMapCount; ++i ) + { + int mx = s->img_x >> i; + int my = s->img_y >> i; + if( mx < 1 ) + { + mx = 1; + } + if( my < 1 ) + { + my = 1; + } + skip( s, mx*my*s->img_n ); + } + } + } + /* data was BGR, I need it RGB */ + for( i = 0; i < sz; i += s->img_n ) + { + unsigned char temp = dds_data[i]; + dds_data[i] = dds_data[i+2]; + dds_data[i+2] = temp; + } + } + /* finished decompressing into RGBA, + adjust the y size if we have a cubemap + note: sz is already up to date */ + s->img_y *= cubemap_faces; + *y = s->img_y; + // did the user want something else, or + // see if all the alpha values are 255 (i.e. no transparency) + has_alpha = 0; + if( s->img_n == 4) + { + for( i = 3; (i < sz) && (has_alpha == 0); i += 4 ) + { + has_alpha |= (dds_data[i] < 255); + } + } + if( (req_comp <= 4) && (req_comp >= 1) ) + { + // user has some requirements, meet them + if( req_comp != s->img_n ) + { + dds_data = convert_format( dds_data, s->img_n, req_comp, s->img_x, s->img_y ); + *comp = s->img_n; + } + } else + { + // user had no requirements, only drop to RGB is no alpha + if( (has_alpha == 0) && (s->img_n == 4) ) + { + dds_data = convert_format( dds_data, 4, 3, s->img_x, s->img_y ); + *comp = 3; + } + } + // OK, done + return dds_data; +} + +#ifndef STBI_NO_STDIO +stbi_uc *stbi_dds_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_file(&s,f); + return dds_load(&s,x,y,comp,req_comp); +} + +stbi_uc *stbi_dds_load (char *filename, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *data; + FILE *f = fopen(filename, "rb"); + if (!f) return NULL; + data = stbi_dds_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return data; +} +#endif + +stbi_uc *stbi_dds_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_mem(&s,buffer, len); + return dds_load(&s,x,y,comp,req_comp); +} diff --git a/cw_8/src/Shader_Loader.cpp b/cw_8/src/Shader_Loader.cpp new file mode 100644 index 0000000..6e118e9 --- /dev/null +++ b/cw_8/src/Shader_Loader.cpp @@ -0,0 +1,102 @@ +#include "Shader_Loader.h" +#include +#include +#include + +using namespace Core; + +Shader_Loader::Shader_Loader(void){} +Shader_Loader::~Shader_Loader(void){} + +std::string Shader_Loader::ReadShader(char *filename) +{ + + std::string shaderCode; + std::ifstream file(filename, std::ios::in); + + if (!file.good()) + { + std::cout << "Can't read file " << filename << std::endl; + std::terminate(); + } + + file.seekg(0, std::ios::end); + shaderCode.resize((unsigned int)file.tellg()); + file.seekg(0, std::ios::beg); + file.read(&shaderCode[0], shaderCode.size()); + file.close(); + return shaderCode; +} + +GLuint Shader_Loader::CreateShader(GLenum shaderType, std::string + source, char* shaderName) +{ + + int compile_result = 0; + + GLuint shader = glCreateShader(shaderType); + const char *shader_code_ptr = source.c_str(); + const int shader_code_size = source.size(); + + glShaderSource(shader, 1, &shader_code_ptr, &shader_code_size); + glCompileShader(shader); + glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_result); + + //sprawdz bledy + if (compile_result == GL_FALSE) + { + + int info_log_length = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_log_length); + std::vector shader_log(info_log_length); + glGetShaderInfoLog(shader, info_log_length, NULL, &shader_log[0]); + std::cout << "ERROR compiling shader: " << shaderName << std::endl << &shader_log[0] << std::endl; + return 0; + } + + return shader; +} + +GLuint Shader_Loader::CreateProgram(char* vertexShaderFilename, + char* fragmentShaderFilename) +{ + + //wczytaj shadery + std::string vertex_shader_code = ReadShader(vertexShaderFilename); + std::string fragment_shader_code = ReadShader(fragmentShaderFilename); + + GLuint vertex_shader = CreateShader(GL_VERTEX_SHADER, vertex_shader_code, "vertex shader"); + GLuint fragment_shader = CreateShader(GL_FRAGMENT_SHADER, fragment_shader_code, "fragment shader"); + + int link_result = 0; + //stworz shader + GLuint program = glCreateProgram(); + glAttachShader(program, vertex_shader); + glAttachShader(program, fragment_shader); + + glLinkProgram(program); + glGetProgramiv(program, GL_LINK_STATUS, &link_result); + //sprawdz bledy w linkerze + if (link_result == GL_FALSE) + { + + int info_log_length = 0; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &info_log_length); + std::vector program_log(info_log_length); + glGetProgramInfoLog(program, info_log_length, NULL, &program_log[0]); + std::cout << "Shader Loader : LINK ERROR" << std::endl << &program_log[0] << std::endl; + return 0; + } + + glDetachShader(program, vertex_shader); + glDetachShader(program, fragment_shader); + glDeleteShader(vertex_shader); + glDeleteShader(fragment_shader); + + return program; +} + +void Shader_Loader::DeleteProgram( GLuint program ) +{ + glDeleteProgram(program); +} diff --git a/cw_8/src/Shader_Loader.h b/cw_8/src/Shader_Loader.h new file mode 100644 index 0000000..1fb9757 --- /dev/null +++ b/cw_8/src/Shader_Loader.h @@ -0,0 +1,29 @@ +#pragma once + +#include "glew.h" +#include "freeglut.h" +#include + +namespace Core +{ + + class Shader_Loader + { + private: + + std::string ReadShader(char *filename); + GLuint CreateShader(GLenum shaderType, + std::string source, + char* shaderName); + + public: + + Shader_Loader(void); + ~Shader_Loader(void); + GLuint CreateProgram(char* VertexShaderFilename, + char* FragmentShaderFilename); + + void DeleteProgram(GLuint program); + + }; +} \ No newline at end of file diff --git a/cw_8/src/Texture.cpp b/cw_8/src/Texture.cpp new file mode 100644 index 0000000..2548b13 --- /dev/null +++ b/cw_8/src/Texture.cpp @@ -0,0 +1,37 @@ +#include "Texture.h" + +#include +#include +#include +#include +#include "SOIL/SOIL.h" + +typedef unsigned char byte; + +GLuint Core::LoadTexture( const char * filepath ) +{ + GLuint id; + glGenTextures(1, &id); + glBindTexture(GL_TEXTURE_2D, id); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + int w, h; + unsigned char* image = SOIL_load_image(filepath, &w, &h, 0, SOIL_LOAD_RGBA); + + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image); + glGenerateMipmap(GL_TEXTURE_2D); + SOIL_free_image_data(image); + + return id; +} + +void Core::SetActiveTexture(GLuint textureID, const char * shaderVariableName, GLuint programID, int textureUnit) +{ + glUniform1i(glGetUniformLocation(programID, shaderVariableName), textureUnit); + glActiveTexture(GL_TEXTURE0 + textureUnit); + glBindTexture(GL_TEXTURE_2D, textureID); +} diff --git a/cw_8/src/Texture.h b/cw_8/src/Texture.h new file mode 100644 index 0000000..910228e --- /dev/null +++ b/cw_8/src/Texture.h @@ -0,0 +1,15 @@ +#pragma once + +#include "glew.h" +#include "freeglut.h" + +namespace Core +{ + GLuint LoadTexture(const char * filepath); + + // textureID - identyfikator tekstury otrzymany z funkcji LoadTexture + // shaderVariableName - nazwa zmiennej typu 'sampler2D' w shaderze, z ktora ma zostac powiazana tekstura + // programID - identyfikator aktualnego programu karty graficznej + // textureUnit - indeks jednostki teksturujacej - liczba od 0 do 7. Jezeli uzywa sie wielu tekstur w jednym shaderze, to kazda z nich nalezy powiazac z inna jednostka. + void SetActiveTexture(GLuint textureID, const char * shaderVariableName, GLuint programID, int textureUnit); +} \ No newline at end of file diff --git a/cw_8/src/ex_8_1.hpp b/cw_8/src/ex_8_1.hpp new file mode 100644 index 0000000..2589088 --- /dev/null +++ b/cw_8/src/ex_8_1.hpp @@ -0,0 +1,243 @@ +#include "glew.h" +#include +#include "glm.hpp" +#include "ext.hpp" +#include +#include + +#include "Shader_Loader.h" +#include "Render_Utils.h" +//#include "Texture.h" + +#include "Box.cpp" +#include +#include +#include +#include + + +namespace texture { + GLuint earth; + GLuint clouds; + GLuint moon; + GLuint ship; + + GLuint grid; + + GLuint earthNormal; + GLuint asteroidNormal; + GLuint shipNormal; +} + + +GLuint program; +GLuint programSun; +GLuint programTex; +Core::Shader_Loader shaderLoader; + +Core::RenderContext shipContext; +Core::RenderContext sphereContext; + +glm::vec3 cameraPos = glm::vec3(-4.f, 0, 0); +glm::vec3 cameraDir = glm::vec3(1.f, 0.f, 0.f); + + +glm::vec3 spaceshipPos = glm::vec3(0.479490f, 1.000000f, -2.124680f); +glm::vec3 spaceshipDir = glm::vec3(-0.354510f, 0.000000f, 0.935054f); + +GLuint VAO,VBO; + +float aspectRatio = 1.f; + +float exposition = 1.f; + +glm::vec3 lightPos = glm::vec3(-8, 4, 2); +glm::vec3 lightColor = glm::vec3(0.9, 0.7, 0.8)*100; + +glm::vec3 spotlightPos = glm::vec3(0, 0, 0); +glm::vec3 spotlightConeDir = glm::vec3(0, 0, 0); +glm::vec3 spotlightColor = glm::vec3(0.5, 0.9, 0.8)*10; +float spotlightPhi = 3.14 / 3; + + +glm::mat4 createCameraMatrix() +{ + glm::vec3 cameraSide = glm::normalize(glm::cross(cameraDir,glm::vec3(0.f,1.f,0.f))); + glm::vec3 cameraUp = glm::normalize(glm::cross(cameraSide,cameraDir)); + glm::mat4 cameraRotrationMatrix = glm::mat4({ + cameraSide.x,cameraSide.y,cameraSide.z,0, + cameraUp.x,cameraUp.y,cameraUp.z ,0, + -cameraDir.x,-cameraDir.y,-cameraDir.z,0, + 0.,0.,0.,1., + }); + cameraRotrationMatrix = glm::transpose(cameraRotrationMatrix); + glm::mat4 cameraMatrix = cameraRotrationMatrix * glm::translate(-cameraPos); + + return cameraMatrix; +} + +glm::mat4 createPerspectiveMatrix() +{ + + glm::mat4 perspectiveMatrix; + float n = 0.05; + float f = 20.; + float a1 = glm::min(aspectRatio, 1.f); + float a2 = glm::min(1 / aspectRatio, 1.f); + perspectiveMatrix = glm::mat4({ + 1,0.,0.,0., + 0.,aspectRatio,0.,0., + 0.,0.,(f+n) / (n - f),2*f * n / (n - f), + 0.,0.,-1.,0., + }); + + + perspectiveMatrix=glm::transpose(perspectiveMatrix); + + return perspectiveMatrix; +} + +void drawObjectColor(Core::RenderContext& context, glm::mat4 modelMatrix, glm::vec3 color) { + + glm::mat4 viewProjectionMatrix = createPerspectiveMatrix() * createCameraMatrix(); + glm::mat4 transformation = viewProjectionMatrix * modelMatrix; + glUniformMatrix4fv(glGetUniformLocation(program, "transformation"), 1, GL_FALSE, (float*)&transformation); + glUniformMatrix4fv(glGetUniformLocation(program, "modelMatrix"), 1, GL_FALSE, (float*)&modelMatrix); + + glUniform1f(glGetUniformLocation(program, "exposition"), exposition); + + glUniform3f(glGetUniformLocation(program, "color"), color.x, color.y, color.z); + + glUniform3f(glGetUniformLocation(program, "cameraPos"), cameraPos.x, cameraPos.y, cameraPos.z); + + glUniform3f(glGetUniformLocation(program, "lightPos"), lightPos.x, lightPos.y, lightPos.z); + glUniform3f(glGetUniformLocation(program, "lightColor"), lightColor.x, lightColor.y, lightColor.z); + + glUniform3f(glGetUniformLocation(program, "spotlightConeDir"), spotlightConeDir.x, spotlightConeDir.y, spotlightConeDir.z); + glUniform3f(glGetUniformLocation(program, "spotlightPos"), spotlightPos.x, spotlightPos.y, spotlightPos.z); + glUniform3f(glGetUniformLocation(program, "spotlightColor"), spotlightColor.x, spotlightColor.y, spotlightColor.z); + glUniform1f(glGetUniformLocation(program, "spotlightPhi"), spotlightPhi); + Core::DrawContext(context); + +} +void renderScene(GLFWwindow* window) +{ + glClearColor(0.0f, 0.3f, 0.3f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glm::mat4 transformation; + float time = glfwGetTime(); + + + glUseProgram(program); + drawObjectColor(sphereContext, glm::mat4(), glm::vec3(0.95, 0.1, 0.1)); + + glm::vec3 spaceshipSide = glm::normalize(glm::cross(spaceshipDir, glm::vec3(0.f, 1.f, 0.f))); + glm::vec3 spaceshipUp = glm::normalize(glm::cross(spaceshipSide, spaceshipDir)); + glm::mat4 specshipCameraRotrationMatrix = glm::mat4({ + spaceshipSide.x,spaceshipSide.y,spaceshipSide.z,0, + spaceshipUp.x,spaceshipUp.y,spaceshipUp.z ,0, + -spaceshipDir.x,-spaceshipDir.y,-spaceshipDir.z,0, + 0.,0.,0.,1., + }); + + + //drawObjectColor(shipContext, + // glm::translate(cameraPos + 1.5 * cameraDir + cameraUp * -0.5f) * inveseCameraRotrationMatrix * glm::eulerAngleY(glm::pi()), + // glm::vec3(0.3, 0.3, 0.5) + // ); + drawObjectColor(shipContext, + glm::translate(spaceshipPos) * specshipCameraRotrationMatrix * glm::eulerAngleY(glm::pi()) * glm::scale(glm::vec3(0.1)), + glm::vec3(0.3, 0.3, 0.5) + ); + + spotlightPos = spaceshipPos + 0.5 * spaceshipDir; + spotlightConeDir = spaceshipDir; + + glUseProgram(0); + glfwSwapBuffers(window); +} +void framebuffer_size_callback(GLFWwindow* window, int width, int height) +{ + aspectRatio = width / float(height); + glViewport(0, 0, width, height); +} +void loadModelToContext(std::string path, Core::RenderContext& context) +{ + Assimp::Importer import; + const aiScene* scene = import.ReadFile(path, aiProcess_Triangulate | aiProcess_CalcTangentSpace); + + if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) + { + std::cout << "ERROR::ASSIMP::" << import.GetErrorString() << std::endl; + return; + } + context.initFromAssimpMesh(scene->mMeshes[0]); +} + +void init(GLFWwindow* window) +{ + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + + glEnable(GL_DEPTH_TEST); + program = shaderLoader.CreateProgram("shaders/shader_8_1.vert", "shaders/shader_8_1.frag"); + programSun = shaderLoader.CreateProgram("shaders/shader_8_sun.vert", "shaders/shader_8_sun.frag"); + + loadModelToContext("./models/sphere.obj", sphereContext); + loadModelToContext("./models/spaceship.obj", shipContext); + +} + +void shutdown(GLFWwindow* window) +{ + shaderLoader.DeleteProgram(program); +} + +//obsluga wejscia +void processInput(GLFWwindow* window) +{ + glm::vec3 spaceshipSide = glm::normalize(glm::cross(spaceshipDir, glm::vec3(0.f, 1.f, 0.f))); + glm::vec3 spaceshipUp = glm::vec3(0.f, 1.f, 0.f); + float angleSpeed = 0.05f; + float moveSpeed = 0.05f; + if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) { + glfwSetWindowShouldClose(window, true); + } + if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) + spaceshipPos += spaceshipDir * moveSpeed; + if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) + spaceshipPos -= spaceshipDir * moveSpeed; + if (glfwGetKey(window, GLFW_KEY_X) == GLFW_PRESS) + spaceshipPos += spaceshipSide * moveSpeed; + if (glfwGetKey(window, GLFW_KEY_Z) == GLFW_PRESS) + spaceshipPos -= spaceshipSide * moveSpeed; + if (glfwGetKey(window, GLFW_KEY_Q) == GLFW_PRESS) + spaceshipPos += spaceshipUp * moveSpeed; + if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS) + spaceshipPos -= spaceshipUp * moveSpeed; + if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) + spaceshipDir = glm::vec3(glm::eulerAngleY(angleSpeed) * glm::vec4(spaceshipDir, 0)); + if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) + spaceshipDir = glm::vec3(glm::eulerAngleY(-angleSpeed) * glm::vec4(spaceshipDir, 0)); + + cameraPos = spaceshipPos - 1.5 * spaceshipDir + glm::vec3(0, 1, 0) * 0.5f; + cameraDir = spaceshipDir; + if (glfwGetKey(window, GLFW_KEY_1) == GLFW_PRESS) + exposition -= 0.05; + if (glfwGetKey(window, GLFW_KEY_2) == GLFW_PRESS) + exposition += 0.05; + + //cameraDir = glm::normalize(-cameraPos); + +} + +// funkcja jest glowna petla +void renderLoop(GLFWwindow* window) { + while (!glfwWindowShouldClose(window)) + { + processInput(window); + + renderScene(window); + glfwPollEvents(); + } +} +//} \ No newline at end of file diff --git a/cw_8/src/ex_8_2.hpp b/cw_8/src/ex_8_2.hpp new file mode 100644 index 0000000..0c0392f --- /dev/null +++ b/cw_8/src/ex_8_2.hpp @@ -0,0 +1,270 @@ +#include "glew.h" +#include +#include "glm.hpp" +#include "ext.hpp" +#include +#include + +#include "Shader_Loader.h" +#include "Render_Utils.h" +//#include "Texture.h" + +#include "Box.cpp" +#include +#include +#include +#include + + +namespace texture { + GLuint earth; + GLuint clouds; + GLuint moon; + GLuint ship; + + GLuint grid; + + GLuint earthNormal; + GLuint asteroidNormal; + GLuint shipNormal; +} + + +GLuint program; +GLuint programSun; +GLuint programTex; +Core::Shader_Loader shaderLoader; + +Core::RenderContext shipContext; +Core::RenderContext sphereContext; + +glm::vec3 cameraPos = glm::vec3(-4.f, 0, 0); +glm::vec3 cameraDir = glm::vec3(1.f, 0.f, 0.f); + +glm::vec3 spaceshipPos = glm::vec3(-4.f, 0, 0); +glm::vec3 spaceshipDir = glm::vec3(1.f, 0.f, 0.f); + +GLuint VAO,VBO; + +float aspectRatio = 1.f; + +float exposition = 1.f; + +glm::vec3 lightPos = glm::vec3(0, 0, 0); +glm::vec3 lightColor = glm::vec3(0.9, 0.7, 0.8)*100; + +glm::vec3 spotlightPos = glm::vec3(0, 0, 0); +glm::vec3 spotlightConeDir = glm::vec3(0, 0, 0); +glm::vec3 spotlightColor = glm::vec3(0.5, 0.9, 0.8)*10; +float spotlightPhi = 3.14 / 3; + + +float lastTime = -1.f; +float deltaTime = 0.f; + +void updateDeltaTime(float time) { + if (lastTime < 0) { + lastTime = time; + return; + } + + deltaTime = time - lastTime; + if (deltaTime > 0.1) deltaTime = 0.1; + lastTime = time; +} +glm::mat4 createCameraMatrix() +{ + glm::vec3 cameraSide = glm::normalize(glm::cross(cameraDir,glm::vec3(0.f,1.f,0.f))); + glm::vec3 cameraUp = glm::normalize(glm::cross(cameraSide,cameraDir)); + glm::mat4 cameraRotrationMatrix = glm::mat4({ + cameraSide.x,cameraSide.y,cameraSide.z,0, + cameraUp.x,cameraUp.y,cameraUp.z ,0, + -cameraDir.x,-cameraDir.y,-cameraDir.z,0, + 0.,0.,0.,1., + }); + cameraRotrationMatrix = glm::transpose(cameraRotrationMatrix); + glm::mat4 cameraMatrix = cameraRotrationMatrix * glm::translate(-cameraPos); + + return cameraMatrix; +} + +glm::mat4 createPerspectiveMatrix() +{ + + glm::mat4 perspectiveMatrix; + float n = 0.05; + float f = 20.; + float a1 = glm::min(aspectRatio, 1.f); + float a2 = glm::min(1 / aspectRatio, 1.f); + perspectiveMatrix = glm::mat4({ + 1,0.,0.,0., + 0.,aspectRatio,0.,0., + 0.,0.,(f+n) / (n - f),2*f * n / (n - f), + 0.,0.,-1.,0., + }); + + + perspectiveMatrix=glm::transpose(perspectiveMatrix); + + return perspectiveMatrix; +} + +void drawObjectColor(Core::RenderContext& context, glm::mat4 modelMatrix, glm::vec3 color) { + + glm::mat4 viewProjectionMatrix = createPerspectiveMatrix() * createCameraMatrix(); + glm::mat4 transformation = viewProjectionMatrix * modelMatrix; + glUniformMatrix4fv(glGetUniformLocation(program, "transformation"), 1, GL_FALSE, (float*)&transformation); + glUniformMatrix4fv(glGetUniformLocation(program, "modelMatrix"), 1, GL_FALSE, (float*)&modelMatrix); + + glUniform1f(glGetUniformLocation(program, "exposition"), exposition); + + glUniform3f(glGetUniformLocation(program, "color"), color.x, color.y, color.z); + + glUniform3f(glGetUniformLocation(program, "cameraPos"), cameraPos.x, cameraPos.y, cameraPos.z); + + glUniform3f(glGetUniformLocation(program, "lightPos"), lightPos.x, lightPos.y, lightPos.z); + glUniform3f(glGetUniformLocation(program, "lightColor"), lightColor.x, lightColor.y, lightColor.z); + + glUniform3f(glGetUniformLocation(program, "spotlightConeDir"), spotlightConeDir.x, spotlightConeDir.y, spotlightConeDir.z); + glUniform3f(glGetUniformLocation(program, "spotlightPos"), spotlightPos.x, spotlightPos.y, spotlightPos.z); + glUniform3f(glGetUniformLocation(program, "spotlightColor"), spotlightColor.x, spotlightColor.y, spotlightColor.z); + glUniform1f(glGetUniformLocation(program, "spotlightPhi"), spotlightPhi); + Core::DrawContext(context); + +} +void renderScene(GLFWwindow* window) +{ + glClearColor(0.0f, 0.3f, 0.3f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glm::mat4 transformation; + float time = glfwGetTime(); + updateDeltaTime(time); + + + + glUseProgram(programSun); + transformation = createPerspectiveMatrix() * createCameraMatrix(); + glUniformMatrix4fv(glGetUniformLocation(programSun, "transformation"), 1, GL_FALSE, (float*)&transformation); + glUniform3f(glGetUniformLocation(programSun, "color"), 0.9,0.8,0.4); + Core::DrawContext(sphereContext); + + glUseProgram(program); + drawObjectColor(sphereContext, glm::eulerAngleY(time / 3) * glm::translate(glm::vec3(4.f, 0, 0)) * glm::scale(glm::vec3(0.3f)), glm::vec3(0.2, 0.7, 0.3)); + + drawObjectColor(sphereContext, + glm::eulerAngleY(time / 3) * glm::translate(glm::vec3(4.f, 0, 0)) * glm::eulerAngleY(time) * glm::translate(glm::vec3(1.f, 0, 0)) * glm::scale(glm::vec3(0.1f)), + glm::vec3(0.5, 0.5, 0.5)); + + + + glm::vec3 spaceshipSide = glm::normalize(glm::cross(spaceshipDir, glm::vec3(0.f, 1.f, 0.f))); + glm::vec3 spaceshipUp = glm::normalize(glm::cross(spaceshipSide, spaceshipDir)); + glm::mat4 specshipCameraRotrationMatrix = glm::mat4({ + spaceshipSide.x,spaceshipSide.y,spaceshipSide.z,0, + spaceshipUp.x,spaceshipUp.y,spaceshipUp.z ,0, + -spaceshipDir.x,-spaceshipDir.y,-spaceshipDir.z,0, + 0.,0.,0.,1., + }); + + + //drawObjectColor(shipContext, + // glm::translate(cameraPos + 1.5 * cameraDir + cameraUp * -0.5f) * inveseCameraRotrationMatrix * glm::eulerAngleY(glm::pi()), + // glm::vec3(0.3, 0.3, 0.5) + // ); + drawObjectColor(shipContext, + glm::translate(spaceshipPos) * specshipCameraRotrationMatrix * glm::eulerAngleY(glm::pi())*glm::scale(glm::vec3(0.1)), + glm::vec3(0.3, 0.3, 0.5) + ); + + spotlightPos = spaceshipPos + 0.5 * spaceshipDir; + spotlightConeDir = spaceshipDir; + + glUseProgram(0); + glfwSwapBuffers(window); +} +void framebuffer_size_callback(GLFWwindow* window, int width, int height) +{ + aspectRatio = width / float(height); + glViewport(0, 0, width, height); +} +void loadModelToContext(std::string path, Core::RenderContext& context) +{ + Assimp::Importer import; + const aiScene* scene = import.ReadFile(path, aiProcess_Triangulate | aiProcess_CalcTangentSpace); + + if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) + { + std::cout << "ERROR::ASSIMP::" << import.GetErrorString() << std::endl; + return; + } + context.initFromAssimpMesh(scene->mMeshes[0]); +} + +void init(GLFWwindow* window) +{ + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + + glEnable(GL_DEPTH_TEST); + program = shaderLoader.CreateProgram("shaders/shader_8_1.vert", "shaders/shader_8_1.frag"); + programSun = shaderLoader.CreateProgram("shaders/shader_8_sun.vert", "shaders/shader_8_sun.frag"); + + loadModelToContext("./models/sphere.obj", sphereContext); + loadModelToContext("./models/spaceship.obj", shipContext); + +} + +void shutdown(GLFWwindow* window) +{ + shaderLoader.DeleteProgram(program); +} + +//obsluga wejscia +void processInput(GLFWwindow* window) +{ + glm::vec3 spaceshipSide = glm::normalize(glm::cross(spaceshipDir, glm::vec3(0.f, 1.f, 0.f))); + glm::vec3 spaceshipUp = glm::vec3(0.f, 1.f, 0.f); + float angleSpeed = 0.05f * deltaTime * 60; + float moveSpeed = 0.05f * deltaTime * 60; + if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) { + glfwSetWindowShouldClose(window, true); + } + if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) + spaceshipPos += spaceshipDir * moveSpeed; + if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) + spaceshipPos -= spaceshipDir * moveSpeed; + if (glfwGetKey(window, GLFW_KEY_X) == GLFW_PRESS) + spaceshipPos += spaceshipSide * moveSpeed; + if (glfwGetKey(window, GLFW_KEY_Z) == GLFW_PRESS) + spaceshipPos -= spaceshipSide * moveSpeed; + if (glfwGetKey(window, GLFW_KEY_Q) == GLFW_PRESS) + spaceshipPos += spaceshipUp * moveSpeed; + if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS) + spaceshipPos -= spaceshipUp * moveSpeed; + if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) + spaceshipDir = glm::vec3(glm::eulerAngleY(angleSpeed) * glm::vec4(spaceshipDir, 0)); + if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) + spaceshipDir = glm::vec3(glm::eulerAngleY(-angleSpeed) * glm::vec4(spaceshipDir, 0)); + + cameraPos = spaceshipPos - 1.5 * spaceshipDir + glm::vec3(0, 1, 0) * 0.5f; + cameraDir = spaceshipDir; + + if (glfwGetKey(window, GLFW_KEY_1) == GLFW_PRESS) + exposition -= 0.05; + if (glfwGetKey(window, GLFW_KEY_2) == GLFW_PRESS) + exposition += 0.05; + + //cameraDir = glm::normalize(-cameraPos); + +} + +// funkcja jest glowna petla +void renderLoop(GLFWwindow* window) { + while (!glfwWindowShouldClose(window)) + { + processInput(window); + + renderScene(window); + glfwPollEvents(); + } +} +//} \ No newline at end of file diff --git a/cw_8/src/main.cpp b/cw_8/src/main.cpp new file mode 100644 index 0000000..97a3bb4 --- /dev/null +++ b/cw_8/src/main.cpp @@ -0,0 +1,47 @@ +#include "glew.h" + +#include +#include "glm.hpp" +#include "ext.hpp" +#include +#include + +#include "projekt.hpp" + + + +int main(int argc, char** argv) +{ + // inicjalizacja glfw + glfwInit(); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + +#ifdef __APPLE__ + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); +#endif + + // tworzenie okna za pomoca glfw + GLFWwindow* window = glfwCreateWindow(500, 500, "FirstWindow", NULL, NULL); + if (window == NULL) + { + std::cout << "Failed to create GLFW window" << std::endl; + glfwTerminate(); + return -1; + } + glfwMakeContextCurrent(window); + + // ladowanie OpenGL za pomoca glew + glewInit(); + glViewport(0, 0, 500, 500); + + init(window); + + // uruchomienie glownej petli + renderLoop(window); + + shutdown(window); + glfwTerminate(); + return 0; +} diff --git a/cw_8/src/objload.h b/cw_8/src/objload.h new file mode 100644 index 0000000..004ab4e --- /dev/null +++ b/cw_8/src/objload.h @@ -0,0 +1,291 @@ +/* Copyright (c) 2012, Gerhard Reitmayr + All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#ifndef OBJLOAD_H_ +#define OBJLOAD_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace obj { + +struct Model { + std::vector vertex; //< 3 * N entries + std::vector texCoord; //< 2 * N entries + std::vector normal; //< 3 * N entries + + std::map > faces; //< assume triangels and uniform indexing +}; + +struct ObjModel { + struct FaceVertex { + FaceVertex() : v(-1), t(-1), n(-1) {} + int v, t, n; + + bool operator<( const FaceVertex & other ) const; + bool operator==( const FaceVertex & other ) const; + }; + + typedef std::pair, std::vector > FaceList; + + std::vector vertex; //< 3 * N entries + std::vector texCoord; //< 2 * N entries + std::vector normal; //< 3 * N entries + + std::map faces; +}; + +inline ObjModel parseObjModel( std::istream & in); +inline void tesselateObjModel( ObjModel & obj); +inline ObjModel tesselateObjModel( const ObjModel & obj ); +inline Model convertToModel( const ObjModel & obj ); + +inline Model loadModel( std::istream & in ); +inline Model loadModelFromString( const std::string & in ); +inline Model loadModelFromFile( const std::string & in ); + +inline std::ostream & operator<<( std::ostream & out, const Model & m ); +inline std::ostream & operator<<( std::ostream & out, const ObjModel::FaceVertex & f); + +// ---------------------------- Implementation starts here ----------------------- + +inline bool ObjModel::FaceVertex::operator<( const ObjModel::FaceVertex & other ) const { + return (v < other.v) || (v == other.v && t < other.t ) || (v == other.v && t == other.t && n < other.n); +} + +inline bool ObjModel::FaceVertex::operator==( const ObjModel::FaceVertex & other ) const { + return (v == other.v && t == other.t && n == other.n); +} + +template +inline std::istream & operator>>(std::istream & in, std::vector & vec ){ + T temp; + if(in >> temp) + vec.push_back(temp); + return in; +} + +template +inline std::istream & operator>>(std::istream & in, std::set & vec ){ + T temp; + if(in >> temp) + vec.insert(temp); + return in; +} + +inline std::istream & operator>>( std::istream & in, ObjModel::FaceVertex & f){ + if(in >> f.v){ + if(in.peek() == '/'){ + in.get(); + in >> f.t; + in.clear(); + if(in.peek() == '/'){ + in.get(); + in >> f.n; + in.clear(); + } + } + in.clear(); + --f.v; + --f.t; + --f.n; + } + // std::cout << f << std::endl; + return in; +} + +ObjModel parseObjModel( std::istream & in ){ + char line[1024]; + std::string op; + std::istringstream line_in; + std::set groups; + groups.insert("default"); + + ObjModel data; + + while(in.good()){ + in.getline(line, 1023); + line_in.clear(); + line_in.str(line); + + if(!(line_in >> op)) + continue; + if(op == "v") + line_in >> data.vertex >> data.vertex >> data.vertex; + else if(op == "vt") + line_in >> data.texCoord >> data.texCoord >> data.texCoord; + else if(op == "vn") + line_in >> data.normal >> data.normal >> data.normal; + else if(op == "g"){ + groups.clear(); + while(line_in >> groups) ; + groups.insert("default"); + } + else if(op == "f") { + std::vector list; + while(line_in >> list) ; + + for(std::set::const_iterator g = groups.begin(); g != groups.end(); ++g){ + ObjModel::FaceList & fl = data.faces[*g]; + fl.second.push_back(fl.first.size()); + fl.first.insert(fl.first.end(), list.begin(), list.end()); + } + } + } + for(std::map::iterator g = data.faces.begin(); g != data.faces.end(); ++g){ + ObjModel::FaceList & fl = g->second; + fl.second.push_back(fl.first.size()); + } + return data; +} + +inline void tesselateObjModel( std::vector & input, std::vector & input_start){ + std::vector output; + std::vector output_start; + output.reserve(input.size()); + output_start.reserve(input_start.size()); + + for(std::vector::const_iterator s = input_start.begin(); s != input_start.end() - 1; ++s){ + const unsigned size = *(s+1) - *s; + if(size > 3){ + const ObjModel::FaceVertex & start_vertex = input[*s]; + for( int i = 1; i < (int)size-1; ++i){ + output_start.push_back(output.size()); + output.push_back(start_vertex); + output.push_back(input[*s+i]); + output.push_back(input[*s+i+1]); + } + } else { + output_start.push_back(output.size()); + output.insert(output.end(), input.begin() + *s, input.begin() + *(s+1)); + } + } + output_start.push_back(output.size()); + input.swap(output); + input_start.swap(output_start); +} + +void tesselateObjModel( ObjModel & obj){ + for(std::map::iterator g = obj.faces.begin(); g != obj.faces.end(); ++g){ + ObjModel::FaceList & fl = g->second; + tesselateObjModel(fl.first, fl.second); + } +} + +Model convertToModel( const ObjModel & obj ) { + // insert all face vertices into a vector and make unique + std::vector unique(obj.faces.find("default")->second.first); + std::sort(unique.begin(), unique.end()); + unique.erase( std::unique(unique.begin(), unique.end()), unique.end()); + + // build a new model with repeated vertices/texcoords/normals to have single indexing + Model model; + for(std::vector::const_iterator f = unique.begin(); f != unique.end(); ++f){ + model.vertex.insert(model.vertex.end(), obj.vertex.begin() + 3*f->v, obj.vertex.begin() + 3*f->v + 3); + if(!obj.texCoord.empty()){ + const int index = (f->t > -1) ? f->t : f->v; + model.texCoord.insert(model.texCoord.end(), obj.texCoord.begin() + 2*index, obj.texCoord.begin() + 2*index + 2); + } + if(!obj.normal.empty()){ + const int index = (f->n > -1) ? f->n : f->v; + model.normal.insert(model.normal.end(), obj.normal.begin() + 3*index, obj.normal.begin() + 3*index + 3); + } + } + // look up unique index and transform face descriptions + for(std::map::const_iterator g = obj.faces.begin(); g != obj.faces.end(); ++g){ + const std::string & name = g->first; + const ObjModel::FaceList & fl = g->second; + std::vector & v = model.faces[g->first]; + v.reserve(fl.first.size()); + for(std::vector::const_iterator f = fl.first.begin(); f != fl.first.end(); ++f){ + const unsigned short index = std::distance(unique.begin(), std::lower_bound(unique.begin(), unique.end(), *f)); + v.push_back(index); + } + } + return model; +} + +ObjModel tesselateObjModel( const ObjModel & obj ){ + ObjModel result = obj; + tesselateObjModel(result); + return result; +} + +Model loadModel( std::istream & in ){ + ObjModel model = parseObjModel(in); + tesselateObjModel(model); + return convertToModel(model); +} + +Model loadModelFromString( const std::string & str ){ + std::istringstream in(str); + return loadModel(in); +} + +Model loadModelFromFile( const std::string & str) { + std::ifstream in(str.c_str()); + return loadModel(in); +} + +inline std::ostream & operator<<( std::ostream & out, const ObjModel::FaceVertex & f){ + out << f.v << "\t" << f.t << "\t" << f.n; + return out; +} + +std::ostream & operator<<( std::ostream & out, const Model & m ){ + if(!m.vertex.empty()){ + out << "vertex\n"; + for(int i = 0; i < (int)m.vertex.size(); ++i) + out << m.vertex[i] << (((i % 3) == 2)?"\n":"\t"); + } + if(!m.texCoord.empty()){ + out << "texCoord\n"; + for(int i = 0; i < (int)m.texCoord.size(); ++i) + out << m.texCoord[i] << (((i % 2) == 1)?"\n":"\t"); + } + if(!m.normal.empty()){ + out << "normal\n"; + for(int i = 0; i < (int)m.normal.size(); ++i) + out << m.normal[i] << (((i % 3) == 2)?"\n":"\t"); + } + if(!m.faces.empty()){ + out << "faces\t"; + for(std::map >::const_iterator g = m.faces.begin(); g != m.faces.end(); ++g){ + out << g->first << " "; + } + out << "\n"; +// for(int i = 0; i < m.face.size(); ++i) +// out << m.face[i] << (((i % 3) == 2)?"\n":"\t"); + } + return out; +} + +} // namespace obj + +#endif // OBJLOAD_H_ diff --git a/cw_8/src/projekt.hpp b/cw_8/src/projekt.hpp new file mode 100644 index 0000000..7aa4dfe --- /dev/null +++ b/cw_8/src/projekt.hpp @@ -0,0 +1,171 @@ +#include "glew.h" +#include +#include "glm.hpp" +#include "ext.hpp" +#include +#include + +#include "Shader_Loader.h" +#include "Render_Utils.h" + +#include "Texture.h" + +GLuint program; +Core::Shader_Loader shaderLoader; + +Core::RenderContext shipContext; +Core::RenderContext sphereContext; + +float aspectRatio = 1.f; + +glm::vec3 cameraPos = glm::vec3(-4.0f, 0.0f, 0.0f); +glm::vec3 cameraFront = glm::vec3(1.0f, 0.0f, 0.0f); +glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f); + +//declarations of functions +void loadModelToContext(std::string path, Core::RenderContext& context); +void framebuffer_size_callback(GLFWwindow* window, int width, int height); +void processInput(GLFWwindow* window); +void shutdown(GLFWwindow* window); +glm::mat4 createPerspectiveMatrix(); +glm::mat4 createCameraMatrix(); + +void drawObject(Core::RenderContext& context, glm::mat4 modelMatrix, glm::vec3 color); + +void init(GLFWwindow* window) +{ + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + + glEnable(GL_DEPTH_TEST); + program = shaderLoader.CreateProgram("shaders/pbr.vert", "shaders/pbr.frag"); + + //load models here + loadModelToContext("./models/sphere.obj", sphereContext); + loadModelToContext("./models/spaceship.obj", shipContext); + +} + +void renderScene(GLFWwindow* window){ + glClearColor(0.0f, 0.3f, 0.3f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glUseProgram(program); + + //desired objects go here + + drawObject(sphereContext, glm::mat4(), glm::vec3(255, 0.1, 251)); + //desired objects end here + glUseProgram(0); + glfwSwapBuffers(window); +} + +void processInput(GLFWwindow* window) +{ + if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) + glfwSetWindowShouldClose(window, true); + + float cameraSpeed = 0.05f; + if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) + cameraPos += cameraSpeed * cameraFront; + if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) + cameraPos -= cameraSpeed * cameraFront; + if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) + cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed; + if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) + cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed; +} + +//functions below need not be touched +void drawObject(Core::RenderContext& context, glm::mat4 modelMatrix, glm::vec3 color) { + + glm::mat4 viewProjectionMatrix = createPerspectiveMatrix() * createCameraMatrix(); + glm::mat4 transformation = viewProjectionMatrix * modelMatrix; + + glUniformMatrix4fv(glGetUniformLocation(program, "transformation"), 1, GL_FALSE, (float*)&transformation); + glUniformMatrix4fv(glGetUniformLocation(program, "modelMatrix"), 1, GL_FALSE, (float*)&modelMatrix); + + glUniform1f(glGetUniformLocation(program, "exposition"), 1.0f); + + glUniform3f(glGetUniformLocation(program, "color"), color.x, color.y, color.z); + + glUniform1f(glGetUniformLocation(program, "metallic"), 1.0f); + glUniform1f(glGetUniformLocation(program, "roughness"), 1.0f); + + glUniform3f(glGetUniformLocation(program, "cameraPos"), cameraPos.x, cameraPos.y, cameraPos.z); + + glUniform3f(glGetUniformLocation(program, "lightPos"), 0.0f, 0.0f, 0.0f); + glUniform3f(glGetUniformLocation(program, "lightColor"), 30.f, 0.f, 0.f); + + Core::DrawContext(context); +} + +void loadModelToContext(std::string path, Core::RenderContext& context) +{ + Assimp::Importer import; + const aiScene* scene = import.ReadFile(path, aiProcess_Triangulate | aiProcess_CalcTangentSpace); + + if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) + { + std::cout << "ERROR::ASSIMP::" << import.GetErrorString() << std::endl; + return; + } + context.initFromAssimpMesh(scene->mMeshes[0]); +} + +void framebuffer_size_callback(GLFWwindow* window, int width, int height) +{ + aspectRatio = width / float(height); + glViewport(0, 0, width, height); +} + +void shutdown(GLFWwindow* window) +{ + shaderLoader.DeleteProgram(program); +} + +void renderLoop(GLFWwindow* window) { + while (!glfwWindowShouldClose(window)) + { + processInput(window); + + renderScene(window); + glfwPollEvents(); + } +} + +glm::mat4 createPerspectiveMatrix() +{ + + glm::mat4 perspectiveMatrix; + float n = 0.05; + float f = 20.; + float a1 = glm::min(aspectRatio, 1.f); + float a2 = glm::min(1 / aspectRatio, 1.f); + perspectiveMatrix = glm::mat4({ + 1,0.,0.,0., + 0.,aspectRatio,0.,0., + 0.,0.,(f + n) / (n - f),2 * f * n / (n - f), + 0.,0.,-1.,0., + }); + + + perspectiveMatrix = glm::transpose(perspectiveMatrix); + + return perspectiveMatrix; +} + +glm::mat4 createCameraMatrix() +{ + glm::vec3 cameraSide = glm::normalize(glm::cross(cameraFront, glm::vec3(0.f, 1.f, 0.f))); + glm::vec3 cameraUp = glm::normalize(glm::cross(cameraSide, cameraFront)); + glm::mat4 cameraRotrationMatrix = glm::mat4({ + cameraSide.x,cameraSide.y,cameraSide.z,0, + cameraUp.x,cameraUp.y,cameraUp.z ,0, + -cameraFront.x,-cameraFront.y,-cameraFront.z,0, + 0.,0.,0.,1., + }); + cameraRotrationMatrix = glm::transpose(cameraRotrationMatrix); + glm::mat4 cameraMatrix = cameraRotrationMatrix * glm::translate(-cameraPos); + + return cameraMatrix; +} \ No newline at end of file diff --git a/cw_8/zlib.dll b/cw_8/zlib.dll new file mode 100644 index 0000000..7e7c10e Binary files /dev/null and b/cw_8/zlib.dll differ diff --git a/cw_8/zlibd.dll b/cw_8/zlibd.dll new file mode 100644 index 0000000..5f03c2c Binary files /dev/null and b/cw_8/zlibd.dll differ diff --git a/dependencies/assimp/IrrXMLd.lib b/dependencies/assimp/IrrXMLd.lib new file mode 100644 index 0000000..39c0215 Binary files /dev/null and b/dependencies/assimp/IrrXMLd.lib differ diff --git a/dependencies/assimp/assimp-vc141-mtd.dll b/dependencies/assimp/assimp-vc141-mtd.dll new file mode 100644 index 0000000..2e5fd66 Binary files /dev/null and b/dependencies/assimp/assimp-vc141-mtd.dll differ diff --git a/dependencies/assimp/assimp-vc141-mtd.exp b/dependencies/assimp/assimp-vc141-mtd.exp new file mode 100644 index 0000000..7dc5e18 Binary files /dev/null and b/dependencies/assimp/assimp-vc141-mtd.exp differ diff --git a/dependencies/assimp/assimp-vc141-mtd.lib b/dependencies/assimp/assimp-vc141-mtd.lib new file mode 100644 index 0000000..72dc2aa Binary files /dev/null and b/dependencies/assimp/assimp-vc141-mtd.lib differ diff --git a/dependencies/assimp/assimp_viewerd.exe b/dependencies/assimp/assimp_viewerd.exe new file mode 100644 index 0000000..994d52f Binary files /dev/null and b/dependencies/assimp/assimp_viewerd.exe differ diff --git a/dependencies/assimp/assimpd.exe b/dependencies/assimp/assimpd.exe new file mode 100644 index 0000000..00f49bc Binary files /dev/null and b/dependencies/assimp/assimpd.exe differ diff --git a/dependencies/assimp/include/assimp/.editorconfig b/dependencies/assimp/include/assimp/.editorconfig new file mode 100644 index 0000000..9ea6642 --- /dev/null +++ b/dependencies/assimp/include/assimp/.editorconfig @@ -0,0 +1,8 @@ +# See for details + +[*.{h,hpp,inl}] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_size = 4 +indent_style = space diff --git a/dependencies/assimp/include/assimp/BaseImporter.h b/dependencies/assimp/include/assimp/BaseImporter.h new file mode 100644 index 0000000..55f7fe3 --- /dev/null +++ b/dependencies/assimp/include/assimp/BaseImporter.h @@ -0,0 +1,418 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Definition of the base class for all importer worker classes. */ +#ifndef INCLUDED_AI_BASEIMPORTER_H +#define INCLUDED_AI_BASEIMPORTER_H + +#include "Exceptional.h" + +#include +#include +#include +#include +#include +#include + +struct aiScene; +struct aiImporterDesc; + +namespace Assimp { + +class Importer; +class IOSystem; +class BaseProcess; +class SharedPostProcessInfo; +class IOStream; + +// utility to do char4 to uint32 in a portable manner +#define AI_MAKE_MAGIC(string) ((uint32_t)((string[0] << 24) + \ + (string[1] << 16) + (string[2] << 8) + string[3])) + + +// --------------------------------------------------------------------------- +/** FOR IMPORTER PLUGINS ONLY: The BaseImporter defines a common interface + * for all importer worker classes. + * + * The interface defines two functions: CanRead() is used to check if the + * importer can handle the format of the given file. If an implementation of + * this function returns true, the importer then calls ReadFile() which + * imports the given file. ReadFile is not overridable, it just calls + * InternReadFile() and catches any ImportErrorException that might occur. + */ +class ASSIMP_API BaseImporter { + friend class Importer; + +private: + /* Pushes state into importer for the importer scale */ + virtual void UpdateImporterScale( Importer* pImp ); + +public: + + /** Constructor to be privately used by #Importer */ + BaseImporter() AI_NO_EXCEPT; + + /** Destructor, private as well */ + virtual ~BaseImporter(); + + // ------------------------------------------------------------------- + /** Returns whether the class can handle the format of the given file. + * + * The implementation should be as quick as possible. A check for + * the file extension is enough. If no suitable loader is found with + * this strategy, CanRead() is called again, the 'checkSig' parameter + * set to true this time. Now the implementation is expected to + * perform a full check of the file structure, possibly searching the + * first bytes of the file for magic identifiers or keywords. + * + * @param pFile Path and file name of the file to be examined. + * @param pIOHandler The IO handler to use for accessing any file. + * @param checkSig Set to true if this method is called a second time. + * This time, the implementation may take more time to examine the + * contents of the file to be loaded for magic bytes, keywords, etc + * to be able to load files with unknown/not existent file extensions. + * @return true if the class can read this file, false if not. + */ + virtual bool CanRead( + const std::string& pFile, + IOSystem* pIOHandler, + bool checkSig + ) const = 0; + + // ------------------------------------------------------------------- + /** Imports the given file and returns the imported data. + * If the import succeeds, ownership of the data is transferred to + * the caller. If the import fails, NULL is returned. The function + * takes care that any partially constructed data is destroyed + * beforehand. + * + * @param pImp #Importer object hosting this loader. + * @param pFile Path of the file to be imported. + * @param pIOHandler IO-Handler used to open this and possible other files. + * @return The imported data or NULL if failed. If it failed a + * human-readable error description can be retrieved by calling + * GetErrorText() + * + * @note This function is not intended to be overridden. Implement + * InternReadFile() to do the import. If an exception is thrown somewhere + * in InternReadFile(), this function will catch it and transform it into + * a suitable response to the caller. + */ + aiScene* ReadFile( + Importer* pImp, + const std::string& pFile, + IOSystem* pIOHandler + ); + + // ------------------------------------------------------------------- + /** Returns the error description of the last error that occurred. + * @return A description of the last error that occurred. An empty + * string if there was no error. + */ + const std::string& GetErrorText() const { + return m_ErrorText; + } + + // ------------------------------------------------------------------- + /** Called prior to ReadFile(). + * The function is a request to the importer to update its configuration + * basing on the Importer's configuration property list. + * @param pImp Importer instance + */ + virtual void SetupProperties( + const Importer* pImp + ); + + // ------------------------------------------------------------------- + /** Called by #Importer::GetImporterInfo to get a description of + * some loader features. Importers must provide this information. */ + virtual const aiImporterDesc* GetInfo() const = 0; + + /** + * Will be called only by scale process when scaling is requested. + */ + virtual void SetFileScale(double scale) + { + fileScale = scale; + } + + virtual double GetFileScale() const + { + return fileScale; + } + + enum ImporterUnits { + M, + MM, + CM, + INCHES, + FEET + }; + + /** + * Assimp Importer + * unit conversions available + * if you need another measurment unit add it below. + * it's currently defined in assimp that we prefer meters. + * */ + std::map importerUnits = { + {ImporterUnits::M, 1}, + {ImporterUnits::CM, 0.01}, + {ImporterUnits::MM, 0.001}, + {ImporterUnits::INCHES, 0.0254}, + {ImporterUnits::FEET, 0.3048} + }; + + virtual void SetApplicationUnits( const ImporterUnits& unit ) + { + importerScale = importerUnits[unit]; + applicationUnits = unit; + } + + virtual const ImporterUnits& GetApplicationUnits() + { + return applicationUnits; + } + + // ------------------------------------------------------------------- + /** Called by #Importer::GetExtensionList for each loaded importer. + * Take the extension list contained in the structure returned by + * #GetInfo and insert all file extensions into the given set. + * @param extension set to collect file extensions in*/ + void GetExtensionList(std::set& extensions); + +protected: + ImporterUnits applicationUnits = ImporterUnits::M; + double importerScale = 1.0; + double fileScale = 1.0; + + + + // ------------------------------------------------------------------- + /** Imports the given file into the given scene structure. The + * function is expected to throw an ImportErrorException if there is + * an error. If it terminates normally, the data in aiScene is + * expected to be correct. Override this function to implement the + * actual importing. + *
+ * The output scene must meet the following requirements:
+ *
    + *
  • At least a root node must be there, even if its only purpose + * is to reference one mesh.
  • + *
  • aiMesh::mPrimitiveTypes may be 0. The types of primitives + * in the mesh are determined automatically in this case.
  • + *
  • the vertex data is stored in a pseudo-indexed "verbose" format. + * In fact this means that every vertex that is referenced by + * a face is unique. Or the other way round: a vertex index may + * not occur twice in a single aiMesh.
  • + *
  • aiAnimation::mDuration may be -1. Assimp determines the length + * of the animation automatically in this case as the length of + * the longest animation channel.
  • + *
  • aiMesh::mBitangents may be NULL if tangents and normals are + * given. In this case bitangents are computed as the cross product + * between normal and tangent.
  • + *
  • There needn't be a material. If none is there a default material + * is generated. However, it is recommended practice for loaders + * to generate a default material for yourself that matches the + * default material setting for the file format better than Assimp's + * generic default material. Note that default materials *should* + * be named AI_DEFAULT_MATERIAL_NAME if they're just color-shaded + * or AI_DEFAULT_TEXTURED_MATERIAL_NAME if they define a (dummy) + * texture.
  • + *
+ * If the AI_SCENE_FLAGS_INCOMPLETE-Flag is not set:
    + *
  • at least one mesh must be there
  • + *
  • there may be no meshes with 0 vertices or faces
  • + *
+ * This won't be checked (except by the validation step): Assimp will + * crash if one of the conditions is not met! + * + * @param pFile Path of the file to be imported. + * @param pScene The scene object to hold the imported data. + * NULL is not a valid parameter. + * @param pIOHandler The IO handler to use for any file access. + * NULL is not a valid parameter. */ + virtual void InternReadFile( + const std::string& pFile, + aiScene* pScene, + IOSystem* pIOHandler + ) = 0; + +public: // static utilities + + // ------------------------------------------------------------------- + /** A utility for CanRead(). + * + * The function searches the header of a file for a specific token + * and returns true if this token is found. This works for text + * files only. There is a rudimentary handling of UNICODE files. + * The comparison is case independent. + * + * @param pIOSystem IO System to work with + * @param file File name of the file + * @param tokens List of tokens to search for + * @param numTokens Size of the token array + * @param searchBytes Number of bytes to be searched for the tokens. + */ + static bool SearchFileHeaderForToken( + IOSystem* pIOSystem, + const std::string& file, + const char** tokens, + unsigned int numTokens, + unsigned int searchBytes = 200, + bool tokensSol = false, + bool noAlphaBeforeTokens = false); + + // ------------------------------------------------------------------- + /** @brief Check whether a file has a specific file extension + * @param pFile Input file + * @param ext0 Extension to check for. Lowercase characters only, no dot! + * @param ext1 Optional second extension + * @param ext2 Optional third extension + * @note Case-insensitive + */ + static bool SimpleExtensionCheck ( + const std::string& pFile, + const char* ext0, + const char* ext1 = NULL, + const char* ext2 = NULL); + + // ------------------------------------------------------------------- + /** @brief Extract file extension from a string + * @param pFile Input file + * @return Extension without trailing dot, all lowercase + */ + static std::string GetExtension ( + const std::string& pFile); + + // ------------------------------------------------------------------- + /** @brief Check whether a file starts with one or more magic tokens + * @param pFile Input file + * @param pIOHandler IO system to be used + * @param magic n magic tokens + * @params num Size of magic + * @param offset Offset from file start where tokens are located + * @param Size of one token, in bytes. Maximally 16 bytes. + * @return true if one of the given tokens was found + * + * @note For convenience, the check is also performed for the + * byte-swapped variant of all tokens (big endian). Only for + * tokens of size 2,4. + */ + static bool CheckMagicToken( + IOSystem* pIOHandler, + const std::string& pFile, + const void* magic, + unsigned int num, + unsigned int offset = 0, + unsigned int size = 4); + + // ------------------------------------------------------------------- + /** An utility for all text file loaders. It converts a file to our + * UTF8 character set. Errors are reported, but ignored. + * + * @param data File buffer to be converted to UTF8 data. The buffer + * is resized as appropriate. */ + static void ConvertToUTF8( + std::vector& data); + + // ------------------------------------------------------------------- + /** An utility for all text file loaders. It converts a file from our + * UTF8 character set back to ISO-8859-1. Errors are reported, but ignored. + * + * @param data File buffer to be converted from UTF8 to ISO-8859-1. The buffer + * is resized as appropriate. */ + static void ConvertUTF8toISO8859_1( + std::string& data); + + // ------------------------------------------------------------------- + /// @brief Enum to define, if empty files are ok or not. + enum TextFileMode { + ALLOW_EMPTY, + FORBID_EMPTY + }; + + // ------------------------------------------------------------------- + /** Utility for text file loaders which copies the contents of the + * file into a memory buffer and converts it to our UTF8 + * representation. + * @param stream Stream to read from. + * @param data Output buffer to be resized and filled with the + * converted text file data. The buffer is terminated with + * a binary 0. + * @param mode Whether it is OK to load empty text files. */ + static void TextFileToBuffer( + IOStream* stream, + std::vector& data, + TextFileMode mode = FORBID_EMPTY); + + // ------------------------------------------------------------------- + /** Utility function to move a std::vector into a aiScene array + * @param vec The vector to be moved + * @param out The output pointer to the allocated array. + * @param numOut The output count of elements copied. */ + template + AI_FORCE_INLINE + static void CopyVector( + std::vector& vec, + T*& out, + unsigned int& outLength) + { + outLength = unsigned(vec.size()); + if (outLength) { + out = new T[outLength]; + std::swap_ranges(vec.begin(), vec.end(), out); + } + } + +protected: + /// Error description in case there was one. + std::string m_ErrorText; + /// Currently set progress handler. + ProgressHandler* m_progress; +}; + + + +} // end of namespace Assimp + +#endif // AI_BASEIMPORTER_H_INC diff --git a/dependencies/assimp/include/assimp/Bitmap.h b/dependencies/assimp/include/assimp/Bitmap.h new file mode 100644 index 0000000..e6b5fb1 --- /dev/null +++ b/dependencies/assimp/include/assimp/Bitmap.h @@ -0,0 +1,125 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Bitmap.h + * @brief Defines bitmap format helper for textures + * + * Used for file formats which embed their textures into the model file. + */ + +#ifndef AI_BITMAP_H_INC +#define AI_BITMAP_H_INC + +#include "defs.h" +#include +#include + +struct aiTexture; + +namespace Assimp { + +class IOStream; + +class ASSIMP_API Bitmap { +protected: + + struct Header { + uint16_t type; + uint32_t size; + uint16_t reserved1; + uint16_t reserved2; + uint32_t offset; + + // We define the struct size because sizeof(Header) might return a wrong result because of structure padding. + // Moreover, we must use this ugly and error prone syntax because Visual Studio neither support constexpr or sizeof(name_of_field). + static const std::size_t header_size = + sizeof(uint16_t) + // type + sizeof(uint32_t) + // size + sizeof(uint16_t) + // reserved1 + sizeof(uint16_t) + // reserved2 + sizeof(uint32_t); // offset + }; + + struct DIB { + uint32_t size; + int32_t width; + int32_t height; + uint16_t planes; + uint16_t bits_per_pixel; + uint32_t compression; + uint32_t image_size; + int32_t x_resolution; + int32_t y_resolution; + uint32_t nb_colors; + uint32_t nb_important_colors; + + // We define the struct size because sizeof(DIB) might return a wrong result because of structure padding. + // Moreover, we must use this ugly and error prone syntax because Visual Studio neither support constexpr or sizeof(name_of_field). + static const std::size_t dib_size = + sizeof(uint32_t) + // size + sizeof(int32_t) + // width + sizeof(int32_t) + // height + sizeof(uint16_t) + // planes + sizeof(uint16_t) + // bits_per_pixel + sizeof(uint32_t) + // compression + sizeof(uint32_t) + // image_size + sizeof(int32_t) + // x_resolution + sizeof(int32_t) + // y_resolution + sizeof(uint32_t) + // nb_colors + sizeof(uint32_t); // nb_important_colors + }; + + static const std::size_t mBytesPerPixel = 4; + +public: + static void Save(aiTexture* texture, IOStream* file); + +protected: + static void WriteHeader(Header& header, IOStream* file); + static void WriteDIB(DIB& dib, IOStream* file); + static void WriteData(aiTexture* texture, IOStream* file); +}; + +} + +#endif // AI_BITMAP_H_INC diff --git a/dependencies/assimp/include/assimp/BlobIOSystem.h b/dependencies/assimp/include/assimp/BlobIOSystem.h new file mode 100644 index 0000000..d005e5c --- /dev/null +++ b/dependencies/assimp/include/assimp/BlobIOSystem.h @@ -0,0 +1,338 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Provides cheat implementations for IOSystem and IOStream to + * redirect exporter output to a blob chain.*/ + +#ifndef AI_BLOBIOSYSTEM_H_INCLUDED +#define AI_BLOBIOSYSTEM_H_INCLUDED + +#include +#include +#include +#include +#include +#include +#include + +namespace Assimp { + class BlobIOSystem; + +// -------------------------------------------------------------------------------------------- +/** Redirect IOStream to a blob */ +// -------------------------------------------------------------------------------------------- +class BlobIOStream : public IOStream +{ +public: + + BlobIOStream(BlobIOSystem* creator, const std::string& file, size_t initial = 4096) + : buffer() + , cur_size() + , file_size() + , cursor() + , initial(initial) + , file(file) + , creator(creator) + { + } + + + virtual ~BlobIOStream(); + +public: + + // ------------------------------------------------------------------- + aiExportDataBlob* GetBlob() + { + aiExportDataBlob* blob = new aiExportDataBlob(); + blob->size = file_size; + blob->data = buffer; + + buffer = NULL; + + return blob; + } + + +public: + + + // ------------------------------------------------------------------- + virtual size_t Read( void *, + size_t, + size_t ) + { + return 0; + } + + // ------------------------------------------------------------------- + virtual size_t Write(const void* pvBuffer, + size_t pSize, + size_t pCount) + { + pSize *= pCount; + if (cursor + pSize > cur_size) { + Grow(cursor + pSize); + } + + memcpy(buffer+cursor, pvBuffer, pSize); + cursor += pSize; + + file_size = std::max(file_size,cursor); + return pCount; + } + + // ------------------------------------------------------------------- + virtual aiReturn Seek(size_t pOffset, + aiOrigin pOrigin) + { + switch(pOrigin) + { + case aiOrigin_CUR: + cursor += pOffset; + break; + + case aiOrigin_END: + cursor = file_size - pOffset; + break; + + case aiOrigin_SET: + cursor = pOffset; + break; + + default: + return AI_FAILURE; + } + + if (cursor > file_size) { + Grow(cursor); + } + + file_size = std::max(cursor,file_size); + return AI_SUCCESS; + } + + // ------------------------------------------------------------------- + virtual size_t Tell() const + { + return cursor; + } + + // ------------------------------------------------------------------- + virtual size_t FileSize() const + { + return file_size; + } + + // ------------------------------------------------------------------- + virtual void Flush() + { + // ignore + } + + + +private: + + // ------------------------------------------------------------------- + void Grow(size_t need = 0) + { + // 1.5 and phi are very heap-friendly growth factors (the first + // allows for frequent re-use of heap blocks, the second + // forms a fibonacci sequence with similar characteristics - + // since this heavily depends on the heap implementation + // and other factors as well, i'll just go with 1.5 since + // it is quicker to compute). + size_t new_size = std::max(initial, std::max( need, cur_size+(cur_size>>1) )); + + const uint8_t* const old = buffer; + buffer = new uint8_t[new_size]; + + if (old) { + memcpy(buffer,old,cur_size); + delete[] old; + } + + cur_size = new_size; + } + +private: + + uint8_t* buffer; + size_t cur_size,file_size, cursor, initial; + + const std::string file; + BlobIOSystem* const creator; +}; + + +#define AI_BLOBIO_MAGIC "$blobfile" + +// -------------------------------------------------------------------------------------------- +/** Redirect IOSystem to a blob */ +// -------------------------------------------------------------------------------------------- +class BlobIOSystem : public IOSystem +{ + + friend class BlobIOStream; + typedef std::pair BlobEntry; + +public: + + BlobIOSystem() + { + } + + virtual ~BlobIOSystem() + { + for(BlobEntry& blobby : blobs) { + delete blobby.second; + } + } + +public: + + // ------------------------------------------------------------------- + const char* GetMagicFileName() const + { + return AI_BLOBIO_MAGIC; + } + + + // ------------------------------------------------------------------- + aiExportDataBlob* GetBlobChain() + { + // one must be the master + aiExportDataBlob* master = NULL, *cur; + for(const BlobEntry& blobby : blobs) { + if (blobby.first == AI_BLOBIO_MAGIC) { + master = blobby.second; + break; + } + } + if (!master) { + ASSIMP_LOG_ERROR("BlobIOSystem: no data written or master file was not closed properly."); + return NULL; + } + + master->name.Set(""); + + cur = master; + for(const BlobEntry& blobby : blobs) { + if (blobby.second == master) { + continue; + } + + cur->next = blobby.second; + cur = cur->next; + + // extract the file extension from the file written + const std::string::size_type s = blobby.first.find_first_of('.'); + cur->name.Set(s == std::string::npos ? blobby.first : blobby.first.substr(s+1)); + } + + // give up blob ownership + blobs.clear(); + return master; + } + +public: + + // ------------------------------------------------------------------- + virtual bool Exists( const char* pFile) const { + return created.find(std::string(pFile)) != created.end(); + } + + + // ------------------------------------------------------------------- + virtual char getOsSeparator() const { + return '/'; + } + + + // ------------------------------------------------------------------- + virtual IOStream* Open(const char* pFile, + const char* pMode) + { + if (pMode[0] != 'w') { + return NULL; + } + + created.insert(std::string(pFile)); + return new BlobIOStream(this,std::string(pFile)); + } + + // ------------------------------------------------------------------- + virtual void Close( IOStream* pFile) + { + delete pFile; + } + +private: + + // ------------------------------------------------------------------- + void OnDestruct(const std::string& filename, BlobIOStream* child) + { + // we don't know in which the files are closed, so we + // can't reliably say that the first must be the master + // file ... + blobs.push_back( BlobEntry(filename,child->GetBlob()) ); + } + +private: + std::set created; + std::vector< BlobEntry > blobs; +}; + + +// -------------------------------------------------------------------------------------------- +BlobIOStream :: ~BlobIOStream() +{ + creator->OnDestruct(file,this); + delete[] buffer; +} + + +} // end Assimp + +#endif diff --git a/dependencies/assimp/include/assimp/ByteSwapper.h b/dependencies/assimp/include/assimp/ByteSwapper.h new file mode 100644 index 0000000..20a2463 --- /dev/null +++ b/dependencies/assimp/include/assimp/ByteSwapper.h @@ -0,0 +1,287 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Helper class tp perform various byte oder swappings + (e.g. little to big endian) */ +#ifndef AI_BYTESWAPPER_H_INC +#define AI_BYTESWAPPER_H_INC + +#include +#include +#include + +#if _MSC_VER >= 1400 +#include +#endif + +namespace Assimp { +// -------------------------------------------------------------------------------------- +/** Defines some useful byte order swap routines. + * + * This is required to read big-endian model formats on little-endian machines, + * and vice versa. Direct use of this class is DEPRECATED. Use #StreamReader instead. */ +// -------------------------------------------------------------------------------------- +class ByteSwap { + ByteSwap() AI_NO_EXCEPT {} + +public: + + // ---------------------------------------------------------------------- + /** Swap two bytes of data + * @param[inout] _szOut A void* to save the reintcasts for the caller. */ + static inline void Swap2(void* _szOut) + { + ai_assert(_szOut); + +#if _MSC_VER >= 1400 + uint16_t* const szOut = reinterpret_cast(_szOut); + *szOut = _byteswap_ushort(*szOut); +#else + uint8_t* const szOut = reinterpret_cast(_szOut); + std::swap(szOut[0],szOut[1]); +#endif + } + + // ---------------------------------------------------------------------- + /** Swap four bytes of data + * @param[inout] _szOut A void* to save the reintcasts for the caller. */ + static inline void Swap4(void* _szOut) + { + ai_assert(_szOut); + +#if _MSC_VER >= 1400 + uint32_t* const szOut = reinterpret_cast(_szOut); + *szOut = _byteswap_ulong(*szOut); +#else + uint8_t* const szOut = reinterpret_cast(_szOut); + std::swap(szOut[0],szOut[3]); + std::swap(szOut[1],szOut[2]); +#endif + } + + // ---------------------------------------------------------------------- + /** Swap eight bytes of data + * @param[inout] _szOut A void* to save the reintcasts for the caller. */ + static inline void Swap8(void* _szOut) + { + ai_assert(_szOut); + +#if _MSC_VER >= 1400 + uint64_t* const szOut = reinterpret_cast(_szOut); + *szOut = _byteswap_uint64(*szOut); +#else + uint8_t* const szOut = reinterpret_cast(_szOut); + std::swap(szOut[0],szOut[7]); + std::swap(szOut[1],szOut[6]); + std::swap(szOut[2],szOut[5]); + std::swap(szOut[3],szOut[4]); +#endif + } + + // ---------------------------------------------------------------------- + /** ByteSwap a float. Not a joke. + * @param[inout] fOut ehm. .. */ + static inline void Swap(float* fOut) { + Swap4(fOut); + } + + // ---------------------------------------------------------------------- + /** ByteSwap a double. Not a joke. + * @param[inout] fOut ehm. .. */ + static inline void Swap(double* fOut) { + Swap8(fOut); + } + + + // ---------------------------------------------------------------------- + /** ByteSwap an int16t. Not a joke. + * @param[inout] fOut ehm. .. */ + static inline void Swap(int16_t* fOut) { + Swap2(fOut); + } + + static inline void Swap(uint16_t* fOut) { + Swap2(fOut); + } + + // ---------------------------------------------------------------------- + /** ByteSwap an int32t. Not a joke. + * @param[inout] fOut ehm. .. */ + static inline void Swap(int32_t* fOut){ + Swap4(fOut); + } + + static inline void Swap(uint32_t* fOut){ + Swap4(fOut); + } + + // ---------------------------------------------------------------------- + /** ByteSwap an int64t. Not a joke. + * @param[inout] fOut ehm. .. */ + static inline void Swap(int64_t* fOut) { + Swap8(fOut); + } + + static inline void Swap(uint64_t* fOut) { + Swap8(fOut); + } + + // ---------------------------------------------------------------------- + //! Templatized ByteSwap + //! \returns param tOut as swapped + template + static inline Type Swapped(Type tOut) + { + return _swapper()(tOut); + } + +private: + + template struct _swapper; +}; + +template struct ByteSwap::_swapper { + T operator() (T tOut) { + Swap2(&tOut); + return tOut; + } +}; + +template struct ByteSwap::_swapper { + T operator() (T tOut) { + Swap4(&tOut); + return tOut; + } +}; + +template struct ByteSwap::_swapper { + T operator() (T tOut) { + Swap8(&tOut); + return tOut; + } +}; + + +// -------------------------------------------------------------------------------------- +// ByteSwap macros for BigEndian/LittleEndian support +// -------------------------------------------------------------------------------------- +#if (defined AI_BUILD_BIG_ENDIAN) +# define AI_LE(t) (t) +# define AI_BE(t) ByteSwap::Swapped(t) +# define AI_LSWAP2(p) +# define AI_LSWAP4(p) +# define AI_LSWAP8(p) +# define AI_LSWAP2P(p) +# define AI_LSWAP4P(p) +# define AI_LSWAP8P(p) +# define LE_NCONST const +# define AI_SWAP2(p) ByteSwap::Swap2(&(p)) +# define AI_SWAP4(p) ByteSwap::Swap4(&(p)) +# define AI_SWAP8(p) ByteSwap::Swap8(&(p)) +# define AI_SWAP2P(p) ByteSwap::Swap2((p)) +# define AI_SWAP4P(p) ByteSwap::Swap4((p)) +# define AI_SWAP8P(p) ByteSwap::Swap8((p)) +# define BE_NCONST +#else +# define AI_BE(t) (t) +# define AI_LE(t) ByteSwap::Swapped(t) +# define AI_SWAP2(p) +# define AI_SWAP4(p) +# define AI_SWAP8(p) +# define AI_SWAP2P(p) +# define AI_SWAP4P(p) +# define AI_SWAP8P(p) +# define BE_NCONST const +# define AI_LSWAP2(p) ByteSwap::Swap2(&(p)) +# define AI_LSWAP4(p) ByteSwap::Swap4(&(p)) +# define AI_LSWAP8(p) ByteSwap::Swap8(&(p)) +# define AI_LSWAP2P(p) ByteSwap::Swap2((p)) +# define AI_LSWAP4P(p) ByteSwap::Swap4((p)) +# define AI_LSWAP8P(p) ByteSwap::Swap8((p)) +# define LE_NCONST +#endif + + +namespace Intern { + +// -------------------------------------------------------------------------------------------- +template +struct ByteSwapper { + void operator() (T* inout) { + ByteSwap::Swap(inout); + } +}; + +template +struct ByteSwapper { + void operator() (T*) { + } +}; + +// -------------------------------------------------------------------------------------------- +template +struct Getter { + void operator() (T* inout, bool le) { +#ifdef AI_BUILD_BIG_ENDIAN + le = le; +#else + le = !le; +#endif + if (le) { + ByteSwapper1?true:false)> () (inout); + } + else ByteSwapper () (inout); + } +}; + +template +struct Getter { + + void operator() (T* inout, bool /*le*/) { + // static branch + ByteSwapper1)> () (inout); + } +}; +} // end Intern +} // end Assimp + +#endif //!! AI_BYTESWAPPER_H_INC diff --git a/dependencies/assimp/include/assimp/Compiler/poppack1.h b/dependencies/assimp/include/assimp/Compiler/poppack1.h new file mode 100644 index 0000000..e033bc1 --- /dev/null +++ b/dependencies/assimp/include/assimp/Compiler/poppack1.h @@ -0,0 +1,22 @@ + +// =============================================================================== +// May be included multiple times - resets structure packing to the defaults +// for all supported compilers. Reverts the changes made by #include +// +// Currently this works on the following compilers: +// MSVC 7,8,9 +// GCC +// BORLAND (complains about 'pack state changed but not reverted', but works) +// =============================================================================== + +#ifndef AI_PUSHPACK_IS_DEFINED +# error pushpack1.h must be included after poppack1.h +#endif + +// reset packing to the original value +#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__) +# pragma pack( pop ) +#endif +#undef PACK_STRUCT + +#undef AI_PUSHPACK_IS_DEFINED diff --git a/dependencies/assimp/include/assimp/Compiler/pstdint.h b/dependencies/assimp/include/assimp/Compiler/pstdint.h new file mode 100644 index 0000000..4de4ce2 --- /dev/null +++ b/dependencies/assimp/include/assimp/Compiler/pstdint.h @@ -0,0 +1,912 @@ +/* A portable stdint.h + **************************************************************************** + * BSD License: + **************************************************************************** + * + * Copyright (c) 2005-2016 Paul Hsieh + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **************************************************************************** + * + * Version 0.1.15.4 + * + * The ANSI C standard committee, for the C99 standard, specified the + * inclusion of a new standard include file called stdint.h. This is + * a very useful and long desired include file which contains several + * very precise definitions for integer scalar types that is + * critically important for making portable several classes of + * applications including cryptography, hashing, variable length + * integer libraries and so on. But for most developers its likely + * useful just for programming sanity. + * + * The problem is that some compiler vendors chose to ignore the C99 + * standard and some older compilers have no opportunity to be updated. + * Because of this situation, simply including stdint.h in your code + * makes it unportable. + * + * So that's what this file is all about. Its an attempt to build a + * single universal include file that works on as many platforms as + * possible to deliver what stdint.h is supposed to. Even compilers + * that already come with stdint.h can use this file instead without + * any loss of functionality. A few things that should be noted about + * this file: + * + * 1) It is not guaranteed to be portable and/or present an identical + * interface on all platforms. The extreme variability of the + * ANSI C standard makes this an impossibility right from the + * very get go. Its really only meant to be useful for the vast + * majority of platforms that possess the capability of + * implementing usefully and precisely defined, standard sized + * integer scalars. Systems which are not intrinsically 2s + * complement may produce invalid constants. + * + * 2) There is an unavoidable use of non-reserved symbols. + * + * 3) Other standard include files are invoked. + * + * 4) This file may come in conflict with future platforms that do + * include stdint.h. The hope is that one or the other can be + * used with no real difference. + * + * 5) In the current version, if your platform can't represent + * int32_t, int16_t and int8_t, it just dumps out with a compiler + * error. + * + * 6) 64 bit integers may or may not be defined. Test for their + * presence with the test: #ifdef INT64_MAX or #ifdef UINT64_MAX. + * Note that this is different from the C99 specification which + * requires the existence of 64 bit support in the compiler. If + * this is not defined for your platform, yet it is capable of + * dealing with 64 bits then it is because this file has not yet + * been extended to cover all of your system's capabilities. + * + * 7) (u)intptr_t may or may not be defined. Test for its presence + * with the test: #ifdef PTRDIFF_MAX. If this is not defined + * for your platform, then it is because this file has not yet + * been extended to cover all of your system's capabilities, not + * because its optional. + * + * 8) The following might not been defined even if your platform is + * capable of defining it: + * + * WCHAR_MIN + * WCHAR_MAX + * (u)int64_t + * PTRDIFF_MIN + * PTRDIFF_MAX + * (u)intptr_t + * + * 9) The following have not been defined: + * + * WINT_MIN + * WINT_MAX + * + * 10) The criteria for defining (u)int_least(*)_t isn't clear, + * except for systems which don't have a type that precisely + * defined 8, 16, or 32 bit types (which this include file does + * not support anyways). Default definitions have been given. + * + * 11) The criteria for defining (u)int_fast(*)_t isn't something I + * would trust to any particular compiler vendor or the ANSI C + * committee. It is well known that "compatible systems" are + * commonly created that have very different performance + * characteristics from the systems they are compatible with, + * especially those whose vendors make both the compiler and the + * system. Default definitions have been given, but its strongly + * recommended that users never use these definitions for any + * reason (they do *NOT* deliver any serious guarantee of + * improved performance -- not in this file, nor any vendor's + * stdint.h). + * + * 12) The following macros: + * + * PRINTF_INTMAX_MODIFIER + * PRINTF_INT64_MODIFIER + * PRINTF_INT32_MODIFIER + * PRINTF_INT16_MODIFIER + * PRINTF_LEAST64_MODIFIER + * PRINTF_LEAST32_MODIFIER + * PRINTF_LEAST16_MODIFIER + * PRINTF_INTPTR_MODIFIER + * + * are strings which have been defined as the modifiers required + * for the "d", "u" and "x" printf formats to correctly output + * (u)intmax_t, (u)int64_t, (u)int32_t, (u)int16_t, (u)least64_t, + * (u)least32_t, (u)least16_t and (u)intptr_t types respectively. + * PRINTF_INTPTR_MODIFIER is not defined for some systems which + * provide their own stdint.h. PRINTF_INT64_MODIFIER is not + * defined if INT64_MAX is not defined. These are an extension + * beyond what C99 specifies must be in stdint.h. + * + * In addition, the following macros are defined: + * + * PRINTF_INTMAX_HEX_WIDTH + * PRINTF_INT64_HEX_WIDTH + * PRINTF_INT32_HEX_WIDTH + * PRINTF_INT16_HEX_WIDTH + * PRINTF_INT8_HEX_WIDTH + * PRINTF_INTMAX_DEC_WIDTH + * PRINTF_INT64_DEC_WIDTH + * PRINTF_INT32_DEC_WIDTH + * PRINTF_INT16_DEC_WIDTH + * PRINTF_UINT8_DEC_WIDTH + * PRINTF_UINTMAX_DEC_WIDTH + * PRINTF_UINT64_DEC_WIDTH + * PRINTF_UINT32_DEC_WIDTH + * PRINTF_UINT16_DEC_WIDTH + * PRINTF_UINT8_DEC_WIDTH + * + * Which specifies the maximum number of characters required to + * print the number of that type in either hexadecimal or decimal. + * These are an extension beyond what C99 specifies must be in + * stdint.h. + * + * Compilers tested (all with 0 warnings at their highest respective + * settings): Borland Turbo C 2.0, WATCOM C/C++ 11.0 (16 bits and 32 + * bits), Microsoft Visual C++ 6.0 (32 bit), Microsoft Visual Studio + * .net (VC7), Intel C++ 4.0, GNU gcc v3.3.3 + * + * This file should be considered a work in progress. Suggestions for + * improvements, especially those which increase coverage are strongly + * encouraged. + * + * Acknowledgements + * + * The following people have made significant contributions to the + * development and testing of this file: + * + * Chris Howie + * John Steele Scott + * Dave Thorup + * John Dill + * Florian Wobbe + * Christopher Sean Morrison + * Mikkel Fahnoe Jorgensen + * + */ + +#include +#include +#include + +/* + * For gcc with _STDINT_H, fill in the PRINTF_INT*_MODIFIER macros, and + * do nothing else. On the Mac OS X version of gcc this is _STDINT_H_. + */ + +#if ((defined(__SUNPRO_C) && __SUNPRO_C >= 0x570) || (defined(_MSC_VER) && _MSC_VER >= 1600) || (defined(__STDC__) && __STDC__ && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || (defined (__WATCOMC__) && (defined (_STDINT_H_INCLUDED) || __WATCOMC__ >= 1250)) || (defined(__GNUC__) && (__GNUC__ > 3 || defined(_STDINT_H) || defined(_STDINT_H_) || defined (__UINT_FAST64_TYPE__)) )) && !defined (_PSTDINT_H_INCLUDED) +#include +#define _PSTDINT_H_INCLUDED +# if defined(__GNUC__) && (defined(__x86_64__) || defined(__ppc64__)) && !(defined(__APPLE__) && defined(__MACH__)) +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "l" +# endif +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +# else +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# ifndef PRINTF_INT32_MODIFIER +# if (UINT_MAX == UINT32_MAX) +# define PRINTF_INT32_MODIFIER "" +# else +# define PRINTF_INT32_MODIFIER "l" +# endif +# endif +# endif +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "h" +# endif +# ifndef PRINTF_INTMAX_MODIFIER +# define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER +# endif +# ifndef PRINTF_INT64_HEX_WIDTH +# define PRINTF_INT64_HEX_WIDTH "16" +# endif +# ifndef PRINTF_UINT64_HEX_WIDTH +# define PRINTF_UINT64_HEX_WIDTH "16" +# endif +# ifndef PRINTF_INT32_HEX_WIDTH +# define PRINTF_INT32_HEX_WIDTH "8" +# endif +# ifndef PRINTF_UINT32_HEX_WIDTH +# define PRINTF_UINT32_HEX_WIDTH "8" +# endif +# ifndef PRINTF_INT16_HEX_WIDTH +# define PRINTF_INT16_HEX_WIDTH "4" +# endif +# ifndef PRINTF_UINT16_HEX_WIDTH +# define PRINTF_UINT16_HEX_WIDTH "4" +# endif +# ifndef PRINTF_INT8_HEX_WIDTH +# define PRINTF_INT8_HEX_WIDTH "2" +# endif +# ifndef PRINTF_UINT8_HEX_WIDTH +# define PRINTF_UINT8_HEX_WIDTH "2" +# endif +# ifndef PRINTF_INT64_DEC_WIDTH +# define PRINTF_INT64_DEC_WIDTH "19" +# endif +# ifndef PRINTF_UINT64_DEC_WIDTH +# define PRINTF_UINT64_DEC_WIDTH "20" +# endif +# ifndef PRINTF_INT32_DEC_WIDTH +# define PRINTF_INT32_DEC_WIDTH "10" +# endif +# ifndef PRINTF_UINT32_DEC_WIDTH +# define PRINTF_UINT32_DEC_WIDTH "10" +# endif +# ifndef PRINTF_INT16_DEC_WIDTH +# define PRINTF_INT16_DEC_WIDTH "5" +# endif +# ifndef PRINTF_UINT16_DEC_WIDTH +# define PRINTF_UINT16_DEC_WIDTH "5" +# endif +# ifndef PRINTF_INT8_DEC_WIDTH +# define PRINTF_INT8_DEC_WIDTH "3" +# endif +# ifndef PRINTF_UINT8_DEC_WIDTH +# define PRINTF_UINT8_DEC_WIDTH "3" +# endif +# ifndef PRINTF_INTMAX_HEX_WIDTH +# define PRINTF_INTMAX_HEX_WIDTH PRINTF_UINT64_HEX_WIDTH +# endif +# ifndef PRINTF_UINTMAX_HEX_WIDTH +# define PRINTF_UINTMAX_HEX_WIDTH PRINTF_UINT64_HEX_WIDTH +# endif +# ifndef PRINTF_INTMAX_DEC_WIDTH +# define PRINTF_INTMAX_DEC_WIDTH PRINTF_UINT64_DEC_WIDTH +# endif +# ifndef PRINTF_UINTMAX_DEC_WIDTH +# define PRINTF_UINTMAX_DEC_WIDTH PRINTF_UINT64_DEC_WIDTH +# endif + +/* + * Something really weird is going on with Open Watcom. Just pull some of + * these duplicated definitions from Open Watcom's stdint.h file for now. + */ + +# if defined (__WATCOMC__) && __WATCOMC__ >= 1250 +# if !defined (INT64_C) +# define INT64_C(x) (x + (INT64_MAX - INT64_MAX)) +# endif +# if !defined (UINT64_C) +# define UINT64_C(x) (x + (UINT64_MAX - UINT64_MAX)) +# endif +# if !defined (INT32_C) +# define INT32_C(x) (x + (INT32_MAX - INT32_MAX)) +# endif +# if !defined (UINT32_C) +# define UINT32_C(x) (x + (UINT32_MAX - UINT32_MAX)) +# endif +# if !defined (INT16_C) +# define INT16_C(x) (x) +# endif +# if !defined (UINT16_C) +# define UINT16_C(x) (x) +# endif +# if !defined (INT8_C) +# define INT8_C(x) (x) +# endif +# if !defined (UINT8_C) +# define UINT8_C(x) (x) +# endif +# if !defined (UINT64_MAX) +# define UINT64_MAX 18446744073709551615ULL +# endif +# if !defined (INT64_MAX) +# define INT64_MAX 9223372036854775807LL +# endif +# if !defined (UINT32_MAX) +# define UINT32_MAX 4294967295UL +# endif +# if !defined (INT32_MAX) +# define INT32_MAX 2147483647L +# endif +# if !defined (INTMAX_MAX) +# define INTMAX_MAX INT64_MAX +# endif +# if !defined (INTMAX_MIN) +# define INTMAX_MIN INT64_MIN +# endif +# endif +#endif + +/* + * I have no idea what is the truly correct thing to do on older Solaris. + * From some online discussions, this seems to be what is being + * recommended. For people who actually are developing on older Solaris, + * what I would like to know is, does this define all of the relevant + * macros of a complete stdint.h? Remember, in pstdint.h 64 bit is + * considered optional. + */ + +#if (defined(__SUNPRO_C) && __SUNPRO_C >= 0x420) && !defined(_PSTDINT_H_INCLUDED) +#include +#define _PSTDINT_H_INCLUDED +#endif + +#ifndef _PSTDINT_H_INCLUDED +#define _PSTDINT_H_INCLUDED + +#ifndef SIZE_MAX +# define SIZE_MAX (~(size_t)0) +#endif + +/* + * Deduce the type assignments from limits.h under the assumption that + * integer sizes in bits are powers of 2, and follow the ANSI + * definitions. + */ + +#ifndef UINT8_MAX +# define UINT8_MAX 0xff +#endif +#if !defined(uint8_t) && !defined(_UINT8_T) && !defined(vxWorks) +# if (UCHAR_MAX == UINT8_MAX) || defined (S_SPLINT_S) + typedef unsigned char uint8_t; +# define UINT8_C(v) ((uint8_t) v) +# else +# error "Platform not supported" +# endif +#endif + +#ifndef INT8_MAX +# define INT8_MAX 0x7f +#endif +#ifndef INT8_MIN +# define INT8_MIN INT8_C(0x80) +#endif +#if !defined(int8_t) && !defined(_INT8_T) && !defined(vxWorks) +# if (SCHAR_MAX == INT8_MAX) || defined (S_SPLINT_S) + typedef signed char int8_t; +# define INT8_C(v) ((int8_t) v) +# else +# error "Platform not supported" +# endif +#endif + +#ifndef UINT16_MAX +# define UINT16_MAX 0xffff +#endif +#if !defined(uint16_t) && !defined(_UINT16_T) && !defined(vxWorks) +#if (UINT_MAX == UINT16_MAX) || defined (S_SPLINT_S) + typedef unsigned int uint16_t; +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "" +# endif +# define UINT16_C(v) ((uint16_t) (v)) +#elif (USHRT_MAX == UINT16_MAX) + typedef unsigned short uint16_t; +# define UINT16_C(v) ((uint16_t) (v)) +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "h" +# endif +#else +#error "Platform not supported" +#endif +#endif + +#ifndef INT16_MAX +# define INT16_MAX 0x7fff +#endif +#ifndef INT16_MIN +# define INT16_MIN INT16_C(0x8000) +#endif +#if !defined(int16_t) && !defined(_INT16_T) && !defined(vxWorks) +#if (INT_MAX == INT16_MAX) || defined (S_SPLINT_S) + typedef signed int int16_t; +# define INT16_C(v) ((int16_t) (v)) +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "" +# endif +#elif (SHRT_MAX == INT16_MAX) + typedef signed short int16_t; +# define INT16_C(v) ((int16_t) (v)) +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "h" +# endif +#else +#error "Platform not supported" +#endif +#endif + +#ifndef UINT32_MAX +# define UINT32_MAX (0xffffffffUL) +#endif +#if !defined(uint32_t) && !defined(_UINT32_T) && !defined(vxWorks) +#if (ULONG_MAX == UINT32_MAX) || defined (S_SPLINT_S) + typedef unsigned long uint32_t; +# define UINT32_C(v) v ## UL +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "l" +# endif +#elif (UINT_MAX == UINT32_MAX) + typedef unsigned int uint32_t; +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +# define UINT32_C(v) v ## U +#elif (USHRT_MAX == UINT32_MAX) + typedef unsigned short uint32_t; +# define UINT32_C(v) ((unsigned short) (v)) +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +#else +#error "Platform not supported" +#endif +#endif + +#ifndef INT32_MAX +# define INT32_MAX (0x7fffffffL) +#endif +#ifndef INT32_MIN +# define INT32_MIN INT32_C(0x80000000) +#endif +#if !defined(int32_t) && !defined(_INT32_T) && !defined(vxWorks) +#if (LONG_MAX == INT32_MAX) || defined (S_SPLINT_S) + typedef signed long int32_t; +# define INT32_C(v) v ## L +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "l" +# endif +#elif (INT_MAX == INT32_MAX) + typedef signed int int32_t; +# define INT32_C(v) v +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +#elif (SHRT_MAX == INT32_MAX) + typedef signed short int32_t; +# define INT32_C(v) ((short) (v)) +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +#else +#error "Platform not supported" +#endif +#endif + +/* + * The macro stdint_int64_defined is temporarily used to record + * whether or not 64 integer support is available. It must be + * defined for any 64 integer extensions for new platforms that are + * added. + */ + +#undef stdint_int64_defined +#if (defined(__STDC__) && defined(__STDC_VERSION__)) || defined (S_SPLINT_S) +# if (__STDC__ && __STDC_VERSION__ >= 199901L) || defined (S_SPLINT_S) +# define stdint_int64_defined + typedef long long int64_t; + typedef unsigned long long uint64_t; +# define UINT64_C(v) v ## ULL +# define INT64_C(v) v ## LL +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# endif +#endif + +#if !defined (stdint_int64_defined) +# if defined(__GNUC__) && !defined(vxWorks) +# define stdint_int64_defined + __extension__ typedef long long int64_t; + __extension__ typedef unsigned long long uint64_t; +# define UINT64_C(v) v ## ULL +# define INT64_C(v) v ## LL +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# elif defined(__MWERKS__) || defined (__SUNPRO_C) || defined (__SUNPRO_CC) || defined (__APPLE_CC__) || defined (_LONG_LONG) || defined (_CRAYC) || defined (S_SPLINT_S) +# define stdint_int64_defined + typedef long long int64_t; + typedef unsigned long long uint64_t; +# define UINT64_C(v) v ## ULL +# define INT64_C(v) v ## LL +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# elif (defined(__WATCOMC__) && defined(__WATCOM_INT64__)) || (defined(_MSC_VER) && _INTEGRAL_MAX_BITS >= 64) || (defined (__BORLANDC__) && __BORLANDC__ > 0x460) || defined (__alpha) || defined (__DECC) +# define stdint_int64_defined + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; +# define UINT64_C(v) v ## UI64 +# define INT64_C(v) v ## I64 +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "I64" +# endif +# endif +#endif + +#if !defined (LONG_LONG_MAX) && defined (INT64_C) +# define LONG_LONG_MAX INT64_C (9223372036854775807) +#endif +#ifndef ULONG_LONG_MAX +# define ULONG_LONG_MAX UINT64_C (18446744073709551615) +#endif + +#if !defined (INT64_MAX) && defined (INT64_C) +# define INT64_MAX INT64_C (9223372036854775807) +#endif +#if !defined (INT64_MIN) && defined (INT64_C) +# define INT64_MIN INT64_C (-9223372036854775808) +#endif +#if !defined (UINT64_MAX) && defined (INT64_C) +# define UINT64_MAX UINT64_C (18446744073709551615) +#endif + +/* + * Width of hexadecimal for number field. + */ + +#ifndef PRINTF_INT64_HEX_WIDTH +# define PRINTF_INT64_HEX_WIDTH "16" +#endif +#ifndef PRINTF_INT32_HEX_WIDTH +# define PRINTF_INT32_HEX_WIDTH "8" +#endif +#ifndef PRINTF_INT16_HEX_WIDTH +# define PRINTF_INT16_HEX_WIDTH "4" +#endif +#ifndef PRINTF_INT8_HEX_WIDTH +# define PRINTF_INT8_HEX_WIDTH "2" +#endif +#ifndef PRINTF_INT64_DEC_WIDTH +# define PRINTF_INT64_DEC_WIDTH "19" +#endif +#ifndef PRINTF_INT32_DEC_WIDTH +# define PRINTF_INT32_DEC_WIDTH "10" +#endif +#ifndef PRINTF_INT16_DEC_WIDTH +# define PRINTF_INT16_DEC_WIDTH "5" +#endif +#ifndef PRINTF_INT8_DEC_WIDTH +# define PRINTF_INT8_DEC_WIDTH "3" +#endif +#ifndef PRINTF_UINT64_DEC_WIDTH +# define PRINTF_UINT64_DEC_WIDTH "20" +#endif +#ifndef PRINTF_UINT32_DEC_WIDTH +# define PRINTF_UINT32_DEC_WIDTH "10" +#endif +#ifndef PRINTF_UINT16_DEC_WIDTH +# define PRINTF_UINT16_DEC_WIDTH "5" +#endif +#ifndef PRINTF_UINT8_DEC_WIDTH +# define PRINTF_UINT8_DEC_WIDTH "3" +#endif + +/* + * Ok, lets not worry about 128 bit integers for now. Moore's law says + * we don't need to worry about that until about 2040 at which point + * we'll have bigger things to worry about. + */ + +#ifdef stdint_int64_defined + typedef int64_t intmax_t; + typedef uint64_t uintmax_t; +# define INTMAX_MAX INT64_MAX +# define INTMAX_MIN INT64_MIN +# define UINTMAX_MAX UINT64_MAX +# define UINTMAX_C(v) UINT64_C(v) +# define INTMAX_C(v) INT64_C(v) +# ifndef PRINTF_INTMAX_MODIFIER +# define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER +# endif +# ifndef PRINTF_INTMAX_HEX_WIDTH +# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT64_HEX_WIDTH +# endif +# ifndef PRINTF_INTMAX_DEC_WIDTH +# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT64_DEC_WIDTH +# endif +#else + typedef int32_t intmax_t; + typedef uint32_t uintmax_t; +# define INTMAX_MAX INT32_MAX +# define UINTMAX_MAX UINT32_MAX +# define UINTMAX_C(v) UINT32_C(v) +# define INTMAX_C(v) INT32_C(v) +# ifndef PRINTF_INTMAX_MODIFIER +# define PRINTF_INTMAX_MODIFIER PRINTF_INT32_MODIFIER +# endif +# ifndef PRINTF_INTMAX_HEX_WIDTH +# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT32_HEX_WIDTH +# endif +# ifndef PRINTF_INTMAX_DEC_WIDTH +# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT32_DEC_WIDTH +# endif +#endif + +/* + * Because this file currently only supports platforms which have + * precise powers of 2 as bit sizes for the default integers, the + * least definitions are all trivial. Its possible that a future + * version of this file could have different definitions. + */ + +#ifndef stdint_least_defined + typedef int8_t int_least8_t; + typedef uint8_t uint_least8_t; + typedef int16_t int_least16_t; + typedef uint16_t uint_least16_t; + typedef int32_t int_least32_t; + typedef uint32_t uint_least32_t; +# define PRINTF_LEAST32_MODIFIER PRINTF_INT32_MODIFIER +# define PRINTF_LEAST16_MODIFIER PRINTF_INT16_MODIFIER +# define UINT_LEAST8_MAX UINT8_MAX +# define INT_LEAST8_MAX INT8_MAX +# define UINT_LEAST16_MAX UINT16_MAX +# define INT_LEAST16_MAX INT16_MAX +# define UINT_LEAST32_MAX UINT32_MAX +# define INT_LEAST32_MAX INT32_MAX +# define INT_LEAST8_MIN INT8_MIN +# define INT_LEAST16_MIN INT16_MIN +# define INT_LEAST32_MIN INT32_MIN +# ifdef stdint_int64_defined + typedef int64_t int_least64_t; + typedef uint64_t uint_least64_t; +# define PRINTF_LEAST64_MODIFIER PRINTF_INT64_MODIFIER +# define UINT_LEAST64_MAX UINT64_MAX +# define INT_LEAST64_MAX INT64_MAX +# define INT_LEAST64_MIN INT64_MIN +# endif +#endif +#undef stdint_least_defined + +/* + * The ANSI C committee pretending to know or specify anything about + * performance is the epitome of misguided arrogance. The mandate of + * this file is to *ONLY* ever support that absolute minimum + * definition of the fast integer types, for compatibility purposes. + * No extensions, and no attempt to suggest what may or may not be a + * faster integer type will ever be made in this file. Developers are + * warned to stay away from these types when using this or any other + * stdint.h. + */ + +typedef int_least8_t int_fast8_t; +typedef uint_least8_t uint_fast8_t; +typedef int_least16_t int_fast16_t; +typedef uint_least16_t uint_fast16_t; +typedef int_least32_t int_fast32_t; +typedef uint_least32_t uint_fast32_t; +#define UINT_FAST8_MAX UINT_LEAST8_MAX +#define INT_FAST8_MAX INT_LEAST8_MAX +#define UINT_FAST16_MAX UINT_LEAST16_MAX +#define INT_FAST16_MAX INT_LEAST16_MAX +#define UINT_FAST32_MAX UINT_LEAST32_MAX +#define INT_FAST32_MAX INT_LEAST32_MAX +#define INT_FAST8_MIN INT_LEAST8_MIN +#define INT_FAST16_MIN INT_LEAST16_MIN +#define INT_FAST32_MIN INT_LEAST32_MIN +#ifdef stdint_int64_defined + typedef int_least64_t int_fast64_t; + typedef uint_least64_t uint_fast64_t; +# define UINT_FAST64_MAX UINT_LEAST64_MAX +# define INT_FAST64_MAX INT_LEAST64_MAX +# define INT_FAST64_MIN INT_LEAST64_MIN +#endif + +#undef stdint_int64_defined + +/* + * Whatever piecemeal, per compiler thing we can do about the wchar_t + * type limits. + */ + +#if defined(__WATCOMC__) || defined(_MSC_VER) || defined (__GNUC__) && !defined(vxWorks) +# include +# ifndef WCHAR_MIN +# define WCHAR_MIN 0 +# endif +# ifndef WCHAR_MAX +# define WCHAR_MAX ((wchar_t)-1) +# endif +#endif + +/* + * Whatever piecemeal, per compiler/platform thing we can do about the + * (u)intptr_t types and limits. + */ + +#if (defined (_MSC_VER) && defined (_UINTPTR_T_DEFINED)) || defined (_UINTPTR_T) +# define STDINT_H_UINTPTR_T_DEFINED +#endif + +#ifndef STDINT_H_UINTPTR_T_DEFINED +# if defined (__alpha__) || defined (__ia64__) || defined (__x86_64__) || defined (_WIN64) || defined (__ppc64__) +# define stdint_intptr_bits 64 +# elif defined (__WATCOMC__) || defined (__TURBOC__) +# if defined(__TINY__) || defined(__SMALL__) || defined(__MEDIUM__) +# define stdint_intptr_bits 16 +# else +# define stdint_intptr_bits 32 +# endif +# elif defined (__i386__) || defined (_WIN32) || defined (WIN32) || defined (__ppc64__) +# define stdint_intptr_bits 32 +# elif defined (__INTEL_COMPILER) +/* TODO -- what did Intel do about x86-64? */ +# else +/* #error "This platform might not be supported yet" */ +# endif + +# ifdef stdint_intptr_bits +# define stdint_intptr_glue3_i(a,b,c) a##b##c +# define stdint_intptr_glue3(a,b,c) stdint_intptr_glue3_i(a,b,c) +# ifndef PRINTF_INTPTR_MODIFIER +# define PRINTF_INTPTR_MODIFIER stdint_intptr_glue3(PRINTF_INT,stdint_intptr_bits,_MODIFIER) +# endif +# ifndef PTRDIFF_MAX +# define PTRDIFF_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX) +# endif +# ifndef PTRDIFF_MIN +# define PTRDIFF_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN) +# endif +# ifndef UINTPTR_MAX +# define UINTPTR_MAX stdint_intptr_glue3(UINT,stdint_intptr_bits,_MAX) +# endif +# ifndef INTPTR_MAX +# define INTPTR_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX) +# endif +# ifndef INTPTR_MIN +# define INTPTR_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN) +# endif +# ifndef INTPTR_C +# define INTPTR_C(x) stdint_intptr_glue3(INT,stdint_intptr_bits,_C)(x) +# endif +# ifndef UINTPTR_C +# define UINTPTR_C(x) stdint_intptr_glue3(UINT,stdint_intptr_bits,_C)(x) +# endif + typedef stdint_intptr_glue3(uint,stdint_intptr_bits,_t) uintptr_t; + typedef stdint_intptr_glue3( int,stdint_intptr_bits,_t) intptr_t; +# else +/* TODO -- This following is likely wrong for some platforms, and does + nothing for the definition of uintptr_t. */ + typedef ptrdiff_t intptr_t; +# endif +# define STDINT_H_UINTPTR_T_DEFINED +#endif + +/* + * Assumes sig_atomic_t is signed and we have a 2s complement machine. + */ + +#ifndef SIG_ATOMIC_MAX +# define SIG_ATOMIC_MAX ((((sig_atomic_t) 1) << (sizeof (sig_atomic_t)*CHAR_BIT-1)) - 1) +#endif + +#endif + +#if defined (__TEST_PSTDINT_FOR_CORRECTNESS) + +/* + * Please compile with the maximum warning settings to make sure macros are + * not defined more than once. + */ + +#include +#include +#include + +#define glue3_aux(x,y,z) x ## y ## z +#define glue3(x,y,z) glue3_aux(x,y,z) + +#define DECLU(bits) glue3(uint,bits,_t) glue3(u,bits,) = glue3(UINT,bits,_C) (0); +#define DECLI(bits) glue3(int,bits,_t) glue3(i,bits,) = glue3(INT,bits,_C) (0); + +#define DECL(us,bits) glue3(DECL,us,) (bits) + +#define TESTUMAX(bits) glue3(u,bits,) = ~glue3(u,bits,); if (glue3(UINT,bits,_MAX) != glue3(u,bits,)) printf ("Something wrong with UINT%d_MAX\n", bits) + +#define REPORTERROR(msg) { err_n++; if (err_first <= 0) err_first = __LINE__; printf msg; } + +int main () { + int err_n = 0; + int err_first = 0; + DECL(I,8) + DECL(U,8) + DECL(I,16) + DECL(U,16) + DECL(I,32) + DECL(U,32) +#ifdef INT64_MAX + DECL(I,64) + DECL(U,64) +#endif + intmax_t imax = INTMAX_C(0); + uintmax_t umax = UINTMAX_C(0); + char str0[256], str1[256]; + + sprintf (str0, "%" PRINTF_INT32_MODIFIER "d", INT32_C(2147483647)); + if (0 != strcmp (str0, "2147483647")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str0)); + if (atoi(PRINTF_INT32_DEC_WIDTH) != (int) strlen(str0)) REPORTERROR (("Something wrong with PRINTF_INT32_DEC_WIDTH : %s\n", PRINTF_INT32_DEC_WIDTH)); + sprintf (str0, "%" PRINTF_INT32_MODIFIER "u", UINT32_C(4294967295)); + if (0 != strcmp (str0, "4294967295")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str0)); + if (atoi(PRINTF_UINT32_DEC_WIDTH) != (int) strlen(str0)) REPORTERROR (("Something wrong with PRINTF_UINT32_DEC_WIDTH : %s\n", PRINTF_UINT32_DEC_WIDTH)); +#ifdef INT64_MAX + sprintf (str1, "%" PRINTF_INT64_MODIFIER "d", INT64_C(9223372036854775807)); + if (0 != strcmp (str1, "9223372036854775807")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str1)); + if (atoi(PRINTF_INT64_DEC_WIDTH) != (int) strlen(str1)) REPORTERROR (("Something wrong with PRINTF_INT64_DEC_WIDTH : %s, %d\n", PRINTF_INT64_DEC_WIDTH, (int) strlen(str1))); + sprintf (str1, "%" PRINTF_INT64_MODIFIER "u", UINT64_C(18446744073709550591)); + if (0 != strcmp (str1, "18446744073709550591")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str1)); + if (atoi(PRINTF_UINT64_DEC_WIDTH) != (int) strlen(str1)) REPORTERROR (("Something wrong with PRINTF_UINT64_DEC_WIDTH : %s, %d\n", PRINTF_UINT64_DEC_WIDTH, (int) strlen(str1))); +#endif + + sprintf (str0, "%d %x\n", 0, ~0); + + sprintf (str1, "%d %x\n", i8, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i8 : %s\n", str1)); + sprintf (str1, "%u %x\n", u8, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with u8 : %s\n", str1)); + sprintf (str1, "%d %x\n", i16, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i16 : %s\n", str1)); + sprintf (str1, "%u %x\n", u16, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with u16 : %s\n", str1)); + sprintf (str1, "%" PRINTF_INT32_MODIFIER "d %x\n", i32, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i32 : %s\n", str1)); + sprintf (str1, "%" PRINTF_INT32_MODIFIER "u %x\n", u32, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with u32 : %s\n", str1)); +#ifdef INT64_MAX + sprintf (str1, "%" PRINTF_INT64_MODIFIER "d %x\n", i64, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i64 : %s\n", str1)); +#endif + sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "d %x\n", imax, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with imax : %s\n", str1)); + sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "u %x\n", umax, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with umax : %s\n", str1)); + + TESTUMAX(8); + TESTUMAX(16); + TESTUMAX(32); +#ifdef INT64_MAX + TESTUMAX(64); +#endif + +#define STR(v) #v +#define Q(v) printf ("sizeof " STR(v) " = %u\n", (unsigned) sizeof (v)); + if (err_n) { + printf ("pstdint.h is not correct. Please use sizes below to correct it:\n"); + } + + Q(int) + Q(unsigned) + Q(long int) + Q(short int) + Q(int8_t) + Q(int16_t) + Q(int32_t) +#ifdef INT64_MAX + Q(int64_t) +#endif + + return EXIT_SUCCESS; +} + +#endif diff --git a/dependencies/assimp/include/assimp/Compiler/pushpack1.h b/dependencies/assimp/include/assimp/Compiler/pushpack1.h new file mode 100644 index 0000000..4c9fbb8 --- /dev/null +++ b/dependencies/assimp/include/assimp/Compiler/pushpack1.h @@ -0,0 +1,43 @@ + + +// =============================================================================== +// May be included multiple times - sets structure packing to 1 +// for all supported compilers. #include reverts the changes. +// +// Currently this works on the following compilers: +// MSVC 7,8,9 +// GCC +// BORLAND (complains about 'pack state changed but not reverted', but works) +// Clang +// +// +// USAGE: +// +// struct StructToBePacked { +// } PACK_STRUCT; +// +// =============================================================================== + +#ifdef AI_PUSHPACK_IS_DEFINED +# error poppack1.h must be included after pushpack1.h +#endif + +#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__) +# pragma pack(push,1) +# define PACK_STRUCT +#elif defined( __GNUC__ ) || defined(__clang__) +# if !defined(HOST_MINGW) +# define PACK_STRUCT __attribute__((__packed__)) +# else +# define PACK_STRUCT __attribute__((gcc_struct, __packed__)) +# endif +#else +# error Compiler not supported +#endif + +#if defined(_MSC_VER) +// C4103: Packing was changed after the inclusion of the header, probably missing #pragma pop +# pragma warning (disable : 4103) +#endif + +#define AI_PUSHPACK_IS_DEFINED diff --git a/dependencies/assimp/include/assimp/CreateAnimMesh.h b/dependencies/assimp/include/assimp/CreateAnimMesh.h new file mode 100644 index 0000000..a601735 --- /dev/null +++ b/dependencies/assimp/include/assimp/CreateAnimMesh.h @@ -0,0 +1,58 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file CreateAnimMesh.h + * Create AnimMesh from Mesh + */ +#ifndef INCLUDED_AI_CREATE_ANIM_MESH_H +#define INCLUDED_AI_CREATE_ANIM_MESH_H + +#include + +namespace Assimp { + +/** Create aiAnimMesh from aiMesh. */ +ASSIMP_API aiAnimMesh *aiCreateAnimMesh(const aiMesh *mesh); + +} // end of namespace Assimp +#endif // INCLUDED_AI_CREATE_ANIM_MESH_H + diff --git a/dependencies/assimp/include/assimp/DefaultIOStream.h b/dependencies/assimp/include/assimp/DefaultIOStream.h new file mode 100644 index 0000000..994d728 --- /dev/null +++ b/dependencies/assimp/include/assimp/DefaultIOStream.h @@ -0,0 +1,140 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Default file I/O using fXXX()-family of functions */ +#ifndef AI_DEFAULTIOSTREAM_H_INC +#define AI_DEFAULTIOSTREAM_H_INC + +#include +#include +#include +#include + +namespace Assimp { + +// ---------------------------------------------------------------------------------- +//! @class DefaultIOStream +//! @brief Default IO implementation, use standard IO operations +//! @note An instance of this class can exist without a valid file handle +//! attached to it. All calls fail, but the instance can nevertheless be +//! used with no restrictions. +class ASSIMP_API DefaultIOStream : public IOStream +{ + friend class DefaultIOSystem; +#if __ANDROID__ +# if __ANDROID_API__ > 9 +# if defined(AI_CONFIG_ANDROID_JNI_ASSIMP_MANAGER_SUPPORT) + friend class AndroidJNIIOSystem; +# endif // defined(AI_CONFIG_ANDROID_JNI_ASSIMP_MANAGER_SUPPORT) +# endif // __ANDROID_API__ > 9 +#endif // __ANDROID__ + +protected: + DefaultIOStream() AI_NO_EXCEPT; + DefaultIOStream(FILE* pFile, const std::string &strFilename); + +public: + /** Destructor public to allow simple deletion to close the file. */ + ~DefaultIOStream (); + + // ------------------------------------------------------------------- + /// Read from stream + size_t Read(void* pvBuffer, + size_t pSize, + size_t pCount); + + + // ------------------------------------------------------------------- + /// Write to stream + size_t Write(const void* pvBuffer, + size_t pSize, + size_t pCount); + + // ------------------------------------------------------------------- + /// Seek specific position + aiReturn Seek(size_t pOffset, + aiOrigin pOrigin); + + // ------------------------------------------------------------------- + /// Get current seek position + size_t Tell() const; + + // ------------------------------------------------------------------- + /// Get size of file + size_t FileSize() const; + + // ------------------------------------------------------------------- + /// Flush file contents + void Flush(); + +private: + // File data-structure, using clib + FILE* mFile; + // Filename + std::string mFilename; + // Cached file size + mutable size_t mCachedSize; +}; + +// ---------------------------------------------------------------------------------- +inline +DefaultIOStream::DefaultIOStream() AI_NO_EXCEPT +: mFile(nullptr) +, mFilename("") +, mCachedSize(SIZE_MAX) { + // empty +} + +// ---------------------------------------------------------------------------------- +inline +DefaultIOStream::DefaultIOStream (FILE* pFile, const std::string &strFilename) +: mFile(pFile) +, mFilename(strFilename) +, mCachedSize(SIZE_MAX) { + // empty +} +// ---------------------------------------------------------------------------------- + +} // ns assimp + +#endif //!!AI_DEFAULTIOSTREAM_H_INC + diff --git a/dependencies/assimp/include/assimp/DefaultIOSystem.h b/dependencies/assimp/include/assimp/DefaultIOSystem.h new file mode 100644 index 0000000..2dd5c80 --- /dev/null +++ b/dependencies/assimp/include/assimp/DefaultIOSystem.h @@ -0,0 +1,93 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Default implementation of IOSystem using the standard C file functions */ +#ifndef AI_DEFAULTIOSYSTEM_H_INC +#define AI_DEFAULTIOSYSTEM_H_INC + +#include + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** Default implementation of IOSystem using the standard C file functions */ +class ASSIMP_API DefaultIOSystem : public IOSystem { +public: + // ------------------------------------------------------------------- + /** Tests for the existence of a file at the given path. */ + bool Exists( const char* pFile) const; + + // ------------------------------------------------------------------- + /** Returns the directory separator. */ + char getOsSeparator() const; + + // ------------------------------------------------------------------- + /** Open a new file with a given path. */ + IOStream* Open( const char* pFile, const char* pMode = "rb"); + + // ------------------------------------------------------------------- + /** Closes the given file and releases all resources associated with it. */ + void Close( IOStream* pFile); + + // ------------------------------------------------------------------- + /** Compare two paths */ + bool ComparePaths (const char* one, const char* second) const; + + /** @brief get the file name of a full filepath + * example: /tmp/archive.tar.gz -> archive.tar.gz + */ + static std::string fileName( const std::string &path ); + + /** @brief get the complete base name of a full filepath + * example: /tmp/archive.tar.gz -> archive.tar + */ + static std::string completeBaseName( const std::string &path); + + /** @brief get the path of a full filepath + * example: /tmp/archive.tar.gz -> /tmp/ + */ + static std::string absolutePath( const std::string &path); +}; + +} //!ns Assimp + +#endif //AI_DEFAULTIOSYSTEM_H_INC diff --git a/dependencies/assimp/include/assimp/DefaultLogger.hpp b/dependencies/assimp/include/assimp/DefaultLogger.hpp new file mode 100644 index 0000000..1946e25 --- /dev/null +++ b/dependencies/assimp/include/assimp/DefaultLogger.hpp @@ -0,0 +1,188 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/** @file DefaultLogger.hpp +*/ + +#ifndef INCLUDED_AI_DEFAULTLOGGER +#define INCLUDED_AI_DEFAULTLOGGER + +#include "Logger.hpp" +#include "LogStream.hpp" +#include "NullLogger.hpp" +#include + +namespace Assimp { +// ------------------------------------------------------------------------------------ +class IOStream; +struct LogStreamInfo; + +/** default name of logfile */ +#define ASSIMP_DEFAULT_LOG_NAME "AssimpLog.txt" + +// ------------------------------------------------------------------------------------ +/** @brief CPP-API: Primary logging facility of Assimp. + * + * The library stores its primary #Logger as a static member of this class. + * #get() returns this primary logger. By default the underlying implementation is + * just a #NullLogger which rejects all log messages. By calling #create(), logging + * is turned on. To capture the log output multiple log streams (#LogStream) can be + * attach to the logger. Some default streams for common streaming locations (such as + * a file, std::cout, OutputDebugString()) are also provided. + * + * If you wish to customize the logging at an even deeper level supply your own + * implementation of #Logger to #set(). + * @note The whole logging stuff causes a small extra overhead for all imports. */ +class ASSIMP_API DefaultLogger : + public Logger { + +public: + + // ---------------------------------------------------------------------- + /** @brief Creates a logging instance. + * @param name Name for log file. Only valid in combination + * with the aiDefaultLogStream_FILE flag. + * @param severity Log severity, VERBOSE turns on debug messages + * @param defStreams Default log streams to be attached. Any bitwise + * combination of the aiDefaultLogStream enumerated values. + * If #aiDefaultLogStream_FILE is specified but an empty string is + * passed for 'name', no log file is created at all. + * @param io IOSystem to be used to open external files (such as the + * log file). Pass NULL to rely on the default implementation. + * This replaces the default #NullLogger with a #DefaultLogger instance. */ + static Logger *create(const char* name = ASSIMP_DEFAULT_LOG_NAME, + LogSeverity severity = NORMAL, + unsigned int defStreams = aiDefaultLogStream_DEBUGGER | aiDefaultLogStream_FILE, + IOSystem* io = NULL); + + // ---------------------------------------------------------------------- + /** @brief Setup a custom #Logger implementation. + * + * Use this if the provided #DefaultLogger class doesn't fit into + * your needs. If the provided message formatting is OK for you, + * it's much easier to use #create() and to attach your own custom + * output streams to it. + * @param logger Pass NULL to setup a default NullLogger*/ + static void set (Logger *logger); + + // ---------------------------------------------------------------------- + /** @brief Getter for singleton instance + * @return Only instance. This is never null, but it could be a + * NullLogger. Use isNullLogger to check this.*/ + static Logger *get(); + + // ---------------------------------------------------------------------- + /** @brief Return whether a #NullLogger is currently active + * @return true if the current logger is a #NullLogger. + * Use create() or set() to setup a logger that does actually do + * something else than just rejecting all log messages. */ + static bool isNullLogger(); + + // ---------------------------------------------------------------------- + /** @brief Kills the current singleton logger and replaces it with a + * #NullLogger instance. */ + static void kill(); + + // ---------------------------------------------------------------------- + /** @copydoc Logger::attachStream */ + bool attachStream(LogStream *pStream, + unsigned int severity); + + // ---------------------------------------------------------------------- + /** @copydoc Logger::detatchStream */ + bool detatchStream(LogStream *pStream, + unsigned int severity); + +private: + // ---------------------------------------------------------------------- + /** @briefPrivate construction for internal use by create(). + * @param severity Logging granularity */ + explicit DefaultLogger(LogSeverity severity); + + // ---------------------------------------------------------------------- + /** @briefDestructor */ + ~DefaultLogger(); + + /** @brief Logs debug infos, only been written when severity level VERBOSE is set */ + void OnDebug(const char* message); + + /** @brief Logs an info message */ + void OnInfo(const char* message); + + /** @brief Logs a warning message */ + void OnWarn(const char* message); + + /** @brief Logs an error message */ + void OnError(const char* message); + + // ---------------------------------------------------------------------- + /** @brief Writes a message to all streams */ + void WriteToStreams(const char* message, ErrorSeverity ErrorSev ); + + // ---------------------------------------------------------------------- + /** @brief Returns the thread id. + * @note This is an OS specific feature, if not supported, a + * zero will be returned. + */ + unsigned int GetThreadID(); + +private: + // Aliases for stream container + typedef std::vector StreamArray; + typedef std::vector::iterator StreamIt; + typedef std::vector::const_iterator ConstStreamIt; + + //! only logging instance + static Logger *m_pLogger; + static NullLogger s_pNullLogger; + + //! Attached streams + StreamArray m_StreamArray; + + bool noRepeatMsg; + char lastMsg[MAX_LOG_MESSAGE_LENGTH*2]; + size_t lastLen; +}; +// ------------------------------------------------------------------------------------ + +} // Namespace Assimp + +#endif // !! INCLUDED_AI_DEFAULTLOGGER diff --git a/dependencies/assimp/include/assimp/Defines.h b/dependencies/assimp/include/assimp/Defines.h new file mode 100644 index 0000000..15e1d83 --- /dev/null +++ b/dependencies/assimp/include/assimp/Defines.h @@ -0,0 +1,49 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +// We need those constants, workaround for any platforms where nobody defined them yet +#if (!defined SIZE_MAX) +# define SIZE_MAX (~((size_t)0)) +#endif + +#if (!defined UINT_MAX) +# define UINT_MAX (~((unsigned int)0)) +#endif + diff --git a/dependencies/assimp/include/assimp/Exceptional.h b/dependencies/assimp/include/assimp/Exceptional.h new file mode 100644 index 0000000..5109b8f --- /dev/null +++ b/dependencies/assimp/include/assimp/Exceptional.h @@ -0,0 +1,125 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2008, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#ifndef INCLUDED_EXCEPTIONAL_H +#define INCLUDED_EXCEPTIONAL_H + +#include +#include +using std::runtime_error; + +#ifdef _MSC_VER +# pragma warning(disable : 4275) +#endif + +// --------------------------------------------------------------------------- +/** FOR IMPORTER PLUGINS ONLY: Simple exception class to be thrown if an + * unrecoverable error occurs while importing. Loading APIs return + * NULL instead of a valid aiScene then. */ +class DeadlyImportError + : public runtime_error +{ +public: + /** Constructor with arguments */ + explicit DeadlyImportError( const std::string& errorText) + : runtime_error(errorText) + { + } + +private: +}; + +typedef DeadlyImportError DeadlyExportError; + +#ifdef _MSC_VER +# pragma warning(default : 4275) +#endif + +// --------------------------------------------------------------------------- +template +struct ExceptionSwallower { + T operator ()() const { + return T(); + } +}; + +// --------------------------------------------------------------------------- +template +struct ExceptionSwallower { + T* operator ()() const { + return NULL; + } +}; + +// --------------------------------------------------------------------------- +template <> +struct ExceptionSwallower { + aiReturn operator ()() const { + try { + throw; + } + catch (std::bad_alloc&) { + return aiReturn_OUTOFMEMORY; + } + catch (...) { + return aiReturn_FAILURE; + } + } +}; + +// --------------------------------------------------------------------------- +template <> +struct ExceptionSwallower { + void operator ()() const { + return; + } +}; + +#define ASSIMP_BEGIN_EXCEPTION_REGION()\ +{\ + try { + +#define ASSIMP_END_EXCEPTION_REGION(type)\ + } catch(...) {\ + return ExceptionSwallower()();\ + }\ +} + +#endif // INCLUDED_EXCEPTIONAL_H diff --git a/dependencies/assimp/include/assimp/Exporter.hpp b/dependencies/assimp/include/assimp/Exporter.hpp new file mode 100644 index 0000000..ea0303e --- /dev/null +++ b/dependencies/assimp/include/assimp/Exporter.hpp @@ -0,0 +1,505 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Exporter.hpp +* @brief Defines the CPP-API for the Assimp export interface +*/ +#pragma once +#ifndef AI_EXPORT_HPP_INC +#define AI_EXPORT_HPP_INC + +#ifndef ASSIMP_BUILD_NO_EXPORT + +#include "cexport.h" +#include + +namespace Assimp { + +class ExporterPimpl; +class IOSystem; +class ProgressHandler; + +// ---------------------------------------------------------------------------------- +/** CPP-API: The Exporter class forms an C++ interface to the export functionality + * of the Open Asset Import Library. Note that the export interface is available + * only if Assimp has been built with ASSIMP_BUILD_NO_EXPORT not defined. + * + * The interface is modeled after the importer interface and mostly + * symmetric. The same rules for threading etc. apply. + * + * In a nutshell, there are two export interfaces: #Export, which writes the + * output file(s) either to the regular file system or to a user-supplied + * #IOSystem, and #ExportToBlob which returns a linked list of memory + * buffers (blob), each referring to one output file (in most cases + * there will be only one output file of course, but this extra complexity is + * needed since Assimp aims at supporting a wide range of file formats). + * + * #ExportToBlob is especially useful if you intend to work + * with the data in-memory. +*/ +class ASSIMP_API ExportProperties; + +class ASSIMP_API Exporter { +public: + /** Function pointer type of a Export worker function */ + typedef void (*fpExportFunc)(const char*, IOSystem*, const aiScene*, const ExportProperties*); + + /** Internal description of an Assimp export format option */ + struct ExportFormatEntry { + /// Public description structure to be returned by aiGetExportFormatDescription() + aiExportFormatDesc mDescription; + + // Worker function to do the actual exporting + fpExportFunc mExportFunction; + + // Post-processing steps to be executed PRIOR to invoking mExportFunction + unsigned int mEnforcePP; + + // Constructor to fill all entries + ExportFormatEntry( const char* pId, const char* pDesc, const char* pExtension, fpExportFunc pFunction, unsigned int pEnforcePP = 0u) + { + mDescription.id = pId; + mDescription.description = pDesc; + mDescription.fileExtension = pExtension; + mExportFunction = pFunction; + mEnforcePP = pEnforcePP; + } + + ExportFormatEntry() : + mExportFunction() + , mEnforcePP() + { + mDescription.id = NULL; + mDescription.description = NULL; + mDescription.fileExtension = NULL; + } + }; + + /** + * @brief The class constructor. + */ + Exporter(); + + /** + * @brief The class destructor. + */ + ~Exporter(); + + // ------------------------------------------------------------------- + /** Supplies a custom IO handler to the exporter to use to open and + * access files. + * + * If you need #Export to use custom IO logic to access the files, + * you need to supply a custom implementation of IOSystem and + * IOFile to the exporter. + * + * #Exporter takes ownership of the object and will destroy it + * afterwards. The previously assigned handler will be deleted. + * Pass NULL to take again ownership of your IOSystem and reset Assimp + * to use its default implementation, which uses plain file IO. + * + * @param pIOHandler The IO handler to be used in all file accesses + * of the Importer. */ + void SetIOHandler( IOSystem* pIOHandler); + + // ------------------------------------------------------------------- + /** Retrieves the IO handler that is currently set. + * You can use #IsDefaultIOHandler() to check whether the returned + * interface is the default IO handler provided by ASSIMP. The default + * handler is active as long the application doesn't supply its own + * custom IO handler via #SetIOHandler(). + * @return A valid IOSystem interface, never NULL. */ + IOSystem* GetIOHandler() const; + + // ------------------------------------------------------------------- + /** Checks whether a default IO handler is active + * A default handler is active as long the application doesn't + * supply its own custom IO handler via #SetIOHandler(). + * @return true by default */ + bool IsDefaultIOHandler() const; + + // ------------------------------------------------------------------- + /** Supplies a custom progress handler to the exporter. This + * interface exposes an #Update() callback, which is called + * more or less periodically (please don't sue us if it + * isn't as periodically as you'd like it to have ...). + * This can be used to implement progress bars and loading + * timeouts. + * @param pHandler Progress callback interface. Pass nullptr to + * disable progress reporting. + * @note Progress handlers can be used to abort the loading + * at almost any time.*/ + void SetProgressHandler(ProgressHandler* pHandler); + + // ------------------------------------------------------------------- + /** Exports the given scene to a chosen file format. Returns the exported + * data as a binary blob which you can write into a file or something. + * When you're done with the data, simply let the #Exporter instance go + * out of scope to have it released automatically. + * @param pScene The scene to export. Stays in possession of the caller, + * is not changed by the function. + * @param pFormatId ID string to specify to which format you want to + * export to. Use + * #GetExportFormatCount / #GetExportFormatDescription to learn which + * export formats are available. + * @param pPreprocessing See the documentation for #Export + * @return the exported data or NULL in case of error. + * @note If the Exporter instance did already hold a blob from + * a previous call to #ExportToBlob, it will be disposed. + * Any IO handlers set via #SetIOHandler are ignored here. + * @note Use aiCopyScene() to get a modifiable copy of a previously + * imported scene. */ + const aiExportDataBlob* ExportToBlob(const aiScene* pScene, const char* pFormatId, + unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = nullptr); + const aiExportDataBlob* ExportToBlob( const aiScene* pScene, const std::string& pFormatId, + unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = nullptr); + + // ------------------------------------------------------------------- + /** Convenience function to export directly to a file. Use + * #SetIOSystem to supply a custom IOSystem to gain fine-grained control + * about the output data flow of the export process. + * @param pBlob A data blob obtained from a previous call to #aiExportScene. Must not be NULL. + * @param pPath Full target file name. Target must be accessible. + * @param pPreprocessing Accepts any choice of the #aiPostProcessSteps enumerated + * flags, but in reality only a subset of them makes sense here. Specifying + * 'preprocessing' flags is useful if the input scene does not conform to + * Assimp's default conventions as specified in the @link data Data Structures Page @endlink. + * In short, this means the geometry data should use a right-handed coordinate systems, face + * winding should be counter-clockwise and the UV coordinate origin is assumed to be in + * the upper left. The #aiProcess_MakeLeftHanded, #aiProcess_FlipUVs and + * #aiProcess_FlipWindingOrder flags are used in the import side to allow users + * to have those defaults automatically adapted to their conventions. Specifying those flags + * for exporting has the opposite effect, respectively. Some other of the + * #aiPostProcessSteps enumerated values may be useful as well, but you'll need + * to try out what their effect on the exported file is. Many formats impose + * their own restrictions on the structure of the geometry stored therein, + * so some preprocessing may have little or no effect at all, or may be + * redundant as exporters would apply them anyhow. A good example + * is triangulation - whilst you can enforce it by specifying + * the #aiProcess_Triangulate flag, most export formats support only + * triangulate data so they would run the step even if it wasn't requested. + * + * If assimp detects that the input scene was directly taken from the importer side of + * the library (i.e. not copied using aiCopyScene and potentially modified afterwards), + * any post-processing steps already applied to the scene will not be applied again, unless + * they show non-idempotent behavior (#aiProcess_MakeLeftHanded, #aiProcess_FlipUVs and + * #aiProcess_FlipWindingOrder). + * @return AI_SUCCESS if everything was fine. + * @note Use aiCopyScene() to get a modifiable copy of a previously + * imported scene.*/ + aiReturn Export( const aiScene* pScene, const char* pFormatId, const char* pPath, + unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = nullptr); + aiReturn Export( const aiScene* pScene, const std::string& pFormatId, const std::string& pPath, + unsigned int pPreprocessing = 0u, const ExportProperties* pProperties = nullptr); + + // ------------------------------------------------------------------- + /** Returns an error description of an error that occurred in #Export + * or #ExportToBlob + * + * Returns an empty string if no error occurred. + * @return A description of the last error, an empty string if no + * error occurred. The string is never NULL. + * + * @note The returned function remains valid until one of the + * following methods is called: #Export, #ExportToBlob, #FreeBlob */ + const char* GetErrorString() const; + + // ------------------------------------------------------------------- + /** Return the blob obtained from the last call to #ExportToBlob */ + const aiExportDataBlob* GetBlob() const; + + // ------------------------------------------------------------------- + /** Orphan the blob from the last call to #ExportToBlob. This means + * the caller takes ownership and is thus responsible for calling + * the C API function #aiReleaseExportBlob to release it. */ + const aiExportDataBlob* GetOrphanedBlob() const; + + // ------------------------------------------------------------------- + /** Frees the current blob. + * + * The function does nothing if no blob has previously been + * previously produced via #ExportToBlob. #FreeBlob is called + * automatically by the destructor. The only reason to call + * it manually would be to reclaim as much storage as possible + * without giving up the #Exporter instance yet. */ + void FreeBlob( ); + + // ------------------------------------------------------------------- + /** Returns the number of export file formats available in the current + * Assimp build. Use #Exporter::GetExportFormatDescription to + * retrieve infos of a specific export format. + * + * This includes built-in exporters as well as exporters registered + * using #RegisterExporter. + **/ + size_t GetExportFormatCount() const; + + // ------------------------------------------------------------------- + /** Returns a description of the nth export file format. Use # + * #Exporter::GetExportFormatCount to learn how many export + * formats are supported. + * + * The returned pointer is of static storage duration if the + * pIndex pertains to a built-in exporter (i.e. one not registered + * via #RegistrerExporter). It is restricted to the life-time of the + * #Exporter instance otherwise. + * + * @param pIndex Index of the export format to retrieve information + * for. Valid range is 0 to #Exporter::GetExportFormatCount + * @return A description of that specific export format. + * NULL if pIndex is out of range. */ + const aiExportFormatDesc* GetExportFormatDescription( size_t pIndex ) const; + + // ------------------------------------------------------------------- + /** Register a custom exporter. Custom export formats are limited to + * to the current #Exporter instance and do not affect the + * library globally. The indexes under which the format's + * export format description can be queried are assigned + * monotonously. + * @param desc Exporter description. + * @return aiReturn_SUCCESS if the export format was successfully + * registered. A common cause that would prevent an exporter + * from being registered is that its format id is already + * occupied by another format. */ + aiReturn RegisterExporter(const ExportFormatEntry& desc); + + // ------------------------------------------------------------------- + /** Remove an export format previously registered with #RegisterExporter + * from the #Exporter instance (this can also be used to drop + * built-in exporters because those are implicitly registered + * using #RegisterExporter). + * @param id Format id to be unregistered, this refers to the + * 'id' field of #aiExportFormatDesc. + * @note Calling this method on a format description not yet registered + * has no effect.*/ + void UnregisterExporter(const char* id); + +protected: + // Just because we don't want you to know how we're hacking around. + ExporterPimpl* pimpl; +}; + +class ASSIMP_API ExportProperties { +public: + // Data type to store the key hash + typedef unsigned int KeyType; + + // typedefs for our four configuration maps. + // We don't need more, so there is no need for a generic solution + typedef std::map IntPropertyMap; + typedef std::map FloatPropertyMap; + typedef std::map StringPropertyMap; + typedef std::map MatrixPropertyMap; + +public: + /** Standard constructor + * @see ExportProperties() + */ + ExportProperties(); + + // ------------------------------------------------------------------- + /** Copy constructor. + * + * This copies the configuration properties of another ExportProperties. + * @see ExportProperties(const ExportProperties& other) + */ + ExportProperties(const ExportProperties& other); + + // ------------------------------------------------------------------- + /** Set an integer configuration property. + * @param szName Name of the property. All supported properties + * are defined in the aiConfig.g header (all constants share the + * prefix AI_CONFIG_XXX and are simple strings). + * @param iValue New value of the property + * @return true if the property was set before. The new value replaces + * the previous value in this case. + * @note Property of different types (float, int, string ..) are kept + * on different stacks, so calling SetPropertyInteger() for a + * floating-point property has no effect - the loader will call + * GetPropertyFloat() to read the property, but it won't be there. + */ + bool SetPropertyInteger(const char* szName, int iValue); + + // ------------------------------------------------------------------- + /** Set a boolean configuration property. Boolean properties + * are stored on the integer stack internally so it's possible + * to set them via #SetPropertyBool and query them with + * #GetPropertyBool and vice versa. + * @see SetPropertyInteger() + */ + bool SetPropertyBool(const char* szName, bool value) { + return SetPropertyInteger(szName,value); + } + + // ------------------------------------------------------------------- + /** Set a floating-point configuration property. + * @see SetPropertyInteger() + */ + bool SetPropertyFloat(const char* szName, ai_real fValue); + + // ------------------------------------------------------------------- + /** Set a string configuration property. + * @see SetPropertyInteger() + */ + bool SetPropertyString(const char* szName, const std::string& sValue); + + // ------------------------------------------------------------------- + /** Set a matrix configuration property. + * @see SetPropertyInteger() + */ + bool SetPropertyMatrix(const char* szName, const aiMatrix4x4& sValue); + + // ------------------------------------------------------------------- + /** Get a configuration property. + * @param szName Name of the property. All supported properties + * are defined in the aiConfig.g header (all constants share the + * prefix AI_CONFIG_XXX). + * @param iErrorReturn Value that is returned if the property + * is not found. + * @return Current value of the property + * @note Property of different types (float, int, string ..) are kept + * on different lists, so calling SetPropertyInteger() for a + * floating-point property has no effect - the loader will call + * GetPropertyFloat() to read the property, but it won't be there. + */ + int GetPropertyInteger(const char* szName, + int iErrorReturn = 0xffffffff) const; + + // ------------------------------------------------------------------- + /** Get a boolean configuration property. Boolean properties + * are stored on the integer stack internally so it's possible + * to set them via #SetPropertyBool and query them with + * #GetPropertyBool and vice versa. + * @see GetPropertyInteger() + */ + bool GetPropertyBool(const char* szName, bool bErrorReturn = false) const { + return GetPropertyInteger(szName,bErrorReturn)!=0; + } + + // ------------------------------------------------------------------- + /** Get a floating-point configuration property + * @see GetPropertyInteger() + */ + ai_real GetPropertyFloat(const char* szName, + ai_real fErrorReturn = 10e10f) const; + + // ------------------------------------------------------------------- + /** Get a string configuration property + * + * The return value remains valid until the property is modified. + * @see GetPropertyInteger() + */ + const std::string GetPropertyString(const char* szName, + const std::string& sErrorReturn = "") const; + + // ------------------------------------------------------------------- + /** Get a matrix configuration property + * + * The return value remains valid until the property is modified. + * @see GetPropertyInteger() + */ + const aiMatrix4x4 GetPropertyMatrix(const char* szName, + const aiMatrix4x4& sErrorReturn = aiMatrix4x4()) const; + + // ------------------------------------------------------------------- + /** Determine a integer configuration property has been set. + * @see HasPropertyInteger() + */ + bool HasPropertyInteger(const char* szName) const; + + /** Determine a boolean configuration property has been set. + * @see HasPropertyBool() + */ + bool HasPropertyBool(const char* szName) const; + + /** Determine a boolean configuration property has been set. + * @see HasPropertyFloat() + */ + bool HasPropertyFloat(const char* szName) const; + + /** Determine a String configuration property has been set. + * @see HasPropertyString() + */ + bool HasPropertyString(const char* szName) const; + + /** Determine a Matrix configuration property has been set. + * @see HasPropertyMatrix() + */ + bool HasPropertyMatrix(const char* szName) const; + +protected: + + /** List of integer properties */ + IntPropertyMap mIntProperties; + + /** List of floating-point properties */ + FloatPropertyMap mFloatProperties; + + /** List of string properties */ + StringPropertyMap mStringProperties; + + /** List of Matrix properties */ + MatrixPropertyMap mMatrixProperties; +}; + +// ---------------------------------------------------------------------------------- +inline +const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const std::string& pFormatId, + unsigned int pPreprocessing, const ExportProperties* pProperties) +{ + return ExportToBlob(pScene,pFormatId.c_str(),pPreprocessing, pProperties); +} + +// ---------------------------------------------------------------------------------- +inline +aiReturn Exporter :: Export( const aiScene* pScene, const std::string& pFormatId, + const std::string& pPath, unsigned int pPreprocessing, + const ExportProperties* pProperties) +{ + return Export(pScene,pFormatId.c_str(),pPath.c_str(),pPreprocessing, pProperties); +} + +} // namespace Assimp + +#endif // ASSIMP_BUILD_NO_EXPORT +#endif // AI_EXPORT_HPP_INC diff --git a/dependencies/assimp/include/assimp/GenericProperty.h b/dependencies/assimp/include/assimp/GenericProperty.h new file mode 100644 index 0000000..183ecd5 --- /dev/null +++ b/dependencies/assimp/include/assimp/GenericProperty.h @@ -0,0 +1,133 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#ifndef AI_GENERIC_PROPERTY_H_INCLUDED +#define AI_GENERIC_PROPERTY_H_INCLUDED + +#include +#include +#include "Hash.h" + +#include + +// ------------------------------------------------------------------------------------------------ +template +inline +bool SetGenericProperty(std::map< unsigned int, T >& list, + const char* szName, const T& value) { + ai_assert(nullptr != szName); + const uint32_t hash = SuperFastHash(szName); + + typename std::map::iterator it = list.find(hash); + if (it == list.end()) { + list.insert(std::pair( hash, value )); + return false; + } + (*it).second = value; + + return true; +} + +// ------------------------------------------------------------------------------------------------ +template +inline +const T& GetGenericProperty(const std::map< unsigned int, T >& list, + const char* szName, const T& errorReturn) { + ai_assert(nullptr != szName); + const uint32_t hash = SuperFastHash(szName); + + typename std::map::const_iterator it = list.find(hash); + if (it == list.end()) { + return errorReturn; + } + + return (*it).second; +} + +// ------------------------------------------------------------------------------------------------ +// Special version for pointer types - they will be deleted when replaced with another value +// passing NULL removes the whole property +template +inline +void SetGenericPropertyPtr(std::map< unsigned int, T* >& list, + const char* szName, T* value, bool* bWasExisting = nullptr ) { + ai_assert(nullptr != szName); + const uint32_t hash = SuperFastHash(szName); + + typename std::map::iterator it = list.find(hash); + if (it == list.end()) { + if (bWasExisting) { + *bWasExisting = false; + } + + list.insert(std::pair( hash, value )); + return; + } + if ((*it).second != value) { + delete (*it).second; + (*it).second = value; + } + if (!value) { + list.erase(it); + } + if (bWasExisting) { + *bWasExisting = true; + } +} + +// ------------------------------------------------------------------------------------------------ +template +inline +bool HasGenericProperty(const std::map< unsigned int, T >& list, + const char* szName) { + ai_assert(nullptr != szName); + const uint32_t hash = SuperFastHash(szName); + + typename std::map::const_iterator it = list.find(hash); + if (it == list.end()) { + return false; + } + + return true; +} + +#endif // !! AI_GENERIC_PROPERTY_H_INCLUDED diff --git a/dependencies/assimp/include/assimp/Hash.h b/dependencies/assimp/include/assimp/Hash.h new file mode 100644 index 0000000..30657be --- /dev/null +++ b/dependencies/assimp/include/assimp/Hash.h @@ -0,0 +1,118 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#ifndef AI_HASH_H_INCLUDED +#define AI_HASH_H_INCLUDED + +#include +#include + +// ------------------------------------------------------------------------------------------------ +// Hashing function taken from +// http://www.azillionmonkeys.com/qed/hash.html +// (incremental version) +// +// This code is Copyright 2004-2008 by Paul Hsieh. It is used here in the belief that +// Assimp's license is considered compatible with Pauls's derivative license as specified +// on his web page. +// +// (stdint.h should have been been included here) +// ------------------------------------------------------------------------------------------------ +#undef get16bits +#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) +#define get16bits(d) (*((const uint16_t *) (d))) +#endif + +#if !defined (get16bits) +#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\ + +(uint32_t)(((const uint8_t *)(d))[0]) ) +#endif + +// ------------------------------------------------------------------------------------------------ +inline uint32_t SuperFastHash (const char * data, uint32_t len = 0, uint32_t hash = 0) { +uint32_t tmp; +int rem; + + if (!data) return 0; + if (!len)len = (uint32_t)::strlen(data); + + rem = len & 3; + len >>= 2; + + /* Main loop */ + for (;len > 0; len--) { + hash += get16bits (data); + tmp = (get16bits (data+2) << 11) ^ hash; + hash = (hash << 16) ^ tmp; + data += 2*sizeof (uint16_t); + hash += hash >> 11; + } + + /* Handle end cases */ + switch (rem) { + case 3: hash += get16bits (data); + hash ^= hash << 16; + hash ^= data[sizeof (uint16_t)] << 18; + hash += hash >> 11; + break; + case 2: hash += get16bits (data); + hash ^= hash << 11; + hash += hash >> 17; + break; + case 1: hash += *data; + hash ^= hash << 10; + hash += hash >> 1; + } + + /* Force "avalanching" of final 127 bits */ + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + + return hash; +} + +#endif // !! AI_HASH_H_INCLUDED diff --git a/dependencies/assimp/include/assimp/IOStream.hpp b/dependencies/assimp/include/assimp/IOStream.hpp new file mode 100644 index 0000000..0623d0f --- /dev/null +++ b/dependencies/assimp/include/assimp/IOStream.hpp @@ -0,0 +1,142 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +/** @file IOStream.hpp + * @brief File I/O wrappers for C++. + */ + +#pragma once +#ifndef AI_IOSTREAM_H_INC +#define AI_IOSTREAM_H_INC + +#include "types.h" + +#ifndef __cplusplus +# error This header requires C++ to be used. aiFileIO.h is the \ + corresponding C interface. +#endif + +namespace Assimp { + +// ---------------------------------------------------------------------------------- +/** @brief CPP-API: Class to handle file I/O for C++ + * + * Derive an own implementation from this interface to provide custom IO handling + * to the Importer. If you implement this interface, be sure to also provide an + * implementation for IOSystem that creates instances of your custom IO class. +*/ +class ASSIMP_API IOStream +#ifndef SWIG + : public Intern::AllocateFromAssimpHeap +#endif +{ +protected: + /** Constructor protected, use IOSystem::Open() to create an instance. */ + IOStream() AI_NO_EXCEPT; + +public: + // ------------------------------------------------------------------- + /** @brief Destructor. Deleting the object closes the underlying file, + * alternatively you may use IOSystem::Close() to release the file. + */ + virtual ~IOStream(); + + // ------------------------------------------------------------------- + /** @brief Read from the file + * + * See fread() for more details + * This fails for write-only files */ + virtual size_t Read(void* pvBuffer, + size_t pSize, + size_t pCount) = 0; + + // ------------------------------------------------------------------- + /** @brief Write to the file + * + * See fwrite() for more details + * This fails for read-only files */ + virtual size_t Write(const void* pvBuffer, + size_t pSize, + size_t pCount) = 0; + + // ------------------------------------------------------------------- + /** @brief Set the read/write cursor of the file + * + * Note that the offset is _negative_ for aiOrigin_END. + * See fseek() for more details */ + virtual aiReturn Seek(size_t pOffset, + aiOrigin pOrigin) = 0; + + // ------------------------------------------------------------------- + /** @brief Get the current position of the read/write cursor + * + * See ftell() for more details */ + virtual size_t Tell() const = 0; + + // ------------------------------------------------------------------- + /** @brief Returns filesize + * Returns the filesize. */ + virtual size_t FileSize() const = 0; + + // ------------------------------------------------------------------- + /** @brief Flush the contents of the file buffer (for writers) + * See fflush() for more details. + */ + virtual void Flush() = 0; +}; //! class IOStream + +// ---------------------------------------------------------------------------------- +inline +IOStream::IOStream() AI_NO_EXCEPT { + // empty +} + +// ---------------------------------------------------------------------------------- +inline +IOStream::~IOStream() { + // empty +} +// ---------------------------------------------------------------------------------- + +} //!namespace Assimp + +#endif //!!AI_IOSTREAM_H_INC diff --git a/dependencies/assimp/include/assimp/IOStreamBuffer.h b/dependencies/assimp/include/assimp/IOStreamBuffer.h new file mode 100644 index 0000000..58abd97 --- /dev/null +++ b/dependencies/assimp/include/assimp/IOStreamBuffer.h @@ -0,0 +1,355 @@ +#pragma once + +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#include +#include + +#include "ParsingUtils.h" + +#include + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** + * Implementation of a cached stream buffer. + */ +template +class IOStreamBuffer { +public: + /// @brief The class constructor. + IOStreamBuffer( size_t cache = 4096 * 4096 ); + + /// @brief The class destructor. + ~IOStreamBuffer(); + + /// @brief Will open the cached access for a given stream. + /// @param stream The stream to cache. + /// @return true if successful. + bool open( IOStream *stream ); + + /// @brief Will close the cached access. + /// @return true if successful. + bool close(); + + /// @brief Returns the file-size. + /// @return The file-size. + size_t size() const; + + /// @brief Returns the cache size. + /// @return The cache size. + size_t cacheSize() const; + + /// @brief Will read the next block. + /// @return true if successful. + bool readNextBlock(); + + /// @brief Returns the number of blocks to read. + /// @return The number of blocks. + size_t getNumBlocks() const; + + /// @brief Returns the current block index. + /// @return The current block index. + size_t getCurrentBlockIndex() const; + + /// @brief Returns the current file pos. + /// @return The current file pos. + size_t getFilePos() const; + + /// @brief Will read the next line. + /// @param buffer The buffer for the next line. + /// @return true if successful. + bool getNextDataLine( std::vector &buffer, T continuationToken ); + + /// @brief Will read the next line ascii or binary end line char. + /// @param buffer The buffer for the next line. + /// @return true if successful. + bool getNextLine(std::vector &buffer); + + /// @brief Will read the next block. + /// @param buffer The buffer for the next block. + /// @return true if successful. + bool getNextBlock( std::vector &buffer ); + +private: + IOStream *m_stream; + size_t m_filesize; + size_t m_cacheSize; + size_t m_numBlocks; + size_t m_blockIdx; + std::vector m_cache; + size_t m_cachePos; + size_t m_filePos; +}; + +template +inline +IOStreamBuffer::IOStreamBuffer( size_t cache ) +: m_stream( nullptr ) +, m_filesize( 0 ) +, m_cacheSize( cache ) +, m_numBlocks( 0 ) +, m_blockIdx( 0 ) +, m_cachePos( 0 ) +, m_filePos( 0 ) { + m_cache.resize( cache ); + std::fill( m_cache.begin(), m_cache.end(), '\n' ); +} + +template +inline +IOStreamBuffer::~IOStreamBuffer() { + // empty +} + +template +inline +bool IOStreamBuffer::open( IOStream *stream ) { + // file still opened! + if ( nullptr != m_stream ) { + return false; + } + + // Invalid stream pointer + if ( nullptr == stream ) { + return false; + } + + m_stream = stream; + m_filesize = m_stream->FileSize(); + if ( m_filesize == 0 ) { + return false; + } + if ( m_filesize < m_cacheSize ) { + m_cacheSize = m_filesize; + } + + m_numBlocks = m_filesize / m_cacheSize; + if ( ( m_filesize % m_cacheSize ) > 0 ) { + m_numBlocks++; + } + + return true; +} + +template +inline +bool IOStreamBuffer::close() { + if ( nullptr == m_stream ) { + return false; + } + + // init counters and state vars + m_stream = nullptr; + m_filesize = 0; + m_numBlocks = 0; + m_blockIdx = 0; + m_cachePos = 0; + m_filePos = 0; + + return true; +} + +template +inline +size_t IOStreamBuffer::size() const { + return m_filesize; +} + +template +inline +size_t IOStreamBuffer::cacheSize() const { + return m_cacheSize; +} + +template +inline +bool IOStreamBuffer::readNextBlock() { + m_stream->Seek( m_filePos, aiOrigin_SET ); + size_t readLen = m_stream->Read( &m_cache[ 0 ], sizeof( T ), m_cacheSize ); + if ( readLen == 0 ) { + return false; + } + if ( readLen < m_cacheSize ) { + m_cacheSize = readLen; + } + m_filePos += m_cacheSize; + m_cachePos = 0; + m_blockIdx++; + + return true; +} + +template +inline +size_t IOStreamBuffer::getNumBlocks() const { + return m_numBlocks; +} + +template +inline +size_t IOStreamBuffer::getCurrentBlockIndex() const { + return m_blockIdx; +} + +template +inline +size_t IOStreamBuffer::getFilePos() const { + return m_filePos; +} + +template +inline +bool IOStreamBuffer::getNextDataLine( std::vector &buffer, T continuationToken ) { + buffer.resize( m_cacheSize ); + if ( m_cachePos >= m_cacheSize || 0 == m_filePos ) { + if ( !readNextBlock() ) { + return false; + } + } + + bool continuationFound( false ); + size_t i = 0; + for( ;; ) { + if ( continuationToken == m_cache[ m_cachePos ] ) { + continuationFound = true; + ++m_cachePos; + } + if ( IsLineEnd( m_cache[ m_cachePos ] ) ) { + if ( !continuationFound ) { + // the end of the data line + break; + } else { + // skip line end + while ( m_cache[m_cachePos] != '\n') { + ++m_cachePos; + } + ++m_cachePos; + continuationFound = false; + } + } + + buffer[ i ] = m_cache[ m_cachePos ]; + ++m_cachePos; + ++i; + if (m_cachePos >= size()) { + break; + } + if ( m_cachePos >= m_cacheSize ) { + if ( !readNextBlock() ) { + return false; + } + } + } + + buffer[ i ] = '\n'; + ++m_cachePos; + + return true; +} + +static inline +bool isEndOfCache( size_t pos, size_t cacheSize ) { + return ( pos == cacheSize ); +} + +template +inline +bool IOStreamBuffer::getNextLine(std::vector &buffer) { + buffer.resize(m_cacheSize); + if ( isEndOfCache( m_cachePos, m_cacheSize ) || 0 == m_filePos) { + if (!readNextBlock()) { + return false; + } + } + + if (IsLineEnd(m_cache[m_cachePos])) { + // skip line end + while (m_cache[m_cachePos] != '\n') { + ++m_cachePos; + } + ++m_cachePos; + if ( isEndOfCache( m_cachePos, m_cacheSize ) ) { + if ( !readNextBlock() ) { + return false; + } + } + } + + size_t i( 0 ); + while (!IsLineEnd(m_cache[ m_cachePos ])) { + buffer[i] = m_cache[ m_cachePos ]; + ++m_cachePos; + ++i; + if (m_cachePos >= m_cacheSize) { + if (!readNextBlock()) { + return false; + } + } + } + buffer[i] = '\n'; + ++m_cachePos; + + return true; +} + +template +inline +bool IOStreamBuffer::getNextBlock( std::vector &buffer) { + // Return the last block-value if getNextLine was used before + if ( 0 != m_cachePos ) { + buffer = std::vector( m_cache.begin() + m_cachePos, m_cache.end() ); + m_cachePos = 0; + } else { + if ( !readNextBlock() ) { + return false; + } + + buffer = std::vector(m_cache.begin(), m_cache.end()); + } + + return true; +} + +} // !ns Assimp diff --git a/dependencies/assimp/include/assimp/IOSystem.hpp b/dependencies/assimp/include/assimp/IOSystem.hpp new file mode 100644 index 0000000..78139c2 --- /dev/null +++ b/dependencies/assimp/include/assimp/IOSystem.hpp @@ -0,0 +1,357 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file IOSystem.hpp + * @brief File system wrapper for C++. Inherit this class to supply + * custom file handling logic to the Import library. +*/ + +#pragma once +#ifndef AI_IOSYSTEM_H_INC +#define AI_IOSYSTEM_H_INC + +#ifndef __cplusplus +# error This header requires C++ to be used. aiFileIO.h is the \ + corresponding C interface. +#endif + +#include "types.h" + +#ifdef _WIN32 +# include +# include +# include +#else +# include +# include +# include +#endif // _WIN32 + +#include + +namespace Assimp { + + class IOStream; + +// --------------------------------------------------------------------------- +/** @brief CPP-API: Interface to the file system. + * + * Derive an own implementation from this interface to supply custom file handling + * to the importer library. If you implement this interface, you also want to + * supply a custom implementation for IOStream. + * + * @see Importer::SetIOHandler() + */ +class ASSIMP_API IOSystem +#ifndef SWIG + : public Intern::AllocateFromAssimpHeap +#endif +{ +public: + + // ------------------------------------------------------------------- + /** @brief Default constructor. + * + * Create an instance of your derived class and assign it to an + * #Assimp::Importer instance by calling Importer::SetIOHandler(). + */ + IOSystem() AI_NO_EXCEPT; + + // ------------------------------------------------------------------- + /** @brief Virtual destructor. + * + * It is safe to be called from within DLL Assimp, we're constructed + * on Assimp's heap. + */ + virtual ~IOSystem(); + + // ------------------------------------------------------------------- + /** @brief For backward compatibility + * @see Exists(const char*) + */ + AI_FORCE_INLINE bool Exists( const std::string& pFile) const; + + // ------------------------------------------------------------------- + /** @brief Tests for the existence of a file at the given path. + * + * @param pFile Path to the file + * @return true if there is a file with this path, else false. + */ + virtual bool Exists( const char* pFile) const = 0; + + // ------------------------------------------------------------------- + /** @brief Returns the system specific directory separator + * @return System specific directory separator + */ + virtual char getOsSeparator() const = 0; + + // ------------------------------------------------------------------- + /** @brief Open a new file with a given path. + * + * When the access to the file is finished, call Close() to release + * all associated resources (or the virtual dtor of the IOStream). + * + * @param pFile Path to the file + * @param pMode Desired file I/O mode. Required are: "wb", "w", "wt", + * "rb", "r", "rt". + * + * @return New IOStream interface allowing the lib to access + * the underlying file. + * @note When implementing this class to provide custom IO handling, + * you probably have to supply an own implementation of IOStream as well. + */ + virtual IOStream* Open(const char* pFile, + const char* pMode = "rb") = 0; + + // ------------------------------------------------------------------- + /** @brief For backward compatibility + * @see Open(const char*, const char*) + */ + inline IOStream* Open(const std::string& pFile, + const std::string& pMode = std::string("rb")); + + // ------------------------------------------------------------------- + /** @brief Closes the given file and releases all resources + * associated with it. + * @param pFile The file instance previously created by Open(). + */ + virtual void Close( IOStream* pFile) = 0; + + // ------------------------------------------------------------------- + /** @brief Compares two paths and check whether the point to + * identical files. + * + * The dummy implementation of this virtual member performs a + * case-insensitive comparison of the given strings. The default IO + * system implementation uses OS mechanisms to convert relative into + * absolute paths, so the result can be trusted. + * @param one First file + * @param second Second file + * @return true if the paths point to the same file. The file needn't + * be existing, however. + */ + virtual bool ComparePaths (const char* one, + const char* second) const; + + // ------------------------------------------------------------------- + /** @brief For backward compatibility + * @see ComparePaths(const char*, const char*) + */ + inline bool ComparePaths (const std::string& one, + const std::string& second) const; + + // ------------------------------------------------------------------- + /** @brief Pushes a new directory onto the directory stack. + * @param path Path to push onto the stack. + * @return True, when push was successful, false if path is empty. + */ + virtual bool PushDirectory( const std::string &path ); + + // ------------------------------------------------------------------- + /** @brief Returns the top directory from the stack. + * @return The directory on the top of the stack. + * Returns empty when no directory was pushed to the stack. + */ + virtual const std::string &CurrentDirectory() const; + + // ------------------------------------------------------------------- + /** @brief Returns the number of directories stored on the stack. + * @return The number of directories of the stack. + */ + virtual size_t StackSize() const; + + // ------------------------------------------------------------------- + /** @brief Pops the top directory from the stack. + * @return True, when a directory was on the stack. False if no + * directory was on the stack. + */ + virtual bool PopDirectory(); + + // ------------------------------------------------------------------- + /** @brief CReates an new directory at the given path. + * @param path [in] The path to create. + * @return True, when a directory was created. False if the directory + * cannot be created. + */ + virtual bool CreateDirectory( const std::string &path ); + + // ------------------------------------------------------------------- + /** @brief Will change the current directory to the given path. + * @param path [in] The path to change to. + * @return True, when the directory has changed successfully. + */ + virtual bool ChangeDirectory( const std::string &path ); + + virtual bool DeleteFile( const std::string &file ); + +private: + std::vector m_pathStack; +}; + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +IOSystem::IOSystem() AI_NO_EXCEPT +: m_pathStack() { + // empty +} + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +IOSystem::~IOSystem() { + // empty +} + +// ---------------------------------------------------------------------------- +// For compatibility, the interface of some functions taking a std::string was +// changed to const char* to avoid crashes between binary incompatible STL +// versions. This code her is inlined, so it shouldn't cause any problems. +// ---------------------------------------------------------------------------- + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +IOStream* IOSystem::Open(const std::string& pFile, const std::string& pMode) { + // NOTE: + // For compatibility, interface was changed to const char* to + // avoid crashes between binary incompatible STL versions + return Open(pFile.c_str(),pMode.c_str()); +} + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +bool IOSystem::Exists( const std::string& pFile) const { + // NOTE: + // For compatibility, interface was changed to const char* to + // avoid crashes between binary incompatible STL versions + return Exists(pFile.c_str()); +} + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +bool IOSystem::ComparePaths (const std::string& one, const std::string& second) const { + // NOTE: + // For compatibility, interface was changed to const char* to + // avoid crashes between binary incompatible STL versions + return ComparePaths(one.c_str(),second.c_str()); +} + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +bool IOSystem::PushDirectory( const std::string &path ) { + if ( path.empty() ) { + return false; + } + + m_pathStack.push_back( path ); + + return true; +} + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +const std::string &IOSystem::CurrentDirectory() const { + if ( m_pathStack.empty() ) { + static const std::string Dummy(""); + return Dummy; + } + return m_pathStack[ m_pathStack.size()-1 ]; +} + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +size_t IOSystem::StackSize() const { + return m_pathStack.size(); +} + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +bool IOSystem::PopDirectory() { + if ( m_pathStack.empty() ) { + return false; + } + + m_pathStack.pop_back(); + + return true; +} + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +bool IOSystem::CreateDirectory( const std::string &path ) { + if ( path.empty() ) { + return false; + } + +#ifdef _WIN32 + return 0 != ::_mkdir( path.c_str() ); +#else + return 0 != ::mkdir( path.c_str(), 0777 ); +#endif // _WIN32 +} + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +bool IOSystem::ChangeDirectory( const std::string &path ) { + if ( path.empty() ) { + return false; + } + +#ifdef _WIN32 + return 0 != ::_chdir( path.c_str() ); +#else + return 0 != ::chdir( path.c_str() ); +#endif // _WIN32 +} + + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE +bool IOSystem::DeleteFile( const std::string &file ) { + if ( file.empty() ) { + return false; + } + const int retCode( ::remove( file.c_str() ) ); + return ( 0 == retCode ); +} +} //!ns Assimp + +#endif //AI_IOSYSTEM_H_INC diff --git a/dependencies/assimp/include/assimp/Importer.hpp b/dependencies/assimp/include/assimp/Importer.hpp new file mode 100644 index 0000000..4941df4 --- /dev/null +++ b/dependencies/assimp/include/assimp/Importer.hpp @@ -0,0 +1,659 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Importer.hpp + * @brief Defines the C++-API to the Open Asset Import Library. + */ +#pragma once +#ifndef AI_ASSIMP_HPP_INC +#define AI_ASSIMP_HPP_INC + +#ifndef __cplusplus +# error This header requires C++ to be used. Use assimp.h for plain C. +#endif // __cplusplus + +// Public ASSIMP data structures +#include + +namespace Assimp { + // ======================================================================= + // Public interface to Assimp + class Importer; + class IOStream; + class IOSystem; + class ProgressHandler; + + // ======================================================================= + // Plugin development + // + // Include the following headers for the declarations: + // BaseImporter.h + // BaseProcess.h + class BaseImporter; + class BaseProcess; + class SharedPostProcessInfo; + class BatchLoader; + + // ======================================================================= + // Holy stuff, only for members of the high council of the Jedi. + class ImporterPimpl; +} //! namespace Assimp + +#define AI_PROPERTY_WAS_NOT_EXISTING 0xffffffff + +struct aiScene; + +// importerdesc.h +struct aiImporterDesc; + +/** @namespace Assimp Assimp's CPP-API and all internal APIs */ +namespace Assimp { + +// ---------------------------------------------------------------------------------- +/** CPP-API: The Importer class forms an C++ interface to the functionality of the +* Open Asset Import Library. +* +* Create an object of this class and call ReadFile() to import a file. +* If the import succeeds, the function returns a pointer to the imported data. +* The data remains property of the object, it is intended to be accessed +* read-only. The imported data will be destroyed along with the Importer +* object. If the import fails, ReadFile() returns a NULL pointer. In this +* case you can retrieve a human-readable error description be calling +* GetErrorString(). You can call ReadFile() multiple times with a single Importer +* instance. Actually, constructing Importer objects involves quite many +* allocations and may take some time, so it's better to reuse them as often as +* possible. +* +* If you need the Importer to do custom file handling to access the files, +* implement IOSystem and IOStream and supply an instance of your custom +* IOSystem implementation by calling SetIOHandler() before calling ReadFile(). +* If you do not assign a custion IO handler, a default handler using the +* standard C++ IO logic will be used. +* +* @note One Importer instance is not thread-safe. If you use multiple +* threads for loading, each thread should maintain its own Importer instance. +*/ +class ASSIMP_API Importer { +public: + /** + * @brief The upper limit for hints. + */ + static const unsigned int MaxLenHint = 200; + +public: + + // ------------------------------------------------------------------- + /** Constructor. Creates an empty importer object. + * + * Call ReadFile() to start the import process. The configuration + * property table is initially empty. + */ + Importer(); + + // ------------------------------------------------------------------- + /** Copy constructor. + * + * This copies the configuration properties of another Importer. + * If this Importer owns a scene it won't be copied. + * Call ReadFile() to start the import process. + */ + Importer(const Importer& other)=delete; + + // ------------------------------------------------------------------- + /** Assignment operator has been deleted + */ + Importer &operator=(const Importer &) = delete; + + // ------------------------------------------------------------------- + /** Destructor. The object kept ownership of the imported data, + * which now will be destroyed along with the object. + */ + ~Importer(); + + + // ------------------------------------------------------------------- + /** Registers a new loader. + * + * @param pImp Importer to be added. The Importer instance takes + * ownership of the pointer, so it will be automatically deleted + * with the Importer instance. + * @return AI_SUCCESS if the loader has been added. The registration + * fails if there is already a loader for a specific file extension. + */ + aiReturn RegisterLoader(BaseImporter* pImp); + + // ------------------------------------------------------------------- + /** Unregisters a loader. + * + * @param pImp Importer to be unregistered. + * @return AI_SUCCESS if the loader has been removed. The function + * fails if the loader is currently in use (this could happen + * if the #Importer instance is used by more than one thread) or + * if it has not yet been registered. + */ + aiReturn UnregisterLoader(BaseImporter* pImp); + + // ------------------------------------------------------------------- + /** Registers a new post-process step. + * + * At the moment, there's a small limitation: new post processing + * steps are added to end of the list, or in other words, executed + * last, after all built-in steps. + * @param pImp Post-process step to be added. The Importer instance + * takes ownership of the pointer, so it will be automatically + * deleted with the Importer instance. + * @return AI_SUCCESS if the step has been added correctly. + */ + aiReturn RegisterPPStep(BaseProcess* pImp); + + // ------------------------------------------------------------------- + /** Unregisters a post-process step. + * + * @param pImp Step to be unregistered. + * @return AI_SUCCESS if the step has been removed. The function + * fails if the step is currently in use (this could happen + * if the #Importer instance is used by more than one thread) or + * if it has not yet been registered. + */ + aiReturn UnregisterPPStep(BaseProcess* pImp); + + // ------------------------------------------------------------------- + /** Set an integer configuration property. + * @param szName Name of the property. All supported properties + * are defined in the aiConfig.g header (all constants share the + * prefix AI_CONFIG_XXX and are simple strings). + * @param iValue New value of the property + * @return true if the property was set before. The new value replaces + * the previous value in this case. + * @note Property of different types (float, int, string ..) are kept + * on different stacks, so calling SetPropertyInteger() for a + * floating-point property has no effect - the loader will call + * GetPropertyFloat() to read the property, but it won't be there. + */ + bool SetPropertyInteger(const char* szName, int iValue); + + // ------------------------------------------------------------------- + /** Set a boolean configuration property. Boolean properties + * are stored on the integer stack internally so it's possible + * to set them via #SetPropertyBool and query them with + * #GetPropertyBool and vice versa. + * @see SetPropertyInteger() + */ + bool SetPropertyBool(const char* szName, bool value) { + return SetPropertyInteger(szName,value); + } + + // ------------------------------------------------------------------- + /** Set a floating-point configuration property. + * @see SetPropertyInteger() + */ + bool SetPropertyFloat(const char* szName, ai_real fValue); + + // ------------------------------------------------------------------- + /** Set a string configuration property. + * @see SetPropertyInteger() + */ + bool SetPropertyString(const char* szName, const std::string& sValue); + + // ------------------------------------------------------------------- + /** Set a matrix configuration property. + * @see SetPropertyInteger() + */ + bool SetPropertyMatrix(const char* szName, const aiMatrix4x4& sValue); + + // ------------------------------------------------------------------- + /** Get a configuration property. + * @param szName Name of the property. All supported properties + * are defined in the aiConfig.g header (all constants share the + * prefix AI_CONFIG_XXX). + * @param iErrorReturn Value that is returned if the property + * is not found. + * @return Current value of the property + * @note Property of different types (float, int, string ..) are kept + * on different lists, so calling SetPropertyInteger() for a + * floating-point property has no effect - the loader will call + * GetPropertyFloat() to read the property, but it won't be there. + */ + int GetPropertyInteger(const char* szName, + int iErrorReturn = 0xffffffff) const; + + // ------------------------------------------------------------------- + /** Get a boolean configuration property. Boolean properties + * are stored on the integer stack internally so it's possible + * to set them via #SetPropertyBool and query them with + * #GetPropertyBool and vice versa. + * @see GetPropertyInteger() + */ + bool GetPropertyBool(const char* szName, bool bErrorReturn = false) const { + return GetPropertyInteger(szName,bErrorReturn)!=0; + } + + // ------------------------------------------------------------------- + /** Get a floating-point configuration property + * @see GetPropertyInteger() + */ + ai_real GetPropertyFloat(const char* szName, + ai_real fErrorReturn = 10e10) const; + + // ------------------------------------------------------------------- + /** Get a string configuration property + * + * The return value remains valid until the property is modified. + * @see GetPropertyInteger() + */ + const std::string GetPropertyString(const char* szName, + const std::string& sErrorReturn = "") const; + + // ------------------------------------------------------------------- + /** Get a matrix configuration property + * + * The return value remains valid until the property is modified. + * @see GetPropertyInteger() + */ + const aiMatrix4x4 GetPropertyMatrix(const char* szName, + const aiMatrix4x4& sErrorReturn = aiMatrix4x4()) const; + + // ------------------------------------------------------------------- + /** Supplies a custom IO handler to the importer to use to open and + * access files. If you need the importer to use custom IO logic to + * access the files, you need to provide a custom implementation of + * IOSystem and IOFile to the importer. Then create an instance of + * your custom IOSystem implementation and supply it by this function. + * + * The Importer takes ownership of the object and will destroy it + * afterwards. The previously assigned handler will be deleted. + * Pass NULL to take again ownership of your IOSystem and reset Assimp + * to use its default implementation. + * + * @param pIOHandler The IO handler to be used in all file accesses + * of the Importer. + */ + void SetIOHandler( IOSystem* pIOHandler); + + // ------------------------------------------------------------------- + /** Retrieves the IO handler that is currently set. + * You can use #IsDefaultIOHandler() to check whether the returned + * interface is the default IO handler provided by ASSIMP. The default + * handler is active as long the application doesn't supply its own + * custom IO handler via #SetIOHandler(). + * @return A valid IOSystem interface, never NULL. + */ + IOSystem* GetIOHandler() const; + + // ------------------------------------------------------------------- + /** Checks whether a default IO handler is active + * A default handler is active as long the application doesn't + * supply its own custom IO handler via #SetIOHandler(). + * @return true by default + */ + bool IsDefaultIOHandler() const; + + // ------------------------------------------------------------------- + /** Supplies a custom progress handler to the importer. This + * interface exposes an #Update() callback, which is called + * more or less periodically (please don't sue us if it + * isn't as periodically as you'd like it to have ...). + * This can be used to implement progress bars and loading + * timeouts. + * @param pHandler Progress callback interface. Pass NULL to + * disable progress reporting. + * @note Progress handlers can be used to abort the loading + * at almost any time.*/ + void SetProgressHandler ( ProgressHandler* pHandler ); + + // ------------------------------------------------------------------- + /** Retrieves the progress handler that is currently set. + * You can use #IsDefaultProgressHandler() to check whether the returned + * interface is the default handler provided by ASSIMP. The default + * handler is active as long the application doesn't supply its own + * custom handler via #SetProgressHandler(). + * @return A valid ProgressHandler interface, never NULL. + */ + ProgressHandler* GetProgressHandler() const; + + // ------------------------------------------------------------------- + /** Checks whether a default progress handler is active + * A default handler is active as long the application doesn't + * supply its own custom progress handler via #SetProgressHandler(). + * @return true by default + */ + bool IsDefaultProgressHandler() const; + + // ------------------------------------------------------------------- + /** @brief Check whether a given set of post-processing flags + * is supported. + * + * Some flags are mutually exclusive, others are probably + * not available because your excluded them from your + * Assimp builds. Calling this function is recommended if + * you're unsure. + * + * @param pFlags Bitwise combination of the aiPostProcess flags. + * @return true if this flag combination is fine. + */ + bool ValidateFlags(unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Reads the given file and returns its contents if successful. + * + * If the call succeeds, the contents of the file are returned as a + * pointer to an aiScene object. The returned data is intended to be + * read-only, the importer object keeps ownership of the data and will + * destroy it upon destruction. If the import fails, NULL is returned. + * A human-readable error description can be retrieved by calling + * GetErrorString(). The previous scene will be deleted during this call. + * @param pFile Path and filename to the file to be imported. + * @param pFlags Optional post processing steps to be executed after + * a successful import. Provide a bitwise combination of the + * #aiPostProcessSteps flags. If you wish to inspect the imported + * scene first in order to fine-tune your post-processing setup, + * consider to use #ApplyPostProcessing(). + * @return A pointer to the imported data, NULL if the import failed. + * The pointer to the scene remains in possession of the Importer + * instance. Use GetOrphanedScene() to take ownership of it. + * + * @note Assimp is able to determine the file format of a file + * automatically. + */ + const aiScene* ReadFile( + const char* pFile, + unsigned int pFlags); + + // ------------------------------------------------------------------- + /** Reads the given file from a memory buffer and returns its + * contents if successful. + * + * If the call succeeds, the contents of the file are returned as a + * pointer to an aiScene object. The returned data is intended to be + * read-only, the importer object keeps ownership of the data and will + * destroy it upon destruction. If the import fails, NULL is returned. + * A human-readable error description can be retrieved by calling + * GetErrorString(). The previous scene will be deleted during this call. + * Calling this method doesn't affect the active IOSystem. + * @param pBuffer Pointer to the file data + * @param pLength Length of pBuffer, in bytes + * @param pFlags Optional post processing steps to be executed after + * a successful import. Provide a bitwise combination of the + * #aiPostProcessSteps flags. If you wish to inspect the imported + * scene first in order to fine-tune your post-processing setup, + * consider to use #ApplyPostProcessing(). + * @param pHint An additional hint to the library. If this is a non + * empty string, the library looks for a loader to support + * the file extension specified by pHint and passes the file to + * the first matching loader. If this loader is unable to completely + * the request, the library continues and tries to determine the + * file format on its own, a task that may or may not be successful. + * Check the return value, and you'll know ... + * @return A pointer to the imported data, NULL if the import failed. + * The pointer to the scene remains in possession of the Importer + * instance. Use GetOrphanedScene() to take ownership of it. + * + * @note This is a straightforward way to decode models from memory + * buffers, but it doesn't handle model formats that spread their + * data across multiple files or even directories. Examples include + * OBJ or MD3, which outsource parts of their material info into + * external scripts. If you need full functionality, provide + * a custom IOSystem to make Assimp find these files and use + * the regular ReadFile() API. + */ + const aiScene* ReadFileFromMemory( + const void* pBuffer, + size_t pLength, + unsigned int pFlags, + const char* pHint = ""); + + // ------------------------------------------------------------------- + /** Apply post-processing to an already-imported scene. + * + * This is strictly equivalent to calling #ReadFile() with the same + * flags. However, you can use this separate function to inspect + * the imported scene first to fine-tune your post-processing setup. + * @param pFlags Provide a bitwise combination of the + * #aiPostProcessSteps flags. + * @return A pointer to the post-processed data. This is still the + * same as the pointer returned by #ReadFile(). However, if + * post-processing fails, the scene could now be NULL. + * That's quite a rare case, post processing steps are not really + * designed to 'fail'. To be exact, the #aiProcess_ValidateDS + * flag is currently the only post processing step which can actually + * cause the scene to be reset to NULL. + * + * @note The method does nothing if no scene is currently bound + * to the #Importer instance. */ + const aiScene* ApplyPostProcessing(unsigned int pFlags); + + const aiScene* ApplyCustomizedPostProcessing( BaseProcess *rootProcess, bool requestValidation ); + + // ------------------------------------------------------------------- + /** @brief Reads the given file and returns its contents if successful. + * + * This function is provided for backward compatibility. + * See the const char* version for detailed docs. + * @see ReadFile(const char*, pFlags) */ + const aiScene* ReadFile( + const std::string& pFile, + unsigned int pFlags); + + // ------------------------------------------------------------------- + /** Frees the current scene. + * + * The function does nothing if no scene has previously been + * read via ReadFile(). FreeScene() is called automatically by the + * destructor and ReadFile() itself. */ + void FreeScene( ); + + // ------------------------------------------------------------------- + /** Returns an error description of an error that occurred in ReadFile(). + * + * Returns an empty string if no error occurred. + * @return A description of the last error, an empty string if no + * error occurred. The string is never NULL. + * + * @note The returned function remains valid until one of the + * following methods is called: #ReadFile(), #FreeScene(). */ + const char* GetErrorString() const; + + // ------------------------------------------------------------------- + /** Returns the scene loaded by the last successful call to ReadFile() + * + * @return Current scene or NULL if there is currently no scene loaded */ + const aiScene* GetScene() const; + + // ------------------------------------------------------------------- + /** Returns the scene loaded by the last successful call to ReadFile() + * and releases the scene from the ownership of the Importer + * instance. The application is now responsible for deleting the + * scene. Any further calls to GetScene() or GetOrphanedScene() + * will return NULL - until a new scene has been loaded via ReadFile(). + * + * @return Current scene or NULL if there is currently no scene loaded + * @note Use this method with maximal caution, and only if you have to. + * By design, aiScene's are exclusively maintained, allocated and + * deallocated by Assimp and no one else. The reasoning behind this + * is the golden rule that deallocations should always be done + * by the module that did the original allocation because heaps + * are not necessarily shared. GetOrphanedScene() enforces you + * to delete the returned scene by yourself, but this will only + * be fine if and only if you're using the same heap as assimp. + * On Windows, it's typically fine provided everything is linked + * against the multithreaded-dll version of the runtime library. + * It will work as well for static linkage with Assimp.*/ + aiScene* GetOrphanedScene(); + + // ------------------------------------------------------------------- + /** Returns whether a given file extension is supported by ASSIMP. + * + * @param szExtension Extension to be checked. + * Must include a trailing dot '.'. Example: ".3ds", ".md3". + * Cases-insensitive. + * @return true if the extension is supported, false otherwise */ + bool IsExtensionSupported(const char* szExtension) const; + + // ------------------------------------------------------------------- + /** @brief Returns whether a given file extension is supported by ASSIMP. + * + * This function is provided for backward compatibility. + * See the const char* version for detailed and up-to-date docs. + * @see IsExtensionSupported(const char*) */ + inline bool IsExtensionSupported(const std::string& szExtension) const; + + // ------------------------------------------------------------------- + /** Get a full list of all file extensions supported by ASSIMP. + * + * If a file extension is contained in the list this does of course not + * mean that ASSIMP is able to load all files with this extension --- + * it simply means there is an importer loaded which claims to handle + * files with this file extension. + * @param szOut String to receive the extension list. + * Format of the list: "*.3ds;*.obj;*.dae". This is useful for + * use with the WinAPI call GetOpenFileName(Ex). */ + void GetExtensionList(aiString& szOut) const; + + // ------------------------------------------------------------------- + /** @brief Get a full list of all file extensions supported by ASSIMP. + * + * This function is provided for backward compatibility. + * See the aiString version for detailed and up-to-date docs. + * @see GetExtensionList(aiString&)*/ + inline void GetExtensionList(std::string& szOut) const; + + // ------------------------------------------------------------------- + /** Get the number of importers currently registered with Assimp. */ + size_t GetImporterCount() const; + + // ------------------------------------------------------------------- + /** Get meta data for the importer corresponding to a specific index.. + * + * For the declaration of #aiImporterDesc, include . + * @param index Index to query, must be within [0,GetImporterCount()) + * @return Importer meta data structure, NULL if the index does not + * exist or if the importer doesn't offer meta information ( + * importers may do this at the cost of being hated by their peers).*/ + const aiImporterDesc* GetImporterInfo(size_t index) const; + + // ------------------------------------------------------------------- + /** Find the importer corresponding to a specific index. + * + * @param index Index to query, must be within [0,GetImporterCount()) + * @return Importer instance. NULL if the index does not + * exist. */ + BaseImporter* GetImporter(size_t index) const; + + // ------------------------------------------------------------------- + /** Find the importer corresponding to a specific file extension. + * + * This is quite similar to #IsExtensionSupported except a + * BaseImporter instance is returned. + * @param szExtension Extension to check for. The following formats + * are recognized (BAH being the file extension): "BAH" (comparison + * is case-insensitive), ".bah", "*.bah" (wild card and dot + * characters at the beginning of the extension are skipped). + * @return NULL if no importer is found*/ + BaseImporter* GetImporter (const char* szExtension) const; + + // ------------------------------------------------------------------- + /** Find the importer index corresponding to a specific file extension. + * + * @param szExtension Extension to check for. The following formats + * are recognized (BAH being the file extension): "BAH" (comparison + * is case-insensitive), ".bah", "*.bah" (wild card and dot + * characters at the beginning of the extension are skipped). + * @return (size_t)-1 if no importer is found */ + size_t GetImporterIndex (const char* szExtension) const; + + // ------------------------------------------------------------------- + /** Returns the storage allocated by ASSIMP to hold the scene data + * in memory. + * + * This refers to the currently loaded file, see #ReadFile(). + * @param in Data structure to be filled. + * @note The returned memory statistics refer to the actual + * size of the use data of the aiScene. Heap-related overhead + * is (naturally) not included.*/ + void GetMemoryRequirements(aiMemoryInfo& in) const; + + // ------------------------------------------------------------------- + /** Enables "extra verbose" mode. + * + * 'Extra verbose' means the data structure is validated after *every* + * single post processing step to make sure everyone modifies the data + * structure in a well-defined manner. This is a debug feature and not + * intended for use in production environments. */ + void SetExtraVerbose(bool bDo); + + // ------------------------------------------------------------------- + /** Private, do not use. */ + ImporterPimpl* Pimpl() { return pimpl; } + const ImporterPimpl* Pimpl() const { return pimpl; } + +protected: + + // Just because we don't want you to know how we're hacking around. + ImporterPimpl* pimpl; +}; //! class Importer + + +// ---------------------------------------------------------------------------- +// For compatibility, the interface of some functions taking a std::string was +// changed to const char* to avoid crashes between binary incompatible STL +// versions. This code her is inlined, so it shouldn't cause any problems. +// ---------------------------------------------------------------------------- + +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE const aiScene* Importer::ReadFile( const std::string& pFile,unsigned int pFlags){ + return ReadFile(pFile.c_str(),pFlags); +} +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE void Importer::GetExtensionList(std::string& szOut) const { + aiString s; + GetExtensionList(s); + szOut = s.data; +} +// ---------------------------------------------------------------------------- +AI_FORCE_INLINE bool Importer::IsExtensionSupported(const std::string& szExtension) const { + return IsExtensionSupported(szExtension.c_str()); +} + +} // !namespace Assimp + +#endif // AI_ASSIMP_HPP_INC diff --git a/dependencies/assimp/include/assimp/LineSplitter.h b/dependencies/assimp/include/assimp/LineSplitter.h new file mode 100644 index 0000000..4afe45b --- /dev/null +++ b/dependencies/assimp/include/assimp/LineSplitter.h @@ -0,0 +1,285 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file LineSplitter.h + * @brief LineSplitter, a helper class to iterate through all lines + * of a file easily. Works with StreamReader. + */ +#pragma once +#ifndef INCLUDED_LINE_SPLITTER_H +#define INCLUDED_LINE_SPLITTER_H + +#include +#include "StreamReader.h" +#include "ParsingUtils.h" + +namespace Assimp { + +// ------------------------------------------------------------------------------------------------ +/** Usage: +@code +for(LineSplitter splitter(stream);splitter;++splitter) { + + if (*splitter == "hi!") { + ... + } + else if (splitter->substr(0,5) == "hello") { + ... + // access the third token in the line (tokens are space-separated) + if (strtol(splitter[2]) > 5) { .. } + } + + std::cout << "Current line is: " << splitter.get_index() << std::endl; +} +@endcode +*/ +// ------------------------------------------------------------------------------------------------ +class LineSplitter { +public: + typedef size_t line_idx; + + // ----------------------------------------- + /** construct from existing stream reader + note: trim is *always* assumed true if skyp_empty_lines==true + */ + LineSplitter(StreamReaderLE& stream, bool skip_empty_lines = true, bool trim = true); + + ~LineSplitter(); + + // ----------------------------------------- + /** pseudo-iterator increment */ + LineSplitter& operator++(); + + // ----------------------------------------- + LineSplitter& operator++(int); + + // ----------------------------------------- + /** get a pointer to the beginning of a particular token */ + const char* operator[] (size_t idx) const; + + // ----------------------------------------- + /** extract the start positions of N tokens from the current line*/ + template + void get_tokens(const char* (&tokens)[N]) const; + + // ----------------------------------------- + /** member access */ + const std::string* operator -> () const; + + std::string operator* () const; + + // ----------------------------------------- + /** boolean context */ + operator bool() const; + + // ----------------------------------------- + /** line indices are zero-based, empty lines are included */ + operator line_idx() const; + + line_idx get_index() const; + + // ----------------------------------------- + /** access the underlying stream object */ + StreamReaderLE& get_stream(); + + // ----------------------------------------- + /** !strcmp((*this)->substr(0,strlen(check)),check) */ + bool match_start(const char* check); + + // ----------------------------------------- + /** swallow the next call to ++, return the previous value. */ + void swallow_next_increment(); + + LineSplitter( const LineSplitter & ) = delete; + LineSplitter(LineSplitter &&) = delete; + LineSplitter &operator = ( const LineSplitter & ) = delete; + +private: + line_idx mIdx; + std::string mCur; + StreamReaderLE& mStream; + bool mSwallow, mSkip_empty_lines, mTrim; +}; + +inline +LineSplitter::LineSplitter(StreamReaderLE& stream, bool skip_empty_lines, bool trim ) +: mIdx(0) +, mCur() +, mStream(stream) +, mSwallow() +, mSkip_empty_lines(skip_empty_lines) +, mTrim(trim) { + mCur.reserve(1024); + operator++(); + mIdx = 0; +} + +inline +LineSplitter::~LineSplitter() { + // empty +} + +inline +LineSplitter& LineSplitter::operator++() { + if (mSwallow) { + mSwallow = false; + return *this; + } + + if (!*this) { + throw std::logic_error("End of file, no more lines to be retrieved."); + } + + char s; + mCur.clear(); + while (mStream.GetRemainingSize() && (s = mStream.GetI1(), 1)) { + if (s == '\n' || s == '\r') { + if (mSkip_empty_lines) { + while (mStream.GetRemainingSize() && ((s = mStream.GetI1()) == ' ' || s == '\r' || s == '\n')); + if (mStream.GetRemainingSize()) { + mStream.IncPtr(-1); + } + } else { + // skip both potential line terminators but don't read past this line. + if (mStream.GetRemainingSize() && (s == '\r' && mStream.GetI1() != '\n')) { + mStream.IncPtr(-1); + } + if (mTrim) { + while (mStream.GetRemainingSize() && ((s = mStream.GetI1()) == ' ' || s == '\t')); + if (mStream.GetRemainingSize()) { + mStream.IncPtr(-1); + } + } + } + break; + } + mCur += s; + } + ++mIdx; + + return *this; +} + +inline +LineSplitter &LineSplitter::operator++(int) { + return ++(*this); +} + +inline +const char *LineSplitter::operator[] (size_t idx) const { + const char* s = operator->()->c_str(); + + SkipSpaces(&s); + for (size_t i = 0; i < idx; ++i) { + + for (; !IsSpace(*s); ++s) { + if (IsLineEnd(*s)) { + throw std::range_error("Token index out of range, EOL reached"); + } + } + SkipSpaces(&s); + } + return s; +} + +template +inline +void LineSplitter::get_tokens(const char* (&tokens)[N]) const { + const char* s = operator->()->c_str(); + + SkipSpaces(&s); + for (size_t i = 0; i < N; ++i) { + if (IsLineEnd(*s)) { + throw std::range_error("Token count out of range, EOL reached"); + } + tokens[i] = s; + + for (; *s && !IsSpace(*s); ++s); + SkipSpaces(&s); + } +} + +inline +const std::string* LineSplitter::operator -> () const { + return &mCur; +} + +inline +std::string LineSplitter::operator* () const { + return mCur; +} + +inline +LineSplitter::operator bool() const { + return mStream.GetRemainingSize() > 0; +} + +inline +LineSplitter::operator line_idx() const { + return mIdx; +} + +inline +LineSplitter::line_idx LineSplitter::get_index() const { + return mIdx; +} + +inline +StreamReaderLE &LineSplitter::get_stream() { + return mStream; +} + +inline +bool LineSplitter::match_start(const char* check) { + const size_t len = ::strlen(check); + + return len <= mCur.length() && std::equal(check, check + len, mCur.begin()); +} + +inline +void LineSplitter::swallow_next_increment() { + mSwallow = true; +} + +} // Namespace Assimp + +#endif // INCLUDED_LINE_SPLITTER_H diff --git a/dependencies/assimp/include/assimp/LogAux.h b/dependencies/assimp/include/assimp/LogAux.h new file mode 100644 index 0000000..5584852 --- /dev/null +++ b/dependencies/assimp/include/assimp/LogAux.h @@ -0,0 +1,131 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file LogAux.h + * @brief Common logging usage patterns for importer implementations + */ +#ifndef INCLUDED_AI_LOGAUX_H +#define INCLUDED_AI_LOGAUX_H + +#include +#include +#include + +namespace Assimp { + +template +class LogFunctions { +public: + // ------------------------------------------------------------------------------------------------ + static void ThrowException(const std::string& msg) + { + throw DeadlyImportError(Prefix()+msg); + } + + // ------------------------------------------------------------------------------------------------ + static void LogWarn(const Formatter::format& message) { + if (!DefaultLogger::isNullLogger()) { + ASSIMP_LOG_WARN(Prefix()+(std::string)message); + } + } + + // ------------------------------------------------------------------------------------------------ + static void LogError(const Formatter::format& message) { + if (!DefaultLogger::isNullLogger()) { + ASSIMP_LOG_ERROR(Prefix()+(std::string)message); + } + } + + // ------------------------------------------------------------------------------------------------ + static void LogInfo(const Formatter::format& message) { + if (!DefaultLogger::isNullLogger()) { + ASSIMP_LOG_INFO(Prefix()+(std::string)message); + } + } + + // ------------------------------------------------------------------------------------------------ + static void LogDebug(const Formatter::format& message) { + if (!DefaultLogger::isNullLogger()) { + ASSIMP_LOG_DEBUG(Prefix()+(std::string)message); + } + } + + // https://sourceforge.net/tracker/?func=detail&atid=1067632&aid=3358562&group_id=226462 +#if !defined(__GNUC__) || !defined(__APPLE__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) + + // ------------------------------------------------------------------------------------------------ + static void LogWarn (const char* message) { + if (!DefaultLogger::isNullLogger()) { + LogWarn(Formatter::format(message)); + } + } + + // ------------------------------------------------------------------------------------------------ + static void LogError (const char* message) { + if (!DefaultLogger::isNullLogger()) { + LogError(Formatter::format(message)); + } + } + + // ------------------------------------------------------------------------------------------------ + static void LogInfo (const char* message) { + if (!DefaultLogger::isNullLogger()) { + LogInfo(Formatter::format(message)); + } + } + + // ------------------------------------------------------------------------------------------------ + static void LogDebug (const char* message) { + if (!DefaultLogger::isNullLogger()) { + LogDebug(Formatter::format(message)); + } + } + +#endif + +private: + static const char* Prefix(); + +}; +} // ! Assimp + +#endif diff --git a/dependencies/assimp/include/assimp/LogStream.hpp b/dependencies/assimp/include/assimp/LogStream.hpp new file mode 100644 index 0000000..d0281e2 --- /dev/null +++ b/dependencies/assimp/include/assimp/LogStream.hpp @@ -0,0 +1,111 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file LogStream.hpp + * @brief Abstract base class 'LogStream', representing an output log stream. + */ +#ifndef INCLUDED_AI_LOGSTREAM_H +#define INCLUDED_AI_LOGSTREAM_H + +#include "types.h" + +namespace Assimp { + +class IOSystem; + +// ------------------------------------------------------------------------------------ +/** @brief CPP-API: Abstract interface for log stream implementations. + * + * Several default implementations are provided, see #aiDefaultLogStream for more + * details. Writing your own implementation of LogStream is just necessary if these + * are not enough for your purpose. */ +class ASSIMP_API LogStream +#ifndef SWIG + : public Intern::AllocateFromAssimpHeap +#endif +{ +protected: + /** @brief Default constructor */ + LogStream() AI_NO_EXCEPT; + +public: + /** @brief Virtual destructor */ + virtual ~LogStream(); + + // ------------------------------------------------------------------- + /** @brief Overwrite this for your own output methods + * + * Log messages *may* consist of multiple lines and you shouldn't + * expect a consistent formatting. If you want custom formatting + * (e.g. generate HTML), supply a custom instance of Logger to + * #DefaultLogger:set(). Usually you can *expect* that a log message + * is exactly one line and terminated with a single \n character. + * @param message Message to be written */ + virtual void write(const char* message) = 0; + + // ------------------------------------------------------------------- + /** @brief Creates a default log stream + * @param streams Type of the default stream + * @param name For aiDefaultLogStream_FILE: name of the output file + * @param io For aiDefaultLogStream_FILE: IOSystem to be used to open the output + * file. Pass NULL for the default implementation. + * @return New LogStream instance. */ + static LogStream* createDefaultStream(aiDefaultLogStream stream, + const char* name = "AssimpLog.txt", + IOSystem* io = nullptr ); + +}; // !class LogStream + +inline +LogStream::LogStream() AI_NO_EXCEPT { + // empty +} + +inline +LogStream::~LogStream() { + // empty +} + +// ------------------------------------------------------------------------------------ +} // Namespace Assimp + +#endif diff --git a/dependencies/assimp/include/assimp/Logger.hpp b/dependencies/assimp/include/assimp/Logger.hpp new file mode 100644 index 0000000..89cade6 --- /dev/null +++ b/dependencies/assimp/include/assimp/Logger.hpp @@ -0,0 +1,305 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Logger.hpp + * @brief Abstract base class 'Logger', base of the logging system. + */ +#ifndef INCLUDED_AI_LOGGER_H +#define INCLUDED_AI_LOGGER_H + +#include +#include + +namespace Assimp { + +class LogStream; + +// Maximum length of a log message. Longer messages are rejected. +#define MAX_LOG_MESSAGE_LENGTH 1024u + +// ---------------------------------------------------------------------------------- +/** @brief CPP-API: Abstract interface for logger implementations. + * Assimp provides a default implementation and uses it for almost all + * logging stuff ('DefaultLogger'). This class defines just basic logging + * behavior and is not of interest for you. Instead, take a look at #DefaultLogger. */ +class ASSIMP_API Logger +#ifndef SWIG + : public Intern::AllocateFromAssimpHeap +#endif +{ +public: + + // ---------------------------------------------------------------------- + /** @enum LogSeverity + * @brief Log severity to describe the granularity of logging. + */ + enum LogSeverity { + NORMAL, //!< Normal granularity of logging + VERBOSE //!< Debug infos will be logged, too + }; + + // ---------------------------------------------------------------------- + /** @enum ErrorSeverity + * @brief Description for severity of a log message. + * + * Every LogStream has a bitwise combination of these flags. + * A LogStream doesn't receive any messages of a specific type + * if it doesn't specify the corresponding ErrorSeverity flag. + */ + enum ErrorSeverity { + Debugging = 1, //!< Debug log message + Info = 2, //!< Info log message + Warn = 4, //!< Warn log message + Err = 8 //!< Error log message + }; + +public: + + /** @brief Virtual destructor */ + virtual ~Logger(); + + // ---------------------------------------------------------------------- + /** @brief Writes a debug message + * @param message Debug message*/ + void debug(const char* message); + void debug(const std::string &message); + + // ---------------------------------------------------------------------- + /** @brief Writes a info message + * @param message Info message*/ + void info(const char* message); + void info(const std::string &message); + + // ---------------------------------------------------------------------- + /** @brief Writes a warning message + * @param message Warn message*/ + void warn(const char* message); + void warn(const std::string &message); + + // ---------------------------------------------------------------------- + /** @brief Writes an error message + * @param message Error message*/ + void error(const char* message); + void error(const std::string &message); + + // ---------------------------------------------------------------------- + /** @brief Set a new log severity. + * @param log_severity New severity for logging*/ + void setLogSeverity(LogSeverity log_severity); + + // ---------------------------------------------------------------------- + /** @brief Get the current log severity*/ + LogSeverity getLogSeverity() const; + + // ---------------------------------------------------------------------- + /** @brief Attach a new log-stream + * + * The logger takes ownership of the stream and is responsible + * for its destruction (which is done using ::delete when the logger + * itself is destroyed). Call detachStream to detach a stream and to + * gain ownership of it again. + * @param pStream Log-stream to attach + * @param severity Message filter, specified which types of log + * messages are dispatched to the stream. Provide a bitwise + * combination of the ErrorSeverity flags. + * @return true if the stream has been attached, false otherwise.*/ + virtual bool attachStream(LogStream *pStream, + unsigned int severity = Debugging | Err | Warn | Info) = 0; + + // ---------------------------------------------------------------------- + /** @brief Detach a still attached stream from the logger (or + * modify the filter flags bits) + * @param pStream Log-stream instance for detaching + * @param severity Provide a bitwise combination of the ErrorSeverity + * flags. This value is &~ed with the current flags of the stream, + * if the result is 0 the stream is detached from the Logger and + * the caller retakes the possession of the stream. + * @return true if the stream has been detached, false otherwise.*/ + virtual bool detatchStream(LogStream *pStream, + unsigned int severity = Debugging | Err | Warn | Info) = 0; + +protected: + /** + * Default constructor + */ + Logger() AI_NO_EXCEPT; + + /** + * Construction with a given log severity + */ + explicit Logger(LogSeverity severity); + + // ---------------------------------------------------------------------- + /** + * @brief Called as a request to write a specific debug message + * @param message Debug message. Never longer than + * MAX_LOG_MESSAGE_LENGTH characters (excluding the '0'). + * @note The message string is only valid until the scope of + * the function is left. + */ + virtual void OnDebug(const char* message)= 0; + + // ---------------------------------------------------------------------- + /** + * @brief Called as a request to write a specific info message + * @param message Info message. Never longer than + * MAX_LOG_MESSAGE_LENGTH characters (ecxluding the '0'). + * @note The message string is only valid until the scope of + * the function is left. + */ + virtual void OnInfo(const char* message) = 0; + + // ---------------------------------------------------------------------- + /** + * @brief Called as a request to write a specific warn message + * @param message Warn message. Never longer than + * MAX_LOG_MESSAGE_LENGTH characters (exluding the '0'). + * @note The message string is only valid until the scope of + * the function is left. + */ + virtual void OnWarn(const char* essage) = 0; + + // ---------------------------------------------------------------------- + /** + * @brief Called as a request to write a specific error message + * @param message Error message. Never longer than + * MAX_LOG_MESSAGE_LENGTH characters (exluding the '0'). + * @note The message string is only valid until the scope of + * the function is left. + */ + virtual void OnError(const char* message) = 0; + +protected: + LogSeverity m_Severity; +}; + +// ---------------------------------------------------------------------------------- +// Default constructor +inline +Logger::Logger() AI_NO_EXCEPT +: m_Severity(NORMAL) { + // empty +} + +// ---------------------------------------------------------------------------------- +// Virtual destructor +inline +Logger::~Logger() { + // empty +} + +// ---------------------------------------------------------------------------------- +// Construction with given logging severity +inline +Logger::Logger(LogSeverity severity) +: m_Severity(severity) { + // empty +} + +// ---------------------------------------------------------------------------------- +// Log severity setter +inline +void Logger::setLogSeverity(LogSeverity log_severity){ + m_Severity = log_severity; +} + +// ---------------------------------------------------------------------------------- +// Log severity getter +inline +Logger::LogSeverity Logger::getLogSeverity() const { + return m_Severity; +} + +// ---------------------------------------------------------------------------------- +inline +void Logger::debug(const std::string &message) { + return debug(message.c_str()); +} + +// ---------------------------------------------------------------------------------- +inline +void Logger::error(const std::string &message) { + return error(message.c_str()); +} + +// ---------------------------------------------------------------------------------- +inline +void Logger::warn(const std::string &message) { + return warn(message.c_str()); +} + +// ---------------------------------------------------------------------------------- +inline +void Logger::info(const std::string &message) { + return info(message.c_str()); +} + +// ------------------------------------------------------------------------------------------------ +#define ASSIMP_LOG_WARN_F(string,...)\ + DefaultLogger::get()->warn((Formatter::format(string),__VA_ARGS__)) + +#define ASSIMP_LOG_ERROR_F(string,...)\ + DefaultLogger::get()->error((Formatter::format(string),__VA_ARGS__)) + +#define ASSIMP_LOG_DEBUG_F(string,...)\ + DefaultLogger::get()->debug((Formatter::format(string),__VA_ARGS__)) + +#define ASSIMP_LOG_INFO_F(string,...)\ + DefaultLogger::get()->info((Formatter::format(string),__VA_ARGS__)) + + +#define ASSIMP_LOG_WARN(string)\ + DefaultLogger::get()->warn(string) + +#define ASSIMP_LOG_ERROR(string)\ + DefaultLogger::get()->error(string) + +#define ASSIMP_LOG_DEBUG(string)\ + DefaultLogger::get()->debug(string) + +#define ASSIMP_LOG_INFO(string)\ + DefaultLogger::get()->info(string) + + +} // Namespace Assimp + +#endif // !! INCLUDED_AI_LOGGER_H diff --git a/dependencies/assimp/include/assimp/Macros.h b/dependencies/assimp/include/assimp/Macros.h new file mode 100644 index 0000000..6515303 --- /dev/null +++ b/dependencies/assimp/include/assimp/Macros.h @@ -0,0 +1,49 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/* Helper macro to set a pointer to NULL in debug builds + */ +#if (defined ASSIMP_BUILD_DEBUG) +# define AI_DEBUG_INVALIDATE_PTR(x) x = NULL; +#else +# define AI_DEBUG_INVALIDATE_PTR(x) +#endif + diff --git a/dependencies/assimp/include/assimp/MathFunctions.h b/dependencies/assimp/include/assimp/MathFunctions.h new file mode 100644 index 0000000..f49273b --- /dev/null +++ b/dependencies/assimp/include/assimp/MathFunctions.h @@ -0,0 +1,86 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2016, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +#pragma once + +/** @file MathFunctions.h +* @brief Implementation of math utility functions. + * +*/ + +#include + +namespace Assimp { +namespace Math { + +// TODO: use binary GCD for unsigned integers .... +template < typename IntegerType > +inline +IntegerType gcd( IntegerType a, IntegerType b ) { + const IntegerType zero = (IntegerType)0; + while ( true ) { + if ( a == zero ) + return b; + b %= a; + + if ( b == zero ) + return a; + a %= b; + } +} + +template < typename IntegerType > +inline +IntegerType lcm( IntegerType a, IntegerType b ) { + const IntegerType t = gcd (a,b); + if (!t) + return t; + return a / t * b; +} + +template +inline +T getEpsilon() { + return std::numeric_limits::epsilon(); +} + +} +} diff --git a/dependencies/assimp/include/assimp/MemoryIOWrapper.h b/dependencies/assimp/include/assimp/MemoryIOWrapper.h new file mode 100644 index 0000000..c522787 --- /dev/null +++ b/dependencies/assimp/include/assimp/MemoryIOWrapper.h @@ -0,0 +1,244 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file MemoryIOWrapper.h + * Handy IOStream/IOSystem implemetation to read directly from a memory buffer */ +#ifndef AI_MEMORYIOSTREAM_H_INC +#define AI_MEMORYIOSTREAM_H_INC + +#include +#include +#include +#include + +namespace Assimp { + +#define AI_MEMORYIO_MAGIC_FILENAME "$$$___magic___$$$" +#define AI_MEMORYIO_MAGIC_FILENAME_LENGTH 17 + +// ---------------------------------------------------------------------------------- +/** Implementation of IOStream to read directly from a memory buffer */ +// ---------------------------------------------------------------------------------- +class MemoryIOStream : public IOStream { +public: + MemoryIOStream (const uint8_t* buff, size_t len, bool own = false) + : buffer (buff) + , length(len) + , pos((size_t)0) + , own(own) { + // empty + } + + ~MemoryIOStream () { + if(own) { + delete[] buffer; + } + } + + // ------------------------------------------------------------------- + // Read from stream + size_t Read(void* pvBuffer, size_t pSize, size_t pCount) { + ai_assert(nullptr != pvBuffer); + ai_assert(0 != pSize); + + const size_t cnt = std::min( pCount, (length-pos) / pSize); + const size_t ofs = pSize * cnt; + + ::memcpy(pvBuffer,buffer+pos,ofs); + pos += ofs; + + return cnt; + } + + // ------------------------------------------------------------------- + // Write to stream + size_t Write(const void* /*pvBuffer*/, size_t /*pSize*/,size_t /*pCount*/) { + ai_assert(false); // won't be needed + return 0; + } + + // ------------------------------------------------------------------- + // Seek specific position + aiReturn Seek(size_t pOffset, aiOrigin pOrigin) { + if (aiOrigin_SET == pOrigin) { + if (pOffset > length) { + return AI_FAILURE; + } + pos = pOffset; + } else if (aiOrigin_END == pOrigin) { + if (pOffset > length) { + return AI_FAILURE; + } + pos = length-pOffset; + } else { + if (pOffset+pos > length) { + return AI_FAILURE; + } + pos += pOffset; + } + return AI_SUCCESS; + } + + // ------------------------------------------------------------------- + // Get current seek position + size_t Tell() const { + return pos; + } + + // ------------------------------------------------------------------- + // Get size of file + size_t FileSize() const { + return length; + } + + // ------------------------------------------------------------------- + // Flush file contents + void Flush() { + ai_assert(false); // won't be needed + } + +private: + const uint8_t* buffer; + size_t length,pos; + bool own; +}; + +// --------------------------------------------------------------------------- +/** Dummy IO system to read from a memory buffer */ +class MemoryIOSystem : public IOSystem { +public: + /** Constructor. */ + MemoryIOSystem(const uint8_t* buff, size_t len, IOSystem* io) + : buffer(buff) + , length(len) + , existing_io(io) + , created_streams() { + // empty + } + + /** Destructor. */ + ~MemoryIOSystem() { + } + + // ------------------------------------------------------------------- + /** Tests for the existence of a file at the given path. */ + bool Exists(const char* pFile) const override { + if (0 == strncmp( pFile, AI_MEMORYIO_MAGIC_FILENAME, AI_MEMORYIO_MAGIC_FILENAME_LENGTH ) ) { + return true; + } + return existing_io ? existing_io->Exists(pFile) : false; + } + + // ------------------------------------------------------------------- + /** Returns the directory separator. */ + char getOsSeparator() const override { + return existing_io ? existing_io->getOsSeparator() + : '/'; // why not? it doesn't care + } + + // ------------------------------------------------------------------- + /** Open a new file with a given path. */ + IOStream* Open(const char* pFile, const char* pMode = "rb") override { + if ( 0 == strncmp( pFile, AI_MEMORYIO_MAGIC_FILENAME, AI_MEMORYIO_MAGIC_FILENAME_LENGTH ) ) { + created_streams.emplace_back(new MemoryIOStream(buffer, length)); + return created_streams.back(); + } + return existing_io ? existing_io->Open(pFile, pMode) : NULL; + } + + // ------------------------------------------------------------------- + /** Closes the given file and releases all resources associated with it. */ + void Close( IOStream* pFile) override { + auto it = std::find(created_streams.begin(), created_streams.end(), pFile); + if (it != created_streams.end()) { + delete pFile; + created_streams.erase(it); + } else if (existing_io) { + existing_io->Close(pFile); + } + } + + // ------------------------------------------------------------------- + /** Compare two paths */ + bool ComparePaths(const char* one, const char* second) const override { + return existing_io ? existing_io->ComparePaths(one, second) : false; + } + + bool PushDirectory( const std::string &path ) override { + return existing_io ? existing_io->PushDirectory(path) : false; + } + + const std::string &CurrentDirectory() const override { + static std::string empty; + return existing_io ? existing_io->CurrentDirectory() : empty; + } + + size_t StackSize() const override { + return existing_io ? existing_io->StackSize() : 0; + } + + bool PopDirectory() override { + return existing_io ? existing_io->PopDirectory() : false; + } + + bool CreateDirectory( const std::string &path ) override { + return existing_io ? existing_io->CreateDirectory(path) : false; + } + + bool ChangeDirectory( const std::string &path ) override { + return existing_io ? existing_io->ChangeDirectory(path) : false; + } + + bool DeleteFile( const std::string &file ) override { + return existing_io ? existing_io->DeleteFile(file) : false; + } + +private: + const uint8_t* buffer; + size_t length; + IOSystem* existing_io; + std::vector created_streams; +}; + +} // end namespace Assimp + +#endif diff --git a/dependencies/assimp/include/assimp/NullLogger.hpp b/dependencies/assimp/include/assimp/NullLogger.hpp new file mode 100644 index 0000000..c45d01b --- /dev/null +++ b/dependencies/assimp/include/assimp/NullLogger.hpp @@ -0,0 +1,99 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file NullLogger.hpp + * @brief Dummy logger +*/ + +#ifndef INCLUDED_AI_NULLLOGGER_H +#define INCLUDED_AI_NULLLOGGER_H + +#include "Logger.hpp" + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** @brief CPP-API: Empty logging implementation. + * + * Does nothing! Used by default if the application hasn't requested a + * custom logger via #DefaultLogger::set() or #DefaultLogger::create(); */ +class ASSIMP_API NullLogger + : public Logger { + +public: + + /** @brief Logs a debug message */ + void OnDebug(const char* message) { + (void)message; //this avoids compiler warnings + } + + /** @brief Logs an info message */ + void OnInfo(const char* message) { + (void)message; //this avoids compiler warnings + } + + /** @brief Logs a warning message */ + void OnWarn(const char* message) { + (void)message; //this avoids compiler warnings + } + + /** @brief Logs an error message */ + void OnError(const char* message) { + (void)message; //this avoids compiler warnings + } + + /** @brief Detach a still attached stream from logger */ + bool attachStream(LogStream *pStream, unsigned int severity) { + (void)pStream; (void)severity; //this avoids compiler warnings + return false; + } + + /** @brief Detach a still attached stream from logger */ + bool detatchStream(LogStream *pStream, unsigned int severity) { + (void)pStream; (void)severity; //this avoids compiler warnings + return false; + } + +private: +}; +} +#endif // !! AI_NULLLOGGER_H_INCLUDED diff --git a/dependencies/assimp/include/assimp/ParsingUtils.h b/dependencies/assimp/include/assimp/ParsingUtils.h new file mode 100644 index 0000000..6b9574f --- /dev/null +++ b/dependencies/assimp/include/assimp/ParsingUtils.h @@ -0,0 +1,259 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + + +/** @file ParsingUtils.h + * @brief Defines helper functions for text parsing + */ +#ifndef AI_PARSING_UTILS_H_INC +#define AI_PARSING_UTILS_H_INC + +#include "StringComparison.h" +#include "StringUtils.h" +#include + +namespace Assimp { + +// NOTE: the functions below are mostly intended as replacement for +// std::upper, std::lower, std::isupper, std::islower, std::isspace. +// we don't bother of locales. We don't want them. We want reliable +// (i.e. identical) results across all locales. + +// The functions below accept any character type, but know only +// about ASCII. However, UTF-32 is the only safe ASCII superset to +// use since it doesn't have multi-byte sequences. + +static const unsigned int BufferSize = 4096; + +// --------------------------------------------------------------------------------- +template +AI_FORCE_INLINE +char_t ToLower( char_t in ) { + return (in >= (char_t)'A' && in <= (char_t)'Z') ? (char_t)(in+0x20) : in; +} + +// --------------------------------------------------------------------------------- +template +AI_FORCE_INLINE +char_t ToUpper( char_t in) { + return (in >= (char_t)'a' && in <= (char_t)'z') ? (char_t)(in-0x20) : in; +} + +// --------------------------------------------------------------------------------- +template +AI_FORCE_INLINE +bool IsUpper( char_t in) { + return (in >= (char_t)'A' && in <= (char_t)'Z'); +} + +// --------------------------------------------------------------------------------- +template +AI_FORCE_INLINE +bool IsLower( char_t in) { + return (in >= (char_t)'a' && in <= (char_t)'z'); +} + +// --------------------------------------------------------------------------------- +template +AI_FORCE_INLINE +bool IsSpace( char_t in) { + return (in == (char_t)' ' || in == (char_t)'\t'); +} + +// --------------------------------------------------------------------------------- +template +AI_FORCE_INLINE +bool IsLineEnd( char_t in) { + return (in==(char_t)'\r'||in==(char_t)'\n'||in==(char_t)'\0'||in==(char_t)'\f'); +} + +// --------------------------------------------------------------------------------- +template +AI_FORCE_INLINE +bool IsSpaceOrNewLine( char_t in) { + return IsSpace(in) || IsLineEnd(in); +} + +// --------------------------------------------------------------------------------- +template +AI_FORCE_INLINE +bool SkipSpaces( const char_t* in, const char_t** out) { + while( *in == ( char_t )' ' || *in == ( char_t )'\t' ) { + ++in; + } + *out = in; + return !IsLineEnd(*in); +} + +// --------------------------------------------------------------------------------- +template +AI_FORCE_INLINE +bool SkipSpaces( const char_t** inout) { + return SkipSpaces(*inout,inout); +} + +// --------------------------------------------------------------------------------- +template +AI_FORCE_INLINE +bool SkipLine( const char_t* in, const char_t** out) { + while( *in != ( char_t )'\r' && *in != ( char_t )'\n' && *in != ( char_t )'\0' ) { + ++in; + } + + // files are opened in binary mode. Ergo there are both NL and CR + while( *in == ( char_t )'\r' || *in == ( char_t )'\n' ) { + ++in; + } + *out = in; + return *in != (char_t)'\0'; +} + +// --------------------------------------------------------------------------------- +template +AI_FORCE_INLINE +bool SkipLine( const char_t** inout) { + return SkipLine(*inout,inout); +} + +// --------------------------------------------------------------------------------- +template +AI_FORCE_INLINE +bool SkipSpacesAndLineEnd( const char_t* in, const char_t** out) { + while( *in == ( char_t )' ' || *in == ( char_t )'\t' || *in == ( char_t )'\r' || *in == ( char_t )'\n' ) { + ++in; + } + *out = in; + return *in != '\0'; +} + +// --------------------------------------------------------------------------------- +template +AI_FORCE_INLINE +bool SkipSpacesAndLineEnd( const char_t** inout) { + return SkipSpacesAndLineEnd(*inout,inout); +} + +// --------------------------------------------------------------------------------- +template +AI_FORCE_INLINE +bool GetNextLine( const char_t*& buffer, char_t out[ BufferSize ] ) { + if( ( char_t )'\0' == *buffer ) { + return false; + } + + char* _out = out; + char* const end = _out + BufferSize; + while( !IsLineEnd( *buffer ) && _out < end ) { + *_out++ = *buffer++; + } + *_out = (char_t)'\0'; + + while( IsLineEnd( *buffer ) && '\0' != *buffer ) { + ++buffer; + } + + return true; +} + +// --------------------------------------------------------------------------------- +template +AI_FORCE_INLINE bool IsNumeric( char_t in) { + return ( in >= '0' && in <= '9' ) || '-' == in || '+' == in; +} + +// --------------------------------------------------------------------------------- +template +AI_FORCE_INLINE +bool TokenMatch(char_t*& in, const char* token, unsigned int len) +{ + if (!::strncmp(token,in,len) && IsSpaceOrNewLine(in[len])) { + if (in[len] != '\0') { + in += len+1; + } else { + // If EOF after the token make sure we don't go past end of buffer + in += len; + } + return true; + } + + return false; +} +// --------------------------------------------------------------------------------- +/** @brief Case-ignoring version of TokenMatch + * @param in Input + * @param token Token to check for + * @param len Number of characters to check + */ +AI_FORCE_INLINE +bool TokenMatchI(const char*& in, const char* token, unsigned int len) { + if (!ASSIMP_strincmp(token,in,len) && IsSpaceOrNewLine(in[len])) { + in += len+1; + return true; + } + return false; +} + +// --------------------------------------------------------------------------------- +AI_FORCE_INLINE +void SkipToken(const char*& in) { + SkipSpaces(&in); + while ( !IsSpaceOrNewLine( *in ) ) { + ++in; + } +} + +// --------------------------------------------------------------------------------- +AI_FORCE_INLINE +std::string GetNextToken(const char*& in) { + SkipSpacesAndLineEnd(&in); + const char* cur = in; + while ( !IsSpaceOrNewLine( *in ) ) { + ++in; + } + return std::string(cur,(size_t)(in-cur)); +} + +// --------------------------------------------------------------------------------- + +} // ! namespace Assimp + +#endif // ! AI_PARSING_UTILS_H_INC diff --git a/dependencies/assimp/include/assimp/Profiler.h b/dependencies/assimp/include/assimp/Profiler.h new file mode 100644 index 0000000..6ff9d41 --- /dev/null +++ b/dependencies/assimp/include/assimp/Profiler.h @@ -0,0 +1,99 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Profiler.h + * @brief Utility to measure the respective runtime of each import step + */ +#ifndef INCLUDED_PROFILER_H +#define INCLUDED_PROFILER_H + +#include +#include +#include "TinyFormatter.h" + +#include + +namespace Assimp { +namespace Profiling { + +using namespace Formatter; + +// ------------------------------------------------------------------------------------------------ +/** Simple wrapper around boost::timer to simplify reporting. Timings are automatically + * dumped to the log file. + */ +class Profiler { +public: + Profiler() { + // empty + } + +public: + + /** Start a named timer */ + void BeginRegion(const std::string& region) { + regions[region] = std::chrono::system_clock::now(); + ASSIMP_LOG_DEBUG((format("START `"),region,"`")); + } + + + /** End a specific named timer and write its end time to the log */ + void EndRegion(const std::string& region) { + RegionMap::const_iterator it = regions.find(region); + if (it == regions.end()) { + return; + } + + std::chrono::duration elapsedSeconds = std::chrono::system_clock::now() - regions[region]; + ASSIMP_LOG_DEBUG((format("END `"),region,"`, dt= ", elapsedSeconds.count()," s")); + } + +private: + typedef std::map> RegionMap; + RegionMap regions; +}; + +} +} + +#endif + diff --git a/dependencies/assimp/include/assimp/ProgressHandler.hpp b/dependencies/assimp/include/assimp/ProgressHandler.hpp new file mode 100644 index 0000000..4e47f1d --- /dev/null +++ b/dependencies/assimp/include/assimp/ProgressHandler.hpp @@ -0,0 +1,145 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file ProgressHandler.hpp + * @brief Abstract base class 'ProgressHandler'. + */ +#pragma once +#ifndef AI_PROGRESSHANDLER_H_INC +#define AI_PROGRESSHANDLER_H_INC + +#include "types.h" + +namespace Assimp { + +// ------------------------------------------------------------------------------------ +/** @brief CPP-API: Abstract interface for custom progress report receivers. + * + * Each #Importer instance maintains its own #ProgressHandler. The default + * implementation provided by Assimp doesn't do anything at all. */ +class ASSIMP_API ProgressHandler +#ifndef SWIG + : public Intern::AllocateFromAssimpHeap +#endif +{ +protected: + /// @brief Default constructor + ProgressHandler () AI_NO_EXCEPT { + // empty + } + +public: + /// @brief Virtual destructor. + virtual ~ProgressHandler () { + } + + // ------------------------------------------------------------------- + /** @brief Progress callback. + * @param percentage An estimate of the current loading progress, + * in percent. Or -1.f if such an estimate is not available. + * + * There are restriction on what you may do from within your + * implementation of this method: no exceptions may be thrown and no + * non-const #Importer methods may be called. It is + * not generally possible to predict the number of callbacks + * fired during a single import. + * + * @return Return false to abort loading at the next possible + * occasion (loaders and Assimp are generally allowed to perform + * all needed cleanup tasks prior to returning control to the + * caller). If the loading is aborted, #Importer::ReadFile() + * returns always NULL. + * */ + virtual bool Update(float percentage = -1.f) = 0; + + // ------------------------------------------------------------------- + /** @brief Progress callback for file loading steps + * @param numberOfSteps The number of total post-processing + * steps + * @param currentStep The index of the current post-processing + * step that will run, or equal to numberOfSteps if all of + * them has finished. This number is always strictly monotone + * increasing, although not necessarily linearly. + * + * @note This is currently only used at the start and the end + * of the file parsing. + * */ + virtual void UpdateFileRead(int currentStep /*= 0*/, int numberOfSteps /*= 0*/) { + float f = numberOfSteps ? currentStep / (float)numberOfSteps : 1.0f; + Update( f * 0.5f ); + } + + // ------------------------------------------------------------------- + /** @brief Progress callback for post-processing steps + * @param numberOfSteps The number of total post-processing + * steps + * @param currentStep The index of the current post-processing + * step that will run, or equal to numberOfSteps if all of + * them has finished. This number is always strictly monotone + * increasing, although not necessarily linearly. + * */ + virtual void UpdatePostProcess(int currentStep /*= 0*/, int numberOfSteps /*= 0*/) { + float f = numberOfSteps ? currentStep / (float)numberOfSteps : 1.0f; + Update( f * 0.5f + 0.5f ); + } + + + // ------------------------------------------------------------------- + /** @brief Progress callback for export steps. + * @param numberOfSteps The number of total processing + * steps + * @param currentStep The index of the current post-processing + * step that will run, or equal to numberOfSteps if all of + * them has finished. This number is always strictly monotone + * increasing, although not necessarily linearly. + * */ + virtual void UpdateFileWrite(int currentStep /*= 0*/, int numberOfSteps /*= 0*/) { + float f = numberOfSteps ? currentStep / (float)numberOfSteps : 1.0f; + Update(f * 0.5f); + } +}; // !class ProgressHandler + +// ------------------------------------------------------------------------------------ + +} // Namespace Assimp + +#endif // AI_PROGRESSHANDLER_H_INC diff --git a/dependencies/assimp/include/assimp/RemoveComments.h b/dependencies/assimp/include/assimp/RemoveComments.h new file mode 100644 index 0000000..404b496 --- /dev/null +++ b/dependencies/assimp/include/assimp/RemoveComments.h @@ -0,0 +1,91 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Declares a helper class, "CommentRemover", which can be + * used to remove comments (single and multi line) from a text file. + */ +#ifndef AI_REMOVE_COMMENTS_H_INC +#define AI_REMOVE_COMMENTS_H_INC + + +#include + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** \brief Helper class to remove single and multi line comments from a file + * + * Some mesh formats like MD5 have comments that are quite similar + * to those in C or C++ so this code has been moved to a separate + * module. + */ +class ASSIMP_API CommentRemover +{ + // class cannot be instanced + CommentRemover() {} + +public: + + //! Remove single-line comments. The end of a line is + //! expected to be either NL or CR or NLCR. + //! \param szComment The start sequence of the comment, e.g. "//" + //! \param szBuffer Buffer to work with + //! \param chReplacement Character to be used as replacement + //! for commented lines. By default this is ' ' + static void RemoveLineComments(const char* szComment, + char* szBuffer, char chReplacement = ' '); + + //! Remove multi-line comments. The end of a line is + //! expected to be either NL or CR or NLCR. Multi-line comments + //! may not be nested (as in C). + //! \param szCommentStart The start sequence of the comment, e.g. "/*" + //! \param szCommentEnd The end sequence of the comment, e.g. "*/" + //! \param szBuffer Buffer to work with + //! \param chReplacement Character to be used as replacement + //! for commented lines. By default this is ' ' + static void RemoveMultiLineComments(const char* szCommentStart, + const char* szCommentEnd,char* szBuffer, + char chReplacement = ' '); +}; +} // ! Assimp + +#endif // !! AI_REMOVE_COMMENTS_H_INC diff --git a/dependencies/assimp/include/assimp/SGSpatialSort.h b/dependencies/assimp/include/assimp/SGSpatialSort.h new file mode 100644 index 0000000..5b4f3f4 --- /dev/null +++ b/dependencies/assimp/include/assimp/SGSpatialSort.h @@ -0,0 +1,150 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** Small helper classes to optimize finding vertices close to a given location + */ +#ifndef AI_D3DSSPATIALSORT_H_INC +#define AI_D3DSSPATIALSORT_H_INC + +#include +#include +#include + +namespace Assimp { + +// ---------------------------------------------------------------------------------- +/** Specialized version of SpatialSort to support smoothing groups + * This is used in by the 3DS, ASE and LWO loaders. 3DS and ASE share their + * normal computation code in SmoothingGroups.inl, the LWO loader has its own + * implementation to handle all details of its file format correctly. + */ +// ---------------------------------------------------------------------------------- +class ASSIMP_API SGSpatialSort +{ +public: + + SGSpatialSort(); + + // ------------------------------------------------------------------- + /** Construction from a given face array, handling smoothing groups + * properly + */ + explicit SGSpatialSort(const std::vector& vPositions); + + // ------------------------------------------------------------------- + /** Add a vertex to the spatial sort + * @param vPosition Vertex position to be added + * @param index Index of the vrtex + * @param smoothingGroup SmoothingGroup for this vertex + */ + void Add(const aiVector3D& vPosition, unsigned int index, + unsigned int smoothingGroup); + + // ------------------------------------------------------------------- + /** Prepare the spatial sorter for use. This step runs in O(logn) + */ + void Prepare(); + + /** Destructor */ + ~SGSpatialSort(); + + // ------------------------------------------------------------------- + /** Returns an iterator for all positions close to the given position. + * @param pPosition The position to look for vertices. + * @param pSG Only included vertices with at least one shared smooth group + * @param pRadius Maximal distance from the position a vertex may have + * to be counted in. + * @param poResults The container to store the indices of the found + * positions. Will be emptied by the call so it may contain anything. + * @param exactMatch Specifies whether smoothing groups are bit masks + * (false) or integral values (true). In the latter case, a vertex + * cannot belong to more than one smoothing group. + * @return An iterator to iterate over all vertices in the given area. + */ + // ------------------------------------------------------------------- + void FindPositions( const aiVector3D& pPosition, uint32_t pSG, + float pRadius, std::vector& poResults, + bool exactMatch = false) const; + +protected: + /** Normal of the sorting plane, normalized. The center is always at (0, 0, 0) */ + aiVector3D mPlaneNormal; + + // ------------------------------------------------------------------- + /** An entry in a spatially sorted position array. Consists of a + * vertex index, its position and its pre-calculated distance from + * the reference plane */ + // ------------------------------------------------------------------- + struct Entry { + unsigned int mIndex; ///< The vertex referred by this entry + aiVector3D mPosition; ///< Position + uint32_t mSmoothGroups; + float mDistance; ///< Distance of this vertex to the sorting plane + + Entry() AI_NO_EXCEPT + : mIndex(0) + , mPosition() + , mSmoothGroups(0) + , mDistance(0.0f) { + // empty + } + + Entry( unsigned int pIndex, const aiVector3D& pPosition, float pDistance,uint32_t pSG) + : mIndex( pIndex) + , mPosition( pPosition) + , mSmoothGroups(pSG) + , mDistance( pDistance) { + // empty + } + + bool operator < (const Entry& e) const { + return mDistance < e.mDistance; + } + }; + + // all positions, sorted by distance to the sorting plane + std::vector mPositions; +}; + +} // end of namespace Assimp + +#endif // AI_SPATIALSORT_H_INC diff --git a/dependencies/assimp/include/assimp/SceneCombiner.h b/dependencies/assimp/include/assimp/SceneCombiner.h new file mode 100644 index 0000000..f69a25f --- /dev/null +++ b/dependencies/assimp/include/assimp/SceneCombiner.h @@ -0,0 +1,403 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Declares a helper class, "SceneCombiner" providing various + * utilities to merge scenes. + */ +#ifndef AI_SCENE_COMBINER_H_INC +#define AI_SCENE_COMBINER_H_INC + +#include +#include +#include +#include +#include +#include +#include + +#include + +struct aiScene; +struct aiNode; +struct aiMaterial; +struct aiTexture; +struct aiCamera; +struct aiLight; +struct aiMetadata; +struct aiBone; +struct aiMesh; +struct aiAnimMesh; +struct aiAnimation; +struct aiNodeAnim; + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** \brief Helper data structure for SceneCombiner. + * + * Describes to which node a scene must be attached to. + */ +struct AttachmentInfo +{ + AttachmentInfo() + : scene (NULL) + , attachToNode (NULL) + {} + + AttachmentInfo(aiScene* _scene, aiNode* _attachToNode) + : scene (_scene) + , attachToNode (_attachToNode) + {} + + aiScene* scene; + aiNode* attachToNode; +}; + +// --------------------------------------------------------------------------- +struct NodeAttachmentInfo +{ + NodeAttachmentInfo() + : node (NULL) + , attachToNode (NULL) + , resolved (false) + , src_idx (SIZE_MAX) + {} + + NodeAttachmentInfo(aiNode* _scene, aiNode* _attachToNode,size_t idx) + : node (_scene) + , attachToNode (_attachToNode) + , resolved (false) + , src_idx (idx) + {} + + aiNode* node; + aiNode* attachToNode; + bool resolved; + size_t src_idx; +}; + +// --------------------------------------------------------------------------- +/** @def AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES + * Generate unique names for all named scene items + */ +#define AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES 0x1 + +/** @def AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES + * Generate unique names for materials, too. + * This is not absolutely required to pass the validation. + */ +#define AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES 0x2 + +/** @def AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY + * Use deep copies of duplicate scenes + */ +#define AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY 0x4 + +/** @def AI_INT_MERGE_SCENE_RESOLVE_CROSS_ATTACHMENTS + * If attachment nodes are not found in the given master scene, + * search the other imported scenes for them in an any order. + */ +#define AI_INT_MERGE_SCENE_RESOLVE_CROSS_ATTACHMENTS 0x8 + +/** @def AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY + * Can be combined with AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES. + * Unique names are generated, but only if this is absolutely + * required to avoid name conflicts. + */ +#define AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY 0x10 + +typedef std::pair BoneSrcIndex; + +// --------------------------------------------------------------------------- +/** @brief Helper data structure for SceneCombiner::MergeBones. + */ +struct BoneWithHash : public std::pair { + std::vector pSrcBones; +}; + +// --------------------------------------------------------------------------- +/** @brief Utility for SceneCombiner + */ +struct SceneHelper +{ + SceneHelper () + : scene (NULL) + , idlen (0) + { + id[0] = 0; + } + + explicit SceneHelper (aiScene* _scene) + : scene (_scene) + , idlen (0) + { + id[0] = 0; + } + + AI_FORCE_INLINE aiScene* operator-> () const + { + return scene; + } + + // scene we're working on + aiScene* scene; + + // prefix to be added to all identifiers in the scene ... + char id [32]; + + // and its strlen() + unsigned int idlen; + + // hash table to quickly check whether a name is contained in the scene + std::set hashes; +}; + +// --------------------------------------------------------------------------- +/** \brief Static helper class providing various utilities to merge two + * scenes. It is intended as internal utility and NOT for use by + * applications. + * + * The class is currently being used by various postprocessing steps + * and loaders (ie. LWS). + */ +class ASSIMP_API SceneCombiner { + // class cannot be instanced + SceneCombiner() { + // empty + } + + ~SceneCombiner() { + // empty + } + +public: + // ------------------------------------------------------------------- + /** Merges two or more scenes. + * + * @param dest Receives a pointer to the destination scene. If the + * pointer doesn't point to NULL when the function is called, the + * existing scene is cleared and refilled. + * @param src Non-empty list of scenes to be merged. The function + * deletes the input scenes afterwards. There may be duplicate scenes. + * @param flags Combination of the AI_INT_MERGE_SCENE flags defined above + */ + static void MergeScenes(aiScene** dest,std::vector& src, + unsigned int flags = 0); + + // ------------------------------------------------------------------- + /** Merges two or more scenes and attaches all scenes to a specific + * position in the node graph of the master scene. + * + * @param dest Receives a pointer to the destination scene. If the + * pointer doesn't point to NULL when the function is called, the + * existing scene is cleared and refilled. + * @param master Master scene. It will be deleted afterwards. All + * other scenes will be inserted in its node graph. + * @param src Non-empty list of scenes to be merged along with their + * corresponding attachment points in the master scene. The function + * deletes the input scenes afterwards. There may be duplicate scenes. + * @param flags Combination of the AI_INT_MERGE_SCENE flags defined above + */ + static void MergeScenes(aiScene** dest, aiScene* master, + std::vector& src, + unsigned int flags = 0); + + // ------------------------------------------------------------------- + /** Merges two or more meshes + * + * The meshes should have equal vertex formats. Only components + * that are provided by ALL meshes will be present in the output mesh. + * An exception is made for VColors - they are set to black. The + * meshes should have the same material indices, too. The output + * material index is always the material index of the first mesh. + * + * @param dest Destination mesh. Must be empty. + * @param flags Currently no parameters + * @param begin First mesh to be processed + * @param end Points to the mesh after the last mesh to be processed + */ + static void MergeMeshes(aiMesh** dest,unsigned int flags, + std::vector::const_iterator begin, + std::vector::const_iterator end); + + // ------------------------------------------------------------------- + /** Merges two or more bones + * + * @param out Mesh to receive the output bone list + * @param flags Currently no parameters + * @param begin First mesh to be processed + * @param end Points to the mesh after the last mesh to be processed + */ + static void MergeBones(aiMesh* out,std::vector::const_iterator it, + std::vector::const_iterator end); + + // ------------------------------------------------------------------- + /** Merges two or more materials + * + * The materials should be complementary as much as possible. In case + * of a property present in different materials, the first occurrence + * is used. + * + * @param dest Destination material. Must be empty. + * @param begin First material to be processed + * @param end Points to the material after the last material to be processed + */ + static void MergeMaterials(aiMaterial** dest, + std::vector::const_iterator begin, + std::vector::const_iterator end); + + // ------------------------------------------------------------------- + /** Builds a list of uniquely named bones in a mesh list + * + * @param asBones Receives the output list + * @param it First mesh to be processed + * @param end Last mesh to be processed + */ + static void BuildUniqueBoneList(std::list& asBones, + std::vector::const_iterator it, + std::vector::const_iterator end); + + // ------------------------------------------------------------------- + /** Add a name prefix to all nodes in a scene. + * + * @param Current node. This function is called recursively. + * @param prefix Prefix to be added to all nodes + * @param len STring length + */ + static void AddNodePrefixes(aiNode* node, const char* prefix, + unsigned int len); + + // ------------------------------------------------------------------- + /** Add an offset to all mesh indices in a node graph + * + * @param Current node. This function is called recursively. + * @param offset Offset to be added to all mesh indices + */ + static void OffsetNodeMeshIndices (aiNode* node, unsigned int offset); + + // ------------------------------------------------------------------- + /** Attach a list of node graphs to well-defined nodes in a master + * graph. This is a helper for MergeScenes() + * + * @param master Master scene + * @param srcList List of source scenes along with their attachment + * points. If an attachment point is NULL (or does not exist in + * the master graph), a scene is attached to the root of the master + * graph (as an additional child node) + * @duplicates List of duplicates. If elem[n] == n the scene is not + * a duplicate. Otherwise elem[n] links scene n to its first occurrence. + */ + static void AttachToGraph ( aiScene* master, + std::vector& srcList); + + static void AttachToGraph (aiNode* attach, + std::vector& srcList); + + + // ------------------------------------------------------------------- + /** Get a deep copy of a scene + * + * @param dest Receives a pointer to the destination scene + * @param src Source scene - remains unmodified. + */ + static void CopyScene(aiScene** dest,const aiScene* source,bool allocate = true); + + + // ------------------------------------------------------------------- + /** Get a flat copy of a scene + * + * Only the first hierarchy layer is copied. All pointer members of + * aiScene are shared by source and destination scene. If the + * pointer doesn't point to NULL when the function is called, the + * existing scene is cleared and refilled. + * @param dest Receives a pointer to the destination scene + * @param src Source scene - remains unmodified. + */ + static void CopySceneFlat(aiScene** dest,const aiScene* source); + + + // ------------------------------------------------------------------- + /** Get a deep copy of a mesh + * + * @param dest Receives a pointer to the destination mesh + * @param src Source mesh - remains unmodified. + */ + static void Copy (aiMesh** dest, const aiMesh* src); + + // similar to Copy(): + static void Copy (aiAnimMesh** dest, const aiAnimMesh* src); + static void Copy (aiMaterial** dest, const aiMaterial* src); + static void Copy (aiTexture** dest, const aiTexture* src); + static void Copy (aiAnimation** dest, const aiAnimation* src); + static void Copy (aiCamera** dest, const aiCamera* src); + static void Copy (aiBone** dest, const aiBone* src); + static void Copy (aiLight** dest, const aiLight* src); + static void Copy (aiNodeAnim** dest, const aiNodeAnim* src); + static void Copy (aiMetadata** dest, const aiMetadata* src); + + // recursive, of course + static void Copy (aiNode** dest, const aiNode* src); + + +private: + + // ------------------------------------------------------------------- + // Same as AddNodePrefixes, but with an additional check + static void AddNodePrefixesChecked(aiNode* node, const char* prefix, + unsigned int len, + std::vector& input, + unsigned int cur); + + // ------------------------------------------------------------------- + // Add node identifiers to a hashing set + static void AddNodeHashes(aiNode* node, std::set& hashes); + + + // ------------------------------------------------------------------- + // Search for duplicate names + static bool FindNameMatch(const aiString& name, + std::vector& input, unsigned int cur); +}; + +} + +#endif // !! AI_SCENE_COMBINER_H_INC diff --git a/dependencies/assimp/include/assimp/SkeletonMeshBuilder.h b/dependencies/assimp/include/assimp/SkeletonMeshBuilder.h new file mode 100644 index 0000000..f9b8d9f --- /dev/null +++ b/dependencies/assimp/include/assimp/SkeletonMeshBuilder.h @@ -0,0 +1,125 @@ +/** Helper class to construct a dummy mesh for file formats containing only motion data */ + +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file SkeletonMeshBuilder.h + * Declares SkeletonMeshBuilder, a tiny utility to build dummy meshes + * for animation skeletons. + */ + +#ifndef AI_SKELETONMESHBUILDER_H_INC +#define AI_SKELETONMESHBUILDER_H_INC + +#include +#include + +struct aiMaterial; +struct aiScene; +struct aiNode; + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** + * This little helper class constructs a dummy mesh for a given scene + * the resembles the node hierarchy. This is useful for file formats + * that don't carry any mesh data but only animation data. + */ +class ASSIMP_API SkeletonMeshBuilder +{ +public: + + // ------------------------------------------------------------------- + /** The constructor processes the given scene and adds a mesh there. + * + * Does nothing if the scene already has mesh data. + * @param pScene The scene for which a skeleton mesh should be constructed. + * @param root The node to start with. NULL is the scene root + * @param bKnobsOnly Set this to true if you don't want the connectors + * between the knobs representing the nodes. + */ + SkeletonMeshBuilder( aiScene* pScene, aiNode* root = NULL, + bool bKnobsOnly = false); + +protected: + + // ------------------------------------------------------------------- + /** Recursively builds a simple mesh representation for the given node + * and also creates a joint for the node that affects this part of + * the mesh. + * @param pNode The node to build geometry for. + */ + void CreateGeometry( const aiNode* pNode); + + // ------------------------------------------------------------------- + /** Creates the mesh from the internally accumulated stuff and returns it. + */ + aiMesh* CreateMesh(); + + // ------------------------------------------------------------------- + /** Creates a dummy material and returns it. */ + aiMaterial* CreateMaterial(); + +protected: + /** space to assemble the mesh data: points */ + std::vector mVertices; + + /** faces */ + struct Face + { + unsigned int mIndices[3]; + Face(); + Face( unsigned int p0, unsigned int p1, unsigned int p2) + { mIndices[0] = p0; mIndices[1] = p1; mIndices[2] = p2; } + }; + std::vector mFaces; + + /** bones */ + std::vector mBones; + + bool mKnobsOnly; +}; + +} // end of namespace Assimp + +#endif // AI_SKELETONMESHBUILDER_H_INC diff --git a/dependencies/assimp/include/assimp/SmoothingGroups.h b/dependencies/assimp/include/assimp/SmoothingGroups.h new file mode 100644 index 0000000..92d65ce --- /dev/null +++ b/dependencies/assimp/include/assimp/SmoothingGroups.h @@ -0,0 +1,108 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines the helper data structures for importing 3DS files. +http://www.jalix.org/ressources/graphics/3DS/_unofficials/3ds-unofficial.txt */ + +#ifndef AI_SMOOTHINGGROUPS_H_INC +#define AI_SMOOTHINGGROUPS_H_INC + +#include +#include +#include + +// --------------------------------------------------------------------------- +/** Helper structure representing a face with smoothing groups assigned */ +struct FaceWithSmoothingGroup { + FaceWithSmoothingGroup() AI_NO_EXCEPT + : mIndices() + , iSmoothGroup(0) { + // in debug builds set all indices to a common magic value +#ifdef ASSIMP_BUILD_DEBUG + this->mIndices[0] = 0xffffffff; + this->mIndices[1] = 0xffffffff; + this->mIndices[2] = 0xffffffff; +#endif + } + + + //! Indices. .3ds is using uint16. However, after + //! an unique vertex set has been generated, + //! individual index values might exceed 2^16 + uint32_t mIndices[3]; + + //! specifies to which smoothing group the face belongs to + uint32_t iSmoothGroup; +}; + +// --------------------------------------------------------------------------- +/** Helper structure representing a mesh whose faces have smoothing + groups assigned. This allows us to reuse the code for normal computations + from smoothings groups for several loaders (3DS, ASE). All of them + use face structures which inherit from #FaceWithSmoothingGroup, + but as they add extra members and need to be copied by value we + need to use a template here. + */ +template +struct MeshWithSmoothingGroups +{ + //! Vertex positions + std::vector mPositions; + + //! Face lists + std::vector mFaces; + + //! List of normal vectors + std::vector mNormals; +}; + +// --------------------------------------------------------------------------- +/** Computes normal vectors for the mesh + */ +template +void ComputeNormalsWithSmoothingsGroups(MeshWithSmoothingGroups& sMesh); + + +// include implementations +#include "SmoothingGroups.inl" + +#endif // !! AI_SMOOTHINGGROUPS_H_INC diff --git a/dependencies/assimp/include/assimp/SmoothingGroups.inl b/dependencies/assimp/include/assimp/SmoothingGroups.inl new file mode 100644 index 0000000..84ea4a1 --- /dev/null +++ b/dependencies/assimp/include/assimp/SmoothingGroups.inl @@ -0,0 +1,138 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Generation of normal vectors basing on smoothing groups */ + +#ifndef AI_SMOOTHINGGROUPS_INL_INCLUDED +#define AI_SMOOTHINGGROUPS_INL_INCLUDED + +// internal headers +#include + +// CRT header +#include + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +template +void ComputeNormalsWithSmoothingsGroups(MeshWithSmoothingGroups& sMesh) +{ + // First generate face normals + sMesh.mNormals.resize(sMesh.mPositions.size(),aiVector3D()); + for( unsigned int a = 0; a < sMesh.mFaces.size(); a++) + { + T& face = sMesh.mFaces[a]; + + aiVector3D* pV1 = &sMesh.mPositions[face.mIndices[0]]; + aiVector3D* pV2 = &sMesh.mPositions[face.mIndices[1]]; + aiVector3D* pV3 = &sMesh.mPositions[face.mIndices[2]]; + + aiVector3D pDelta1 = *pV2 - *pV1; + aiVector3D pDelta2 = *pV3 - *pV1; + aiVector3D vNor = pDelta1 ^ pDelta2; + + for (unsigned int c = 0; c < 3;++c) + sMesh.mNormals[face.mIndices[c]] = vNor; + } + + // calculate the position bounds so we have a reliable epsilon to check position differences against + aiVector3D minVec( 1e10f, 1e10f, 1e10f), maxVec( -1e10f, -1e10f, -1e10f); + for( unsigned int a = 0; a < sMesh.mPositions.size(); a++) + { + minVec.x = std::min( minVec.x, sMesh.mPositions[a].x); + minVec.y = std::min( minVec.y, sMesh.mPositions[a].y); + minVec.z = std::min( minVec.z, sMesh.mPositions[a].z); + maxVec.x = std::max( maxVec.x, sMesh.mPositions[a].x); + maxVec.y = std::max( maxVec.y, sMesh.mPositions[a].y); + maxVec.z = std::max( maxVec.z, sMesh.mPositions[a].z); + } + const float posEpsilon = (maxVec - minVec).Length() * 1e-5f; + std::vector avNormals; + avNormals.resize(sMesh.mNormals.size()); + + // now generate the spatial sort tree + SGSpatialSort sSort; + for( typename std::vector::iterator i = sMesh.mFaces.begin(); + i != sMesh.mFaces.end();++i) + { + for (unsigned int c = 0; c < 3;++c) + sSort.Add(sMesh.mPositions[(*i).mIndices[c]],(*i).mIndices[c],(*i).iSmoothGroup); + } + sSort.Prepare(); + + std::vector vertexDone(sMesh.mPositions.size(),false); + for( typename std::vector::iterator i = sMesh.mFaces.begin(); + i != sMesh.mFaces.end();++i) + { + std::vector poResult; + for (unsigned int c = 0; c < 3;++c) + { + unsigned int idx = (*i).mIndices[c]; + if (vertexDone[idx])continue; + + sSort.FindPositions(sMesh.mPositions[idx],(*i).iSmoothGroup, + posEpsilon,poResult); + + aiVector3D vNormals; + for (std::vector::const_iterator + a = poResult.begin(); + a != poResult.end();++a) + { + vNormals += sMesh.mNormals[(*a)]; + } + vNormals.NormalizeSafe(); + + // write back into all affected normals + for (std::vector::const_iterator + a = poResult.begin(); + a != poResult.end();++a) + { + idx = *a; + avNormals [idx] = vNormals; + vertexDone[idx] = true; + } + } + } + sMesh.mNormals = avNormals; +} + +#endif // !! AI_SMOOTHINGGROUPS_INL_INCLUDED diff --git a/dependencies/assimp/include/assimp/SpatialSort.h b/dependencies/assimp/include/assimp/SpatialSort.h new file mode 100644 index 0000000..61b345b --- /dev/null +++ b/dependencies/assimp/include/assimp/SpatialSort.h @@ -0,0 +1,174 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** Small helper classes to optimise finding vertizes close to a given location */ +#ifndef AI_SPATIALSORT_H_INC +#define AI_SPATIALSORT_H_INC + +#include +#include + +namespace Assimp { + +// ------------------------------------------------------------------------------------------------ +/** A little helper class to quickly find all vertices in the epsilon environment of a given + * position. Construct an instance with an array of positions. The class stores the given positions + * by their indices and sorts them by their distance to an arbitrary chosen plane. + * You can then query the instance for all vertices close to a given position in an average O(log n) + * time, with O(n) worst case complexity when all vertices lay on the plane. The plane is chosen + * so that it avoids common planes in usual data sets. */ +// ------------------------------------------------------------------------------------------------ +class ASSIMP_API SpatialSort +{ +public: + + SpatialSort(); + + // ------------------------------------------------------------------------------------ + /** Constructs a spatially sorted representation from the given position array. + * Supply the positions in its layout in memory, the class will only refer to them + * by index. + * @param pPositions Pointer to the first position vector of the array. + * @param pNumPositions Number of vectors to expect in that array. + * @param pElementOffset Offset in bytes from the beginning of one vector in memory + * to the beginning of the next vector. */ + SpatialSort( const aiVector3D* pPositions, unsigned int pNumPositions, + unsigned int pElementOffset); + + /** Destructor */ + ~SpatialSort(); + +public: + + // ------------------------------------------------------------------------------------ + /** Sets the input data for the SpatialSort. This replaces existing data, if any. + * The new data receives new indices in ascending order. + * + * @param pPositions Pointer to the first position vector of the array. + * @param pNumPositions Number of vectors to expect in that array. + * @param pElementOffset Offset in bytes from the beginning of one vector in memory + * to the beginning of the next vector. + * @param pFinalize Specifies whether the SpatialSort's internal representation + * is finalized after the new data has been added. Finalization is + * required in order to use #FindPosition() or #GenerateMappingTable(). + * If you don't finalize yet, you can use #Append() to add data from + * other sources.*/ + void Fill( const aiVector3D* pPositions, unsigned int pNumPositions, + unsigned int pElementOffset, + bool pFinalize = true); + + + // ------------------------------------------------------------------------------------ + /** Same as #Fill(), except the method appends to existing data in the #SpatialSort. */ + void Append( const aiVector3D* pPositions, unsigned int pNumPositions, + unsigned int pElementOffset, + bool pFinalize = true); + + + // ------------------------------------------------------------------------------------ + /** Finalize the spatial hash data structure. This can be useful after + * multiple calls to #Append() with the pFinalize parameter set to false. + * This is finally required before one of #FindPositions() and #GenerateMappingTable() + * can be called to query the spatial sort.*/ + void Finalize(); + + // ------------------------------------------------------------------------------------ + /** Returns an iterator for all positions close to the given position. + * @param pPosition The position to look for vertices. + * @param pRadius Maximal distance from the position a vertex may have to be counted in. + * @param poResults The container to store the indices of the found positions. + * Will be emptied by the call so it may contain anything. + * @return An iterator to iterate over all vertices in the given area.*/ + void FindPositions( const aiVector3D& pPosition, ai_real pRadius, + std::vector& poResults) const; + + // ------------------------------------------------------------------------------------ + /** Fills an array with indices of all positions identical to the given position. In + * opposite to FindPositions(), not an epsilon is used but a (very low) tolerance of + * four floating-point units. + * @param pPosition The position to look for vertices. + * @param poResults The container to store the indices of the found positions. + * Will be emptied by the call so it may contain anything.*/ + void FindIdenticalPositions( const aiVector3D& pPosition, + std::vector& poResults) const; + + // ------------------------------------------------------------------------------------ + /** Compute a table that maps each vertex ID referring to a spatially close + * enough position to the same output ID. Output IDs are assigned in ascending order + * from 0...n. + * @param fill Will be filled with numPositions entries. + * @param pRadius Maximal distance from the position a vertex may have to + * be counted in. + * @return Number of unique vertices (n). */ + unsigned int GenerateMappingTable(std::vector& fill, + ai_real pRadius) const; + +protected: + /** Normal of the sorting plane, normalized. The center is always at (0, 0, 0) */ + aiVector3D mPlaneNormal; + + /** An entry in a spatially sorted position array. Consists of a vertex index, + * its position and its pre-calculated distance from the reference plane */ + struct Entry { + unsigned int mIndex; ///< The vertex referred by this entry + aiVector3D mPosition; ///< Position + ai_real mDistance; ///< Distance of this vertex to the sorting plane + + Entry() AI_NO_EXCEPT + : mIndex( 999999999 ), mPosition(), mDistance( 99999. ) { + // empty + } + Entry( unsigned int pIndex, const aiVector3D& pPosition, ai_real pDistance) + : mIndex( pIndex), mPosition( pPosition), mDistance( pDistance) { + // empty + } + + bool operator < (const Entry& e) const { return mDistance < e.mDistance; } + }; + + // all positions, sorted by distance to the sorting plane + std::vector mPositions; +}; + +} // end of namespace Assimp + +#endif // AI_SPATIALSORT_H_INC diff --git a/dependencies/assimp/include/assimp/StandardShapes.h b/dependencies/assimp/include/assimp/StandardShapes.h new file mode 100644 index 0000000..3791569 --- /dev/null +++ b/dependencies/assimp/include/assimp/StandardShapes.h @@ -0,0 +1,200 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Declares a helper class, "StandardShapes" which generates + * vertices for standard shapes, such as cylnders, cones, spheres .. + */ +#ifndef AI_STANDARD_SHAPES_H_INC +#define AI_STANDARD_SHAPES_H_INC + +#include +#include + +struct aiMesh; + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** \brief Helper class to generate vertex buffers for standard geometric + * shapes, such as cylinders, cones, boxes, spheres, elipsoids ... . + */ +class ASSIMP_API StandardShapes +{ + // class cannot be instanced + StandardShapes() {} + +public: + + + // ---------------------------------------------------------------- + /** Generates a mesh from an array of vertex positions. + * + * @param positions List of vertex positions + * @param numIndices Number of indices per primitive + * @return Output mesh + */ + static aiMesh* MakeMesh(const std::vector& positions, + unsigned int numIndices); + + + static aiMesh* MakeMesh ( unsigned int (*GenerateFunc) + (std::vector&)); + + static aiMesh* MakeMesh ( unsigned int (*GenerateFunc) + (std::vector&, bool)); + + static aiMesh* MakeMesh ( unsigned int n, void (*GenerateFunc) + (unsigned int,std::vector&)); + + // ---------------------------------------------------------------- + /** @brief Generates a hexahedron (cube) + * + * Hexahedrons can be scaled on all axes. + * @param positions Receives output triangles. + * @param polygons If you pass true here quads will be returned + * @return Number of vertices per face + */ + static unsigned int MakeHexahedron( + std::vector& positions, + bool polygons = false); + + // ---------------------------------------------------------------- + /** @brief Generates an icosahedron + * + * @param positions Receives output triangles. + * @return Number of vertices per face + */ + static unsigned int MakeIcosahedron( + std::vector& positions); + + + // ---------------------------------------------------------------- + /** @brief Generates a dodecahedron + * + * @param positions Receives output triangles + * @param polygons If you pass true here pentagons will be returned + * @return Number of vertices per face + */ + static unsigned int MakeDodecahedron( + std::vector& positions, + bool polygons = false); + + + // ---------------------------------------------------------------- + /** @brief Generates an octahedron + * + * @param positions Receives output triangles. + * @return Number of vertices per face + */ + static unsigned int MakeOctahedron( + std::vector& positions); + + + // ---------------------------------------------------------------- + /** @brief Generates a tetrahedron + * + * @param positions Receives output triangles. + * @return Number of vertices per face + */ + static unsigned int MakeTetrahedron( + std::vector& positions); + + + + // ---------------------------------------------------------------- + /** @brief Generates a sphere + * + * @param tess Number of subdivions - 0 generates a octahedron + * @param positions Receives output triangles. + */ + static void MakeSphere(unsigned int tess, + std::vector& positions); + + + // ---------------------------------------------------------------- + /** @brief Generates a cone or a cylinder, either open or closed. + * + * @code + * + * |-----| <- radius 1 + * + * __x__ <- ] ^ + * / \ | height | + * / \ | Y + * / \ | + * / \ | + * /______x______\ <- ] <- end cap + * + * |-------------| <- radius 2 + * + * @endcode + * + * @param height Height of the cone + * @param radius1 First radius + * @param radius2 Second radius + * @param tess Number of triangles. + * @param bOpened true for an open cone/cylinder. An open shape has + * no 'end caps' + * @param positions Receives output triangles + */ + static void MakeCone(ai_real height,ai_real radius1, + ai_real radius2,unsigned int tess, + std::vector& positions,bool bOpen= false); + + + // ---------------------------------------------------------------- + /** @brief Generates a flat circle + * + * The circle is constructed in the planned formed by the x,z + * axes of the cartesian coordinate system. + * + * @param radius Radius of the circle + * @param tess Number of segments. + * @param positions Receives output triangles. + */ + static void MakeCircle(ai_real radius, unsigned int tess, + std::vector& positions); + +}; +} // ! Assimp + +#endif // !! AI_STANDARD_SHAPES_H_INC diff --git a/dependencies/assimp/include/assimp/StreamReader.h b/dependencies/assimp/include/assimp/StreamReader.h new file mode 100644 index 0000000..9116c14 --- /dev/null +++ b/dependencies/assimp/include/assimp/StreamReader.h @@ -0,0 +1,343 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Defines the StreamReader class which reads data from + * a binary stream with a well-defined endianness. + */ + +#ifndef AI_STREAMREADER_H_INCLUDED +#define AI_STREAMREADER_H_INCLUDED + +#include +#include + +#include "ByteSwapper.h" +#include "Exceptional.h" +#include + +namespace Assimp { + +// -------------------------------------------------------------------------------------------- +/** Wrapper class around IOStream to allow for consistent reading of binary data in both + * little and big endian format. Don't attempt to instance the template directly. Use + * StreamReaderLE to read from a little-endian stream and StreamReaderBE to read from a + * BE stream. The class expects that the endianness of any input data is known at + * compile-time, which should usually be true (#BaseImporter::ConvertToUTF8 implements + * runtime endianness conversions for text files). + * + * XXX switch from unsigned int for size types to size_t? or ptrdiff_t?*/ +// -------------------------------------------------------------------------------------------- +template +class StreamReader { +public: + // FIXME: use these data types throughout the whole library, + // then change them to 64 bit values :-) + using diff = int; + using pos = unsigned int; + + // --------------------------------------------------------------------- + /** Construction from a given stream with a well-defined endianness. + * + * The StreamReader holds a permanent strong reference to the + * stream, which is released upon destruction. + * @param stream Input stream. The stream is not restarted if + * its file pointer is not at 0. Instead, the stream reader + * reads from the current position to the end of the stream. + * @param le If @c RuntimeSwitch is true: specifies whether the + * stream is in little endian byte order. Otherwise the + * endianness information is contained in the @c SwapEndianess + * template parameter and this parameter is meaningless. */ + StreamReader(std::shared_ptr stream, bool le = false) + : stream(stream) + , le(le) + { + ai_assert(stream); + InternBegin(); + } + + // --------------------------------------------------------------------- + StreamReader(IOStream* stream, bool le = false) + : stream(std::shared_ptr(stream)) + , le(le) + { + ai_assert(stream); + InternBegin(); + } + + // --------------------------------------------------------------------- + ~StreamReader() { + delete[] buffer; + } + + // deprecated, use overloaded operator>> instead + + // --------------------------------------------------------------------- + /** Read a float from the stream */ + float GetF4() + { + return Get(); + } + + // --------------------------------------------------------------------- + /** Read a double from the stream */ + double GetF8() { + return Get(); + } + + // --------------------------------------------------------------------- + /** Read a signed 16 bit integer from the stream */ + int16_t GetI2() { + return Get(); + } + + // --------------------------------------------------------------------- + /** Read a signed 8 bit integer from the stream */ + int8_t GetI1() { + return Get(); + } + + // --------------------------------------------------------------------- + /** Read an signed 32 bit integer from the stream */ + int32_t GetI4() { + return Get(); + } + + // --------------------------------------------------------------------- + /** Read a signed 64 bit integer from the stream */ + int64_t GetI8() { + return Get(); + } + + // --------------------------------------------------------------------- + /** Read a unsigned 16 bit integer from the stream */ + uint16_t GetU2() { + return Get(); + } + + // --------------------------------------------------------------------- + /** Read a unsigned 8 bit integer from the stream */ + uint8_t GetU1() { + return Get(); + } + + // --------------------------------------------------------------------- + /** Read an unsigned 32 bit integer from the stream */ + uint32_t GetU4() { + return Get(); + } + + // --------------------------------------------------------------------- + /** Read a unsigned 64 bit integer from the stream */ + uint64_t GetU8() { + return Get(); + } + + // --------------------------------------------------------------------- + /** Get the remaining stream size (to the end of the stream) */ + unsigned int GetRemainingSize() const { + return (unsigned int)(end - current); + } + + // --------------------------------------------------------------------- + /** Get the remaining stream size (to the current read limit). The + * return value is the remaining size of the stream if no custom + * read limit has been set. */ + unsigned int GetRemainingSizeToLimit() const { + return (unsigned int)(limit - current); + } + + // --------------------------------------------------------------------- + /** Increase the file pointer (relative seeking) */ + void IncPtr(intptr_t plus) { + current += plus; + if (current > limit) { + throw DeadlyImportError("End of file or read limit was reached"); + } + } + + // --------------------------------------------------------------------- + /** Get the current file pointer */ + int8_t* GetPtr() const { + return current; + } + + // --------------------------------------------------------------------- + /** Set current file pointer (Get it from #GetPtr). This is if you + * prefer to do pointer arithmetics on your own or want to copy + * large chunks of data at once. + * @param p The new pointer, which is validated against the size + * limit and buffer boundaries. */ + void SetPtr(int8_t* p) { + current = p; + if (current > limit || current < buffer) { + throw DeadlyImportError("End of file or read limit was reached"); + } + } + + // --------------------------------------------------------------------- + /** Copy n bytes to an external buffer + * @param out Destination for copying + * @param bytes Number of bytes to copy */ + void CopyAndAdvance(void* out, size_t bytes) { + int8_t* ur = GetPtr(); + SetPtr(ur+bytes); // fire exception if eof + + ::memcpy(out,ur,bytes); + } + + // --------------------------------------------------------------------- + /** Get the current offset from the beginning of the file */ + int GetCurrentPos() const { + return (unsigned int)(current - buffer); + } + + void SetCurrentPos(size_t pos) { + SetPtr(buffer + pos); + } + + // --------------------------------------------------------------------- + /** Setup a temporary read limit + * + * @param limit Maximum number of bytes to be read from + * the beginning of the file. Specifying UINT_MAX + * resets the limit to the original end of the stream. + * Returns the previously set limit. */ + unsigned int SetReadLimit(unsigned int _limit) { + unsigned int prev = GetReadLimit(); + if (UINT_MAX == _limit) { + limit = end; + return prev; + } + + limit = buffer + _limit; + if (limit > end) { + throw DeadlyImportError("StreamReader: Invalid read limit"); + } + return prev; + } + + // --------------------------------------------------------------------- + /** Get the current read limit in bytes. Reading over this limit + * accidentally raises an exception. */ + unsigned int GetReadLimit() const { + return (unsigned int)(limit - buffer); + } + + // --------------------------------------------------------------------- + /** Skip to the read limit in bytes. Reading over this limit + * accidentally raises an exception. */ + void SkipToReadLimit() { + current = limit; + } + + // --------------------------------------------------------------------- + /** overload operator>> and allow chaining of >> ops. */ + template + StreamReader& operator >> (T& f) { + f = Get(); + return *this; + } + + // --------------------------------------------------------------------- + /** Generic read method. ByteSwap::Swap(T*) *must* be defined */ + template + T Get() { + if ( current + sizeof(T) > limit) { + throw DeadlyImportError("End of file or stream limit was reached"); + } + + T f; + ::memcpy (&f, current, sizeof(T)); + Intern::Getter() (&f,le); + current += sizeof(T); + + return f; + } + +private: + // --------------------------------------------------------------------- + void InternBegin() { + if (!stream) { + // in case someone wonders: StreamReader is frequently invoked with + // no prior validation whether the input stream is valid. Since + // no one bothers changing the error message, this message here + // is passed down to the caller and 'unable to open file' + // simply describes best what happened. + throw DeadlyImportError("StreamReader: Unable to open file"); + } + + const size_t s = stream->FileSize() - stream->Tell(); + if (!s) { + throw DeadlyImportError("StreamReader: File is empty or EOF is already reached"); + } + + current = buffer = new int8_t[s]; + const size_t read = stream->Read(current,1,s); + // (read < s) can only happen if the stream was opened in text mode, in which case FileSize() is not reliable + ai_assert(read <= s); + end = limit = &buffer[read-1] + 1; + } + +private: + std::shared_ptr stream; + int8_t *buffer, *current, *end, *limit; + bool le; +}; + +// -------------------------------------------------------------------------------------------- +// `static` StreamReaders. Their byte order is fixed and they might be a little bit faster. +#ifdef AI_BUILD_BIG_ENDIAN + typedef StreamReader StreamReaderLE; + typedef StreamReader StreamReaderBE; +#else + typedef StreamReader StreamReaderBE; + typedef StreamReader StreamReaderLE; +#endif + +// `dynamic` StreamReader. The byte order of the input data is specified in the +// c'tor. This involves runtime branching and might be a little bit slower. +typedef StreamReader StreamReaderAny; + +} // end namespace Assimp + +#endif // !! AI_STREAMREADER_H_INCLUDED diff --git a/dependencies/assimp/include/assimp/StreamWriter.h b/dependencies/assimp/include/assimp/StreamWriter.h new file mode 100644 index 0000000..c7cf6c0 --- /dev/null +++ b/dependencies/assimp/include/assimp/StreamWriter.h @@ -0,0 +1,303 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Defines the StreamWriter class which writes data to + * a binary stream with a well-defined endianness. */ + +#ifndef AI_STREAMWRITER_H_INCLUDED +#define AI_STREAMWRITER_H_INCLUDED + +#include "ByteSwapper.h" +#include + +#include +#include + +namespace Assimp { + +// -------------------------------------------------------------------------------------------- +/** Wrapper class around IOStream to allow for consistent writing of binary data in both + * little and big endian format. Don't attempt to instance the template directly. Use + * StreamWriterLE to write to a little-endian stream and StreamWriterBE to write to a + * BE stream. Alternatively, there is StreamWriterAny if the endianness of the output + * stream is to be determined at runtime. + */ +// -------------------------------------------------------------------------------------------- +template +class StreamWriter +{ + enum { + INITIAL_CAPACITY = 1024 + }; + +public: + + // --------------------------------------------------------------------- + /** Construction from a given stream with a well-defined endianness. + * + * The StreamReader holds a permanent strong reference to the + * stream, which is released upon destruction. + * @param stream Input stream. The stream is not re-seeked and writing + continues at the current position of the stream cursor. + * @param le If @c RuntimeSwitch is true: specifies whether the + * stream is in little endian byte order. Otherwise the + * endianness information is defined by the @c SwapEndianess + * template parameter and this parameter is meaningless. */ + StreamWriter(std::shared_ptr stream, bool le = false) + : stream(stream) + , le(le) + , cursor() + { + ai_assert(stream); + buffer.reserve(INITIAL_CAPACITY); + } + + // --------------------------------------------------------------------- + StreamWriter(IOStream* stream, bool le = false) + : stream(std::shared_ptr(stream)) + , le(le) + , cursor() + { + ai_assert(stream); + buffer.reserve(INITIAL_CAPACITY); + } + + // --------------------------------------------------------------------- + ~StreamWriter() { + stream->Write(buffer.data(), 1, buffer.size()); + stream->Flush(); + } + +public: + + // --------------------------------------------------------------------- + /** Flush the contents of the internal buffer, and the output IOStream */ + void Flush() + { + stream->Write(buffer.data(), 1, buffer.size()); + stream->Flush(); + buffer.clear(); + cursor = 0; + } + + // --------------------------------------------------------------------- + /** Seek to the given offset / origin in the output IOStream. + * + * Flushes the internal buffer and the output IOStream prior to seeking. */ + aiReturn Seek(size_t pOffset, aiOrigin pOrigin=aiOrigin_SET) + { + Flush(); + return stream->Seek(pOffset, pOrigin); + } + + // --------------------------------------------------------------------- + /** Tell the current position in the output IOStream. + * + * First flushes the internal buffer and the output IOStream. */ + size_t Tell() + { + Flush(); + return stream->Tell(); + } + +public: + + // --------------------------------------------------------------------- + /** Write a float to the stream */ + void PutF4(float f) + { + Put(f); + } + + // --------------------------------------------------------------------- + /** Write a double to the stream */ + void PutF8(double d) { + Put(d); + } + + // --------------------------------------------------------------------- + /** Write a signed 16 bit integer to the stream */ + void PutI2(int16_t n) { + Put(n); + } + + // --------------------------------------------------------------------- + /** Write a signed 8 bit integer to the stream */ + void PutI1(int8_t n) { + Put(n); + } + + // --------------------------------------------------------------------- + /** Write an signed 32 bit integer to the stream */ + void PutI4(int32_t n) { + Put(n); + } + + // --------------------------------------------------------------------- + /** Write a signed 64 bit integer to the stream */ + void PutI8(int64_t n) { + Put(n); + } + + // --------------------------------------------------------------------- + /** Write a unsigned 16 bit integer to the stream */ + void PutU2(uint16_t n) { + Put(n); + } + + // --------------------------------------------------------------------- + /** Write a unsigned 8 bit integer to the stream */ + void PutU1(uint8_t n) { + Put(n); + } + + // --------------------------------------------------------------------- + /** Write an unsigned 32 bit integer to the stream */ + void PutU4(uint32_t n) { + Put(n); + } + + // --------------------------------------------------------------------- + /** Write a unsigned 64 bit integer to the stream */ + void PutU8(uint64_t n) { + Put(n); + } + + // --------------------------------------------------------------------- + /** Write a single character to the stream */ + void PutChar(char c) { + Put(c); + } + + // --------------------------------------------------------------------- + /** Write an aiString to the stream */ + void PutString(const aiString& s) + { + // as Put(T f) below + if (cursor + s.length >= buffer.size()) { + buffer.resize(cursor + s.length); + } + void* dest = &buffer[cursor]; + ::memcpy(dest, s.C_Str(), s.length); + cursor += s.length; + } + + // --------------------------------------------------------------------- + /** Write a std::string to the stream */ + void PutString(const std::string& s) + { + // as Put(T f) below + if (cursor + s.size() >= buffer.size()) { + buffer.resize(cursor + s.size()); + } + void* dest = &buffer[cursor]; + ::memcpy(dest, s.c_str(), s.size()); + cursor += s.size(); + } + +public: + + // --------------------------------------------------------------------- + /** overload operator<< and allow chaining of MM ops. */ + template + StreamWriter& operator << (T f) { + Put(f); + return *this; + } + + // --------------------------------------------------------------------- + std::size_t GetCurrentPos() const { + return cursor; + } + + // --------------------------------------------------------------------- + void SetCurrentPos(std::size_t new_cursor) { + cursor = new_cursor; + } + + // --------------------------------------------------------------------- + /** Generic write method. ByteSwap::Swap(T*) *must* be defined */ + template + void Put(T f) { + Intern :: Getter() (&f, le); + + if (cursor + sizeof(T) >= buffer.size()) { + buffer.resize(cursor + sizeof(T)); + } + + void* dest = &buffer[cursor]; + + // reinterpret_cast + assignment breaks strict aliasing rules + // and generally causes trouble on platforms such as ARM that + // do not silently ignore alignment faults. + ::memcpy(dest, &f, sizeof(T)); + cursor += sizeof(T); + } + +private: + + std::shared_ptr stream; + bool le; + + std::vector buffer; + std::size_t cursor; +}; + + +// -------------------------------------------------------------------------------------------- +// `static` StreamWriter. Their byte order is fixed and they might be a little bit faster. +#ifdef AI_BUILD_BIG_ENDIAN + typedef StreamWriter StreamWriterLE; + typedef StreamWriter StreamWriterBE; +#else + typedef StreamWriter StreamWriterBE; + typedef StreamWriter StreamWriterLE; +#endif + +// `dynamic` StreamWriter. The byte order of the input data is specified in the +// c'tor. This involves runtime branching and might be a little bit slower. +typedef StreamWriter StreamWriterAny; + +} // end namespace Assimp + +#endif // !! AI_STREAMWriter_H_INCLUDED diff --git a/dependencies/assimp/include/assimp/StringComparison.h b/dependencies/assimp/include/assimp/StringComparison.h new file mode 100644 index 0000000..8acef27 --- /dev/null +++ b/dependencies/assimp/include/assimp/StringComparison.h @@ -0,0 +1,233 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Definition of platform independent string workers: + + ASSIMP_itoa10 + ASSIMP_stricmp + ASSIMP_strincmp + + These functions are not consistently available on all platforms, + or the provided implementations behave too differently. +*/ +#ifndef INCLUDED_AI_STRING_WORKERS_H +#define INCLUDED_AI_STRING_WORKERS_H + +#include +#include +#include "StringComparison.h" + +#include +#include +#include + +namespace Assimp { + +// ------------------------------------------------------------------------------- +/** @brief itoa with a fixed base 10 + * 'itoa' is not consistently available on all platforms so it is quite useful + * to have a small replacement function here. No need to use a full sprintf() + * if we just want to print a number ... + * @param out Output buffer + * @param max Maximum number of characters to be written, including '\0'. + * This parameter may not be 0. + * @param number Number to be written + * @return Length of the output string, excluding the '\0' + */ +AI_FORCE_INLINE +unsigned int ASSIMP_itoa10( char* out, unsigned int max, int32_t number) { + ai_assert(NULL != out); + + // write the unary minus to indicate we have a negative number + unsigned int written = 1u; + if (number < 0 && written < max) { + *out++ = '-'; + ++written; + number = -number; + } + + // We begin with the largest number that is not zero. + int32_t cur = 1000000000; // 2147483648 + bool mustPrint = false; + while (written < max) { + + const unsigned int digit = number / cur; + if (mustPrint || digit > 0 || 1 == cur) { + // print all future zeroe's from now + mustPrint = true; + + *out++ = '0'+static_cast(digit); + + ++written; + number -= digit*cur; + if (1 == cur) { + break; + } + } + cur /= 10; + } + + // append a terminal zero + *out++ = '\0'; + return written-1; +} + +// ------------------------------------------------------------------------------- +/** @brief itoa with a fixed base 10 (Secure template overload) + * The compiler should choose this function if he or she is able to determine the + * size of the array automatically. + */ +template +AI_FORCE_INLINE +unsigned int ASSIMP_itoa10( char(& out)[length], int32_t number) { + return ASSIMP_itoa10(out,length,number); +} + +// ------------------------------------------------------------------------------- +/** @brief Helper function to do platform independent string comparison. + * + * This is required since stricmp() is not consistently available on + * all platforms. Some platforms use the '_' prefix, others don't even + * have such a function. + * + * @param s1 First input string + * @param s2 Second input string + * @return 0 if the given strings are identical + */ +AI_FORCE_INLINE +int ASSIMP_stricmp(const char *s1, const char *s2) { + ai_assert( NULL != s1 ); + ai_assert( NULL != s2 ); + +#if (defined _MSC_VER) + + return ::_stricmp(s1,s2); +#elif defined( __GNUC__ ) + + return ::strcasecmp(s1,s2); +#else + + char c1, c2; + do { + c1 = tolower(*s1++); + c2 = tolower(*s2++); + } + while ( c1 && (c1 == c2) ); + return c1 - c2; +#endif +} + +// ------------------------------------------------------------------------------- +/** @brief Case independent comparison of two std::strings + * + * @param a First string + * @param b Second string + * @return 0 if a == b + */ +AI_FORCE_INLINE +int ASSIMP_stricmp(const std::string& a, const std::string& b) { + int i = (int)b.length()-(int)a.length(); + return (i ? i : ASSIMP_stricmp(a.c_str(),b.c_str())); +} + +// ------------------------------------------------------------------------------- +/** @brief Helper function to do platform independent string comparison. + * + * This is required since strincmp() is not consistently available on + * all platforms. Some platforms use the '_' prefix, others don't even + * have such a function. + * + * @param s1 First input string + * @param s2 Second input string + * @param n Macimum number of characters to compare + * @return 0 if the given strings are identical + */ +AI_FORCE_INLINE +int ASSIMP_strincmp(const char *s1, const char *s2, unsigned int n) { + ai_assert( NULL != s1 ); + ai_assert( NULL != s2 ); + if ( !n ) { + return 0; + } + +#if (defined _MSC_VER) + + return ::_strnicmp(s1,s2,n); + +#elif defined( __GNUC__ ) + + return ::strncasecmp(s1,s2, n); + +#else + char c1, c2; + unsigned int p = 0; + do + { + if (p++ >= n)return 0; + c1 = tolower(*s1++); + c2 = tolower(*s2++); + } + while ( c1 && (c1 == c2) ); + + return c1 - c2; +#endif +} + + +// ------------------------------------------------------------------------------- +/** @brief Evaluates an integer power + * + * todo: move somewhere where it fits better in than here + */ +AI_FORCE_INLINE +unsigned int integer_pow( unsigned int base, unsigned int power ) { + unsigned int res = 1; + for ( unsigned int i = 0; i < power; ++i ) { + res *= base; + } + + return res; +} + +} // end of namespace + +#endif // ! AI_STRINGCOMPARISON_H_INC diff --git a/dependencies/assimp/include/assimp/StringUtils.h b/dependencies/assimp/include/assimp/StringUtils.h new file mode 100644 index 0000000..d68b7fa --- /dev/null +++ b/dependencies/assimp/include/assimp/StringUtils.h @@ -0,0 +1,143 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#ifndef INCLUDED_AI_STRINGUTILS_H +#define INCLUDED_AI_STRINGUTILS_H + +#include + +#include +#include +#include + +/// @fn ai_snprintf +/// @brief The portable version of the function snprintf ( C99 standard ), which works on visual studio compilers 2013 and earlier. +/// @param outBuf The buffer to write in +/// @param size The buffer size +/// @param format The format string +/// @param ap The additional arguments. +/// @return The number of written characters if the buffer size was big enough. If an encoding error occurs, a negative number is returned. +#if defined(_MSC_VER) && _MSC_VER < 1900 + + AI_FORCE_INLINE + int c99_ai_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) { + int count(-1); + if (0 != size) { + count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap); + } + if (count == -1) { + count = _vscprintf(format, ap); + } + + return count; + } + + AI_FORCE_INLINE + int ai_snprintf(char *outBuf, size_t size, const char *format, ...) { + int count; + va_list ap; + + va_start(ap, format); + count = c99_ai_vsnprintf(outBuf, size, format, ap); + va_end(ap); + + return count; + } + +#else +# define ai_snprintf snprintf +#endif + +/// @fn to_string +/// @brief The portable version of to_string ( some gcc-versions on embedded devices are not supporting this). +/// @param value The value to write into the std::string. +/// @return The value as a std::string +template +AI_FORCE_INLINE +std::string to_string( T value ) { + std::ostringstream os; + os << value; + + return os.str(); +} + +/// @fn ai_strtof +/// @brief The portable version of strtof. +/// @param begin The first character of the string. +/// @param end The last character +/// @return The float value, 0.0f in cas of an error. +AI_FORCE_INLINE +float ai_strtof( const char *begin, const char *end ) { + if ( nullptr == begin ) { + return 0.0f; + } + float val( 0.0f ); + if ( nullptr == end ) { + val = static_cast< float >( ::atof( begin ) ); + } else { + std::string::size_type len( end - begin ); + std::string token( begin, len ); + val = static_cast< float >( ::atof( token.c_str() ) ); + } + + return val; +} + +/// @fn DecimalToHexa +/// @brief The portable to convert a decimal value into a hexadecimal string. +/// @param toConvert Value to convert +/// @return The hexadecimal string, is empty in case of an error. +template +AI_FORCE_INLINE +std::string DecimalToHexa( T toConvert ) { + std::string result; + std::stringstream ss; + ss << std::hex << toConvert; + ss >> result; + + for ( size_t i = 0; i < result.size(); ++i ) { + result[ i ] = toupper( result[ i ] ); + } + + return result; +} + +#endif // INCLUDED_AI_STRINGUTILS_H diff --git a/dependencies/assimp/include/assimp/Subdivision.h b/dependencies/assimp/include/assimp/Subdivision.h new file mode 100644 index 0000000..43feb73 --- /dev/null +++ b/dependencies/assimp/include/assimp/Subdivision.h @@ -0,0 +1,131 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines a helper class to evaluate subdivision surfaces.*/ +#pragma once +#ifndef AI_SUBDISIVION_H_INC +#define AI_SUBDISIVION_H_INC + +#include +#include + +struct aiMesh; + +namespace Assimp { + +// ------------------------------------------------------------------------------ +/** Helper class to evaluate subdivision surfaces. Different algorithms + * are provided for choice. */ +// ------------------------------------------------------------------------------ +class ASSIMP_API Subdivider { +public: + + /** Enumerates all supported subvidision algorithms */ + enum Algorithm { + CATMULL_CLARKE = 0x1 + }; + + virtual ~Subdivider(); + + // --------------------------------------------------------------- + /** Create a subdivider of a specific type + * + * @param algo Algorithm to be used for subdivision + * @return Subdivider instance. */ + static Subdivider* Create (Algorithm algo); + + // --------------------------------------------------------------- + /** Subdivide a mesh using the selected algorithm + * + * @param mesh First mesh to be subdivided. Must be in verbose + * format. + * @param out Receives the output mesh, allocated by me. + * @param num Number of subdivisions to perform. + * @param discard_input If true is passed, the input mesh is + * deleted after the subdivision is complete. This can + * improve performance because it allows the optimization + * to reuse the existing mesh for intermediate results. + * @pre out!=mesh*/ + virtual void Subdivide ( aiMesh* mesh, + aiMesh*& out, unsigned int num, + bool discard_input = false) = 0; + + // --------------------------------------------------------------- + /** Subdivide multiple meshes using the selected algorithm. This + * avoids erroneous smoothing on objects consisting of multiple + * per-material meshes. Usually, most 3d modellers smooth on a + * per-object base, regardless the materials assigned to the + * meshes. + * + * @param smesh Array of meshes to be subdivided. Must be in + * verbose format. + * @param nmesh Number of meshes in smesh. + * @param out Receives the output meshes. The array must be + * sufficiently large (at least @c nmesh elements) and may not + * overlap the input array. Output meshes map one-to-one to + * their corresponding input meshes. The meshes are allocated + * by the function. + * @param discard_input If true is passed, input meshes are + * deleted after the subdivision is complete. This can + * improve performance because it allows the optimization + * of reusing existing meshes for intermediate results. + * @param num Number of subdivisions to perform. + * @pre nmesh != 0, smesh and out may not overlap*/ + virtual void Subdivide ( + aiMesh** smesh, + size_t nmesh, + aiMesh** out, + unsigned int num, + bool discard_input = false) = 0; + +}; + +inline +Subdivider::~Subdivider() { + // empty +} + +} // end namespace Assimp + + +#endif // !! AI_SUBDISIVION_H_INC + diff --git a/dependencies/assimp/include/assimp/TinyFormatter.h b/dependencies/assimp/include/assimp/TinyFormatter.h new file mode 100644 index 0000000..1226b48 --- /dev/null +++ b/dependencies/assimp/include/assimp/TinyFormatter.h @@ -0,0 +1,166 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file TinyFormatter.h + * @brief Utility to format log messages more easily. Introduced + * to get rid of the boost::format dependency. Much slinker, + * basically just extends stringstream. + */ +#ifndef INCLUDED_TINY_FORMATTER_H +#define INCLUDED_TINY_FORMATTER_H + +#include + +namespace Assimp { +namespace Formatter { + +// ------------------------------------------------------------------------------------------------ +/** stringstream utility. Usage: + * @code + * void writelog(const std::string&s); + * void writelog(const std::wstring&s); + * ... + * writelog(format()<< "hi! this is a number: " << 4); + * writelog(wformat()<< L"hi! this is a number: " << 4); + * + * @endcode */ +template < typename T, + typename CharTraits = std::char_traits, + typename Allocator = std::allocator +> +class basic_formatter +{ + +public: + + typedef class std::basic_string< + T,CharTraits,Allocator + > string; + + typedef class std::basic_ostringstream< + T,CharTraits,Allocator + > stringstream; + +public: + + basic_formatter() {} + + /* Allow basic_formatter's to be used almost interchangeably + * with std::(w)string or const (w)char* arguments because the + * conversion c'tor is called implicitly. */ + template + basic_formatter(const TT& sin) { + underlying << sin; + } + + + // The problem described here: + // https://sourceforge.net/tracker/?func=detail&atid=1067632&aid=3358562&group_id=226462 + // can also cause trouble here. Apparently, older gcc versions sometimes copy temporaries + // being bound to const ref& function parameters. Copying streams is not permitted, though. + // This workaround avoids this by manually specifying a copy ctor. +#if !defined(__GNUC__) || !defined(__APPLE__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) + explicit basic_formatter(const basic_formatter& other) { + underlying << (string)other; + } +#endif + + +public: + + operator string () const { + return underlying.str(); + } + + + /* note - this is declared const because binding temporaries does only + * work for const references, so many function prototypes will + * include const basic_formatter& s but might still want to + * modify the formatted string without the need for a full copy.*/ + template + const basic_formatter& operator << (const TToken& s) const { + underlying << s; + return *this; + } + + template + basic_formatter& operator << (const TToken& s) { + underlying << s; + return *this; + } + + + // comma operator overloaded as well, choose your preferred way. + template + const basic_formatter& operator, (const TToken& s) const { + underlying << s; + return *this; + } + + template + basic_formatter& operator, (const TToken& s) { + underlying << s; + return *this; + } + + // Fix for MSVC8 + // See https://sourceforge.net/projects/assimp/forums/forum/817654/topic/4372824 + template + basic_formatter& operator, (TToken& s) { + underlying << s; + return *this; + } + + +private: + mutable stringstream underlying; +}; + + +typedef basic_formatter< char > format; +typedef basic_formatter< wchar_t > wformat; + +} // ! namespace Formatter + +} // ! namespace Assimp + +#endif diff --git a/dependencies/assimp/include/assimp/Vertex.h b/dependencies/assimp/include/assimp/Vertex.h new file mode 100644 index 0000000..2a7f025 --- /dev/null +++ b/dependencies/assimp/include/assimp/Vertex.h @@ -0,0 +1,348 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +/** @file Defines a helper class to represent an interleaved vertex + along with arithmetic operations to support vertex operations + such as subdivision, smoothing etc. + + While the code is kept as general as possible, arithmetic operations + that are not currently well-defined (and would cause compile errors + due to missing operators in the math library), are commented. + */ +#ifndef AI_VERTEX_H_INC +#define AI_VERTEX_H_INC + +#include +#include +#include +#include + +namespace Assimp { + + /////////////////////////////////////////////////////////////////////////// + // std::plus-family operates on operands with identical types - we need to + // support all the (vectype op float) combinations in vector maths. + // Providing T(float) would open the way to endless implicit conversions. + /////////////////////////////////////////////////////////////////////////// + namespace Intern { + template struct plus { + TRES operator() (const T0& t0, const T1& t1) const { + return t0+t1; + } + }; + template struct minus { + TRES operator() (const T0& t0, const T1& t1) const { + return t0-t1; + } + }; + template struct multiplies { + TRES operator() (const T0& t0, const T1& t1) const { + return t0*t1; + } + }; + template struct divides { + TRES operator() (const T0& t0, const T1& t1) const { + return t0/t1; + } + }; + } + +// ------------------------------------------------------------------------------------------------ +/** Intermediate description a vertex with all possible components. Defines a full set of + * operators, so you may use such a 'Vertex' in basic arithmetics. All operators are applied + * to *all* vertex components equally. This is useful for stuff like interpolation + * or subdivision, but won't work if special handling is required for some vertex components. */ +// ------------------------------------------------------------------------------------------------ +class Vertex +{ + friend Vertex operator + (const Vertex&,const Vertex&); + friend Vertex operator - (const Vertex&,const Vertex&); + +// friend Vertex operator + (const Vertex&,ai_real); +// friend Vertex operator - (const Vertex&,ai_real); + friend Vertex operator * (const Vertex&,ai_real); + friend Vertex operator / (const Vertex&,ai_real); + +// friend Vertex operator + (ai_real, const Vertex&); +// friend Vertex operator - (ai_real, const Vertex&); + friend Vertex operator * (ai_real, const Vertex&); +// friend Vertex operator / (ai_real, const Vertex&); + +public: + + Vertex() {} + + // ---------------------------------------------------------------------------- + /** Extract a particular vertex from a mesh and interleave all components */ + explicit Vertex(const aiMesh* msh, unsigned int idx) { + ai_assert(idx < msh->mNumVertices); + position = msh->mVertices[idx]; + + if (msh->HasNormals()) { + normal = msh->mNormals[idx]; + } + + if (msh->HasTangentsAndBitangents()) { + tangent = msh->mTangents[idx]; + bitangent = msh->mBitangents[idx]; + } + + for (unsigned int i = 0; msh->HasTextureCoords(i); ++i) { + texcoords[i] = msh->mTextureCoords[i][idx]; + } + + for (unsigned int i = 0; msh->HasVertexColors(i); ++i) { + colors[i] = msh->mColors[i][idx]; + } + } + + // ---------------------------------------------------------------------------- + /** Extract a particular vertex from a anim mesh and interleave all components */ + explicit Vertex(const aiAnimMesh* msh, unsigned int idx) { + ai_assert(idx < msh->mNumVertices); + position = msh->mVertices[idx]; + + if (msh->HasNormals()) { + normal = msh->mNormals[idx]; + } + + if (msh->HasTangentsAndBitangents()) { + tangent = msh->mTangents[idx]; + bitangent = msh->mBitangents[idx]; + } + + for (unsigned int i = 0; msh->HasTextureCoords(i); ++i) { + texcoords[i] = msh->mTextureCoords[i][idx]; + } + + for (unsigned int i = 0; msh->HasVertexColors(i); ++i) { + colors[i] = msh->mColors[i][idx]; + } + } + +public: + + Vertex& operator += (const Vertex& v) { + *this = *this+v; + return *this; + } + + Vertex& operator -= (const Vertex& v) { + *this = *this-v; + return *this; + } + + +/* + Vertex& operator += (ai_real v) { + *this = *this+v; + return *this; + } + + Vertex& operator -= (ai_real v) { + *this = *this-v; + return *this; + } +*/ + Vertex& operator *= (ai_real v) { + *this = *this*v; + return *this; + } + + Vertex& operator /= (ai_real v) { + *this = *this/v; + return *this; + } + +public: + + // ---------------------------------------------------------------------------- + /** Convert back to non-interleaved storage */ + void SortBack(aiMesh* out, unsigned int idx) const { + + ai_assert(idxmNumVertices); + out->mVertices[idx] = position; + + if (out->HasNormals()) { + out->mNormals[idx] = normal; + } + + if (out->HasTangentsAndBitangents()) { + out->mTangents[idx] = tangent; + out->mBitangents[idx] = bitangent; + } + + for(unsigned int i = 0; out->HasTextureCoords(i); ++i) { + out->mTextureCoords[i][idx] = texcoords[i]; + } + + for(unsigned int i = 0; out->HasVertexColors(i); ++i) { + out->mColors[i][idx] = colors[i]; + } + } + +private: + + // ---------------------------------------------------------------------------- + /** Construct from two operands and a binary operation to combine them */ + template