Normalized line ending

Former-commit-id: bd061c48bdc8f0d26159dac3b41017defef5ae1e
This commit is contained in:
Lynix
2012-09-21 14:24:48 +02:00
parent 4b3772e2d8
commit a73cd1374a
72 changed files with 25415 additions and 25415 deletions

View File

@@ -1,198 +1,198 @@
// Copyright (C) 2012 Jérôme Leclercq
// This file is part of the "Nazara Engine - Utility module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Utility/Loaders/MD2.hpp>
#include <Nazara/Core/Endianness.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Core/InputStream.hpp>
#include <Nazara/Math/Basic.hpp>
#include <Nazara/Utility/Mesh.hpp>
#include <Nazara/Utility/Loaders/MD2/Constants.hpp>
#include <Nazara/Utility/Loaders/MD2/Mesh.hpp>
#include <cstddef>
#include <cstring>
#include <Nazara/Utility/Debug.hpp>
namespace
{
bool NzLoader_MD2_Check(NzInputStream& stream, const NzMeshParams& parameters)
{
NazaraUnused(parameters);
nzUInt32 magic[2];
if (stream.Read(&magic[0], 2*sizeof(nzUInt32)) != 2*sizeof(nzUInt32))
return false;
#if defined(NAZARA_BIG_ENDIAN)
NzByteSwap(&magic[0], sizeof(nzUInt32));
NzByteSwap(&magic[1], sizeof(nzUInt32));
#endif
return magic[0] == md2Ident && magic[1] == 8;
}
bool NzLoader_MD2_Load(NzMesh* mesh, NzInputStream& stream, const NzMeshParams& parameters)
{
md2_header header;
if (stream.Read(&header, sizeof(md2_header)) != sizeof(md2_header))
{
NazaraError("Failed to read header");
return false;
}
#if defined(NAZARA_BIG_ENDIAN)
NzByteSwap(&header.skinwidth, sizeof(nzUInt32));
NzByteSwap(&header.skinheight, sizeof(nzUInt32));
NzByteSwap(&header.framesize, sizeof(nzUInt32));
NzByteSwap(&header.num_skins, sizeof(nzUInt32));
NzByteSwap(&header.num_vertices, sizeof(nzUInt32));
NzByteSwap(&header.num_st, sizeof(nzUInt32));
NzByteSwap(&header.num_tris, sizeof(nzUInt32));
NzByteSwap(&header.num_glcmds, sizeof(nzUInt32));
NzByteSwap(&header.num_frames, sizeof(nzUInt32));
NzByteSwap(&header.offset_skins, sizeof(nzUInt32));
NzByteSwap(&header.offset_st, sizeof(nzUInt32));
NzByteSwap(&header.offset_tris, sizeof(nzUInt32));
NzByteSwap(&header.offset_frames, sizeof(nzUInt32));
NzByteSwap(&header.offset_glcmds, sizeof(nzUInt32));
NzByteSwap(&header.offset_end, sizeof(nzUInt32));
#endif
if (stream.GetSize() < header.offset_end)
{
NazaraError("Incomplete MD2 file");
return false;
}
/// Création du mesh
// Animé ou statique, c'est la question
bool animated;
unsigned int startFrame = NzClamp(parameters.animation.startFrame, 0U, static_cast<unsigned int>(header.num_frames-1));
unsigned int endFrame = NzClamp(parameters.animation.endFrame, 0U, static_cast<unsigned int>(header.num_frames-1));
if (parameters.loadAnimations && startFrame != endFrame)
animated = true;
else
animated = false;
if (!mesh->Create((animated) ? nzAnimationType_Keyframe : nzAnimationType_Static)) // Ne devrait jamais échouer
{
NazaraInternalError("Failed to create mesh");
return false;
}
/// Chargement des skins
if (header.num_skins > 0)
{
stream.SetCursorPos(header.offset_skins);
{
char skin[68];
for (unsigned int i = 0; i < header.num_skins; ++i)
{
stream.Read(skin, 68*sizeof(char));
mesh->AddSkin(skin);
}
}
}
/// Chargement des animations
if (animated)
{
NzAnimation* animation = new NzAnimation;
if (animation->Create(nzAnimationType_Keyframe, endFrame-startFrame+1))
{
// Décodage des séquences
NzString frameName;
NzSequence sequence;
sequence.framePerSecond = 10; // Par défaut pour les animations MD2
char name[16], last[16];
stream.SetCursorPos(header.offset_frames + startFrame*header.framesize + offsetof(md2_frame, name));
stream.Read(last, 16*sizeof(char));
int pos = std::strlen(last)-1;
for (unsigned int j = 0; j < 2; ++j)
{
if (!std::isdigit(last[pos]))
break;
pos--;
}
last[pos+1] = '\0';
unsigned int numFrames = 0;
for (unsigned int i = startFrame; i <= endFrame; ++i)
{
stream.SetCursorPos(header.offset_frames + i*header.framesize + offsetof(md2_frame, name));
stream.Read(name, 16*sizeof(char));
pos = std::strlen(name)-1;
for (unsigned int j = 0; j < 2; ++j)
{
if (!std::isdigit(name[pos]))
break;
pos--;
}
name[pos+1] = '\0';
if (std::strcmp(name, last) != 0) // Si les deux frames n'ont pas le même nom
{
// Alors on enregistre la séquence
sequence.firstFrame = i-numFrames;
sequence.lastFrame = i-1;
sequence.name = last;
animation->AddSequence(sequence);
std::strcpy(last, name);
numFrames = 0;
}
numFrames++;
}
// On ajoute la dernière frame (Qui n'a pas été traitée par la boucle)
sequence.firstFrame = endFrame-numFrames;
sequence.lastFrame = endFrame;
sequence.name = last;
animation->AddSequence(sequence);
mesh->SetAnimation(animation);
animation->SetPersistent(false);
}
else
NazaraInternalError("Failed to create animaton");
}
/// Chargement des submesh
// Actuellement le loader ne charge qu'un submesh
// TODO: Utiliser les commandes OpenGL pour accélérer le rendu
NzMD2Mesh* subMesh = new NzMD2Mesh(mesh);
if (!subMesh->Create(header, stream, parameters))
{
NazaraError("Failed to create MD2 mesh");
return false;
}
mesh->AddSubMesh(subMesh);
return true;
}
}
void NzLoaders_MD2_Register()
{
NzMD2Mesh::Initialize();
NzMeshLoader::RegisterLoader("md2", NzLoader_MD2_Check, NzLoader_MD2_Load);
}
void NzLoaders_MD2_Unregister()
{
NzMeshLoader::UnregisterLoader("md2", NzLoader_MD2_Check, NzLoader_MD2_Load);
NzMD2Mesh::Uninitialize();
}
// Copyright (C) 2012 Jérôme Leclercq
// This file is part of the "Nazara Engine - Utility module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Utility/Loaders/MD2.hpp>
#include <Nazara/Core/Endianness.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Core/InputStream.hpp>
#include <Nazara/Math/Basic.hpp>
#include <Nazara/Utility/Mesh.hpp>
#include <Nazara/Utility/Loaders/MD2/Constants.hpp>
#include <Nazara/Utility/Loaders/MD2/Mesh.hpp>
#include <cstddef>
#include <cstring>
#include <Nazara/Utility/Debug.hpp>
namespace
{
bool NzLoader_MD2_Check(NzInputStream& stream, const NzMeshParams& parameters)
{
NazaraUnused(parameters);
nzUInt32 magic[2];
if (stream.Read(&magic[0], 2*sizeof(nzUInt32)) != 2*sizeof(nzUInt32))
return false;
#if defined(NAZARA_BIG_ENDIAN)
NzByteSwap(&magic[0], sizeof(nzUInt32));
NzByteSwap(&magic[1], sizeof(nzUInt32));
#endif
return magic[0] == md2Ident && magic[1] == 8;
}
bool NzLoader_MD2_Load(NzMesh* mesh, NzInputStream& stream, const NzMeshParams& parameters)
{
md2_header header;
if (stream.Read(&header, sizeof(md2_header)) != sizeof(md2_header))
{
NazaraError("Failed to read header");
return false;
}
#if defined(NAZARA_BIG_ENDIAN)
NzByteSwap(&header.skinwidth, sizeof(nzUInt32));
NzByteSwap(&header.skinheight, sizeof(nzUInt32));
NzByteSwap(&header.framesize, sizeof(nzUInt32));
NzByteSwap(&header.num_skins, sizeof(nzUInt32));
NzByteSwap(&header.num_vertices, sizeof(nzUInt32));
NzByteSwap(&header.num_st, sizeof(nzUInt32));
NzByteSwap(&header.num_tris, sizeof(nzUInt32));
NzByteSwap(&header.num_glcmds, sizeof(nzUInt32));
NzByteSwap(&header.num_frames, sizeof(nzUInt32));
NzByteSwap(&header.offset_skins, sizeof(nzUInt32));
NzByteSwap(&header.offset_st, sizeof(nzUInt32));
NzByteSwap(&header.offset_tris, sizeof(nzUInt32));
NzByteSwap(&header.offset_frames, sizeof(nzUInt32));
NzByteSwap(&header.offset_glcmds, sizeof(nzUInt32));
NzByteSwap(&header.offset_end, sizeof(nzUInt32));
#endif
if (stream.GetSize() < header.offset_end)
{
NazaraError("Incomplete MD2 file");
return false;
}
/// Création du mesh
// Animé ou statique, c'est la question
bool animated;
unsigned int startFrame = NzClamp(parameters.animation.startFrame, 0U, static_cast<unsigned int>(header.num_frames-1));
unsigned int endFrame = NzClamp(parameters.animation.endFrame, 0U, static_cast<unsigned int>(header.num_frames-1));
if (parameters.loadAnimations && startFrame != endFrame)
animated = true;
else
animated = false;
if (!mesh->Create((animated) ? nzAnimationType_Keyframe : nzAnimationType_Static)) // Ne devrait jamais échouer
{
NazaraInternalError("Failed to create mesh");
return false;
}
/// Chargement des skins
if (header.num_skins > 0)
{
stream.SetCursorPos(header.offset_skins);
{
char skin[68];
for (unsigned int i = 0; i < header.num_skins; ++i)
{
stream.Read(skin, 68*sizeof(char));
mesh->AddSkin(skin);
}
}
}
/// Chargement des animations
if (animated)
{
NzAnimation* animation = new NzAnimation;
if (animation->Create(nzAnimationType_Keyframe, endFrame-startFrame+1))
{
// Décodage des séquences
NzString frameName;
NzSequence sequence;
sequence.framePerSecond = 10; // Par défaut pour les animations MD2
char name[16], last[16];
stream.SetCursorPos(header.offset_frames + startFrame*header.framesize + offsetof(md2_frame, name));
stream.Read(last, 16*sizeof(char));
int pos = std::strlen(last)-1;
for (unsigned int j = 0; j < 2; ++j)
{
if (!std::isdigit(last[pos]))
break;
pos--;
}
last[pos+1] = '\0';
unsigned int numFrames = 0;
for (unsigned int i = startFrame; i <= endFrame; ++i)
{
stream.SetCursorPos(header.offset_frames + i*header.framesize + offsetof(md2_frame, name));
stream.Read(name, 16*sizeof(char));
pos = std::strlen(name)-1;
for (unsigned int j = 0; j < 2; ++j)
{
if (!std::isdigit(name[pos]))
break;
pos--;
}
name[pos+1] = '\0';
if (std::strcmp(name, last) != 0) // Si les deux frames n'ont pas le même nom
{
// Alors on enregistre la séquence
sequence.firstFrame = i-numFrames;
sequence.lastFrame = i-1;
sequence.name = last;
animation->AddSequence(sequence);
std::strcpy(last, name);
numFrames = 0;
}
numFrames++;
}
// On ajoute la dernière frame (Qui n'a pas été traitée par la boucle)
sequence.firstFrame = endFrame-numFrames;
sequence.lastFrame = endFrame;
sequence.name = last;
animation->AddSequence(sequence);
mesh->SetAnimation(animation);
animation->SetPersistent(false);
}
else
NazaraInternalError("Failed to create animaton");
}
/// Chargement des submesh
// Actuellement le loader ne charge qu'un submesh
// TODO: Utiliser les commandes OpenGL pour accélérer le rendu
NzMD2Mesh* subMesh = new NzMD2Mesh(mesh);
if (!subMesh->Create(header, stream, parameters))
{
NazaraError("Failed to create MD2 mesh");
return false;
}
mesh->AddSubMesh(subMesh);
return true;
}
}
void NzLoaders_MD2_Register()
{
NzMD2Mesh::Initialize();
NzMeshLoader::RegisterLoader("md2", NzLoader_MD2_Check, NzLoader_MD2_Load);
}
void NzLoaders_MD2_Unregister()
{
NzMeshLoader::UnregisterLoader("md2", NzLoader_MD2_Check, NzLoader_MD2_Load);
NzMD2Mesh::Uninitialize();
}

View File

@@ -1,292 +1,292 @@
// Copyright (C) 2012 Jérôme Leclercq
// This file is part of the "Nazara Engine - Utility module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Utility/Loaders/MD2/Mesh.hpp>
#include <Nazara/Core/InputStream.hpp>
#include <Nazara/Math/Matrix4.hpp>
#include <Nazara/Utility/IndexBuffer.hpp>
#include <Nazara/Utility/Mesh.hpp>
#include <Nazara/Utility/VertexBuffer.hpp>
#include <Nazara/Utility/Debug.hpp>
NzMD2Mesh::NzMD2Mesh(const NzMesh* parent) :
NzKeyframeMesh(parent),
m_frames(nullptr),
m_indexBuffer(nullptr),
m_vertexBuffer(nullptr)
{
}
NzMD2Mesh::~NzMD2Mesh()
{
Destroy();
}
bool NzMD2Mesh::Create(const md2_header& header, NzInputStream& stream, const NzMeshParams& parameters)
{
Destroy();
unsigned int startFrame = NzClamp(parameters.animation.startFrame, 0U, static_cast<unsigned int>(header.num_frames-1));
unsigned int endFrame = NzClamp(parameters.animation.endFrame, 0U, static_cast<unsigned int>(header.num_frames-1));
m_frameCount = endFrame - startFrame + 1;
m_vertexCount = header.num_tris * 3;
/// Chargement des vertices
std::vector<md2_texCoord> texCoords(header.num_st);
std::vector<md2_triangle> triangles(header.num_tris);
// Lecture des coordonnées de texture
stream.SetCursorPos(header.offset_st);
stream.Read(&texCoords[0], header.num_st*sizeof(md2_texCoord));
#if defined(NAZARA_BIG_ENDIAN)
for (unsigned int i = 0; i < header.num_st; ++i)
{
NzByteSwap(&texCoords[i].u, sizeof(nzInt16));
NzByteSwap(&texCoords[i].v, sizeof(nzInt16));
}
#endif
stream.SetCursorPos(header.offset_tris);
stream.Read(&triangles[0], header.num_tris*sizeof(md2_triangle));
#if defined(NAZARA_BIG_ENDIAN)
for (unsigned int i = 0; i < header.num_tris; ++i)
{
NzByteSwap(&triangles[i].vertices[0], sizeof(nzUInt16));
NzByteSwap(&texCoords[i].texCoords[0], sizeof(nzUInt16));
NzByteSwap(&triangles[i].vertices[1], sizeof(nzUInt16));
NzByteSwap(&texCoords[i].texCoords[1], sizeof(nzUInt16));
NzByteSwap(&triangles[i].vertices[2], sizeof(nzUInt16));
NzByteSwap(&texCoords[i].texCoords[2], sizeof(nzUInt16));
}
#endif
stream.SetCursorPos(header.offset_frames + header.framesize*startFrame);
md2_frame frame;
frame.vertices.resize(header.num_vertices);
// Pour que le modèle soit correctement aligné, on génère un quaternion que nous appliquerons à chacune des vertices
NzQuaternionf rotationQuat = NzEulerAnglesf(-90.f, 90.f, 0.f);
//NzMatrix4f rotationMatrix = NzMatrix4f::Rotate(NzEulerAnglesf(-90.f, -90.f, 0.f));
unsigned int stride = s_declaration.GetStride(nzElementStream_VertexData);
m_frames = new Frame[m_frameCount];
for (unsigned int i = 0; i < m_frameCount; ++i)
{
stream.Read(&frame.scale, sizeof(NzVector3f));
stream.Read(&frame.translate, sizeof(NzVector3f));
stream.Read(&frame.name, 16*sizeof(char));
stream.Read(&frame.vertices[0], header.num_vertices*sizeof(md2_vertex));
#if defined(NAZARA_BIG_ENDIAN)
NzByteSwap(&frame.scale.x, sizeof(float));
NzByteSwap(&frame.scale.y, sizeof(float));
NzByteSwap(&frame.scale.z, sizeof(float));
NzByteSwap(&frame.translate.x, sizeof(float));
NzByteSwap(&frame.translate.y, sizeof(float));
NzByteSwap(&frame.translate.z, sizeof(float));
#endif
m_frames[i].normal = new nzUInt8[m_vertexCount]; // Nous stockons l'indice MD2 de la normale plutôt que la normale (gain d'espace)
m_frames[i].vertices = new NzVector3f[m_vertexCount];
NzVector3f max, min;
for (unsigned int t = 0; t < header.num_tris; ++t)
{
for (unsigned int v = 0; v < 3; ++v)
{
const md2_vertex& vert = frame.vertices[triangles[t].vertices[v]];
NzVector3f vertex = rotationQuat * NzVector3f(vert.x * frame.scale.x + frame.translate.x, vert.y * frame.scale.y + frame.translate.y, vert.z * frame.scale.z + frame.translate.z);
// On fait en sorte d'avoir deux vertices de délimitation, définissant un rectangle dans l'espace
max.Maximize(vertex);
min.Minimize(vertex);
// Le MD2 ne définit pas ses vertices dans le bon ordre, il nous faut donc les ajouter dans l'ordre inverse
unsigned int index = m_vertexCount - (t*3 + v) - 1;
m_frames[i].normal[index] = vert.n;
m_frames[i].vertices[index] = vertex;
}
}
m_frames[i].aabb.SetExtends(min, max);
}
m_indexBuffer = nullptr; // Pas d'indexbuffer pour l'instant
m_vertexBuffer = new NzVertexBuffer(m_vertexCount, (3+3+2)*sizeof(float), parameters.storage, nzBufferUsage_Dynamic);
nzUInt8* ptr = reinterpret_cast<nzUInt8*>(m_vertexBuffer->Map(nzBufferAccess_WriteOnly));
if (!ptr)
{
NazaraError("Failed to map vertex buffer");
Destroy();
return false;
}
// On avance jusqu'aux dernières coordonnées de texture et on les définit dans l'ordre inverse
ptr += s_declaration.GetElement(nzElementStream_VertexData, nzElementUsage_TexCoord)->offset + stride * (m_vertexCount-1);
for (unsigned int t = 0; t < header.num_tris; ++t)
{
for (unsigned int v = 0; v < 3; ++v)
{
const md2_texCoord& texC = texCoords[triangles[t].texCoords[v]];
NzVector2f* coords = reinterpret_cast<NzVector2f*>(ptr);
coords->x = texC.u / static_cast<float>(header.skinwidth);
coords->y = 1.f - texC.v / static_cast<float>(header.skinheight);
ptr -= stride;
}
}
if (!m_vertexBuffer->Unmap())
{
NazaraError("Failed to unmap buffer");
Destroy();
return false;
}
m_vertexBuffer->AddResourceReference();
m_vertexBuffer->SetPersistent(false);
AnimateImpl(0, 0, 0.f);
return true;
}
void NzMD2Mesh::Destroy()
{
if (m_frames)
{
for (unsigned int i = 0; i < m_frameCount; ++i)
{
delete[] m_frames[i].normal;
delete[] m_frames[i].vertices;
}
delete[] m_frames;
m_frames = nullptr;
}
if (m_indexBuffer)
{
m_indexBuffer->RemoveResourceReference();
m_indexBuffer = nullptr;
}
if (m_vertexBuffer)
{
m_vertexBuffer->RemoveResourceReference();
m_vertexBuffer = nullptr;
}
}
const NzAxisAlignedBox& NzMD2Mesh::GetAABB() const
{
return m_aabb;
}
nzAnimationType NzMD2Mesh::GetAnimationType() const
{
if (m_frameCount > 1)
return nzAnimationType_Keyframe;
else
return nzAnimationType_Static;
}
unsigned int NzMD2Mesh::GetFrameCount() const
{
return m_frameCount;
}
const NzIndexBuffer* NzMD2Mesh::GetIndexBuffer() const
{
return nullptr;
//return m_indexBuffer;
}
nzPrimitiveType NzMD2Mesh::GetPrimitiveType() const
{
return nzPrimitiveType_TriangleList;
}
const NzVertexBuffer* NzMD2Mesh::GetVertexBuffer() const
{
return m_vertexBuffer;
}
const NzVertexDeclaration* NzMD2Mesh::GetVertexDeclaration() const
{
return &s_declaration;
}
void NzMD2Mesh::Initialize()
{
NzVertexElement elements[3];
elements[0].offset = 0;
elements[0].type = nzElementType_Float3;
elements[0].usage = nzElementUsage_Position;
elements[1].offset = 3*sizeof(float);
elements[1].type = nzElementType_Float3;
elements[1].usage = nzElementUsage_Normal;
elements[2].offset = 3*sizeof(float) + 3*sizeof(float);
elements[2].type = nzElementType_Float2;
elements[2].usage = nzElementUsage_TexCoord;
s_declaration.Create(elements, 3);
}
void NzMD2Mesh::Uninitialize()
{
s_declaration.Destroy();
}
void NzMD2Mesh::AnimateImpl(unsigned int frameA, unsigned int frameB, float interpolation)
{
nzUInt8* ptr = reinterpret_cast<nzUInt8*>(m_vertexBuffer->Map(nzBufferAccess_WriteOnly));
if (!ptr)
{
NazaraError("Failed to map vertex buffer");
return;
}
unsigned int stride = s_declaration.GetStride(nzElementStream_VertexData);
unsigned int positionOffset = s_declaration.GetElement(nzElementStream_VertexData, nzElementUsage_Position)->offset;
unsigned int normalOffset = s_declaration.GetElement(nzElementStream_VertexData, nzElementUsage_Normal)->offset;
Frame* fA = &m_frames[frameA];
Frame* fB = &m_frames[frameB];
for (unsigned int i = 0; i < m_vertexCount; ++i)
{
NzVector3f* position = reinterpret_cast<NzVector3f*>(ptr + positionOffset);
NzVector3f* normal = reinterpret_cast<NzVector3f*>(ptr + normalOffset);
*position = fA->vertices[i] + interpolation * (fB->vertices[i] - fA->vertices[i]);
*normal = md2Normals[fA->normal[i]] + interpolation * (md2Normals[fB->normal[i]] - md2Normals[fA->normal[i]]);
ptr += stride;
}
if (!m_vertexBuffer->Unmap())
NazaraWarning("Failed to unmap vertex buffer, expect mesh corruption");
// Interpolation de l'AABB
NzVector3f max1 = fA->aabb.GetMaximum();
NzVector3f min1 = fA->aabb.GetMinimum();
m_aabb.SetExtends(min1 + interpolation * (fB->aabb.GetMinimum() - min1), max1 + interpolation * (fB->aabb.GetMaximum() - max1));
}
NzVertexDeclaration NzMD2Mesh::s_declaration;
// Copyright (C) 2012 Jérôme Leclercq
// This file is part of the "Nazara Engine - Utility module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Utility/Loaders/MD2/Mesh.hpp>
#include <Nazara/Core/InputStream.hpp>
#include <Nazara/Math/Matrix4.hpp>
#include <Nazara/Utility/IndexBuffer.hpp>
#include <Nazara/Utility/Mesh.hpp>
#include <Nazara/Utility/VertexBuffer.hpp>
#include <Nazara/Utility/Debug.hpp>
NzMD2Mesh::NzMD2Mesh(const NzMesh* parent) :
NzKeyframeMesh(parent),
m_frames(nullptr),
m_indexBuffer(nullptr),
m_vertexBuffer(nullptr)
{
}
NzMD2Mesh::~NzMD2Mesh()
{
Destroy();
}
bool NzMD2Mesh::Create(const md2_header& header, NzInputStream& stream, const NzMeshParams& parameters)
{
Destroy();
unsigned int startFrame = NzClamp(parameters.animation.startFrame, 0U, static_cast<unsigned int>(header.num_frames-1));
unsigned int endFrame = NzClamp(parameters.animation.endFrame, 0U, static_cast<unsigned int>(header.num_frames-1));
m_frameCount = endFrame - startFrame + 1;
m_vertexCount = header.num_tris * 3;
/// Chargement des vertices
std::vector<md2_texCoord> texCoords(header.num_st);
std::vector<md2_triangle> triangles(header.num_tris);
// Lecture des coordonnées de texture
stream.SetCursorPos(header.offset_st);
stream.Read(&texCoords[0], header.num_st*sizeof(md2_texCoord));
#if defined(NAZARA_BIG_ENDIAN)
for (unsigned int i = 0; i < header.num_st; ++i)
{
NzByteSwap(&texCoords[i].u, sizeof(nzInt16));
NzByteSwap(&texCoords[i].v, sizeof(nzInt16));
}
#endif
stream.SetCursorPos(header.offset_tris);
stream.Read(&triangles[0], header.num_tris*sizeof(md2_triangle));
#if defined(NAZARA_BIG_ENDIAN)
for (unsigned int i = 0; i < header.num_tris; ++i)
{
NzByteSwap(&triangles[i].vertices[0], sizeof(nzUInt16));
NzByteSwap(&texCoords[i].texCoords[0], sizeof(nzUInt16));
NzByteSwap(&triangles[i].vertices[1], sizeof(nzUInt16));
NzByteSwap(&texCoords[i].texCoords[1], sizeof(nzUInt16));
NzByteSwap(&triangles[i].vertices[2], sizeof(nzUInt16));
NzByteSwap(&texCoords[i].texCoords[2], sizeof(nzUInt16));
}
#endif
stream.SetCursorPos(header.offset_frames + header.framesize*startFrame);
md2_frame frame;
frame.vertices.resize(header.num_vertices);
// Pour que le modèle soit correctement aligné, on génère un quaternion que nous appliquerons à chacune des vertices
NzQuaternionf rotationQuat = NzEulerAnglesf(-90.f, 90.f, 0.f);
//NzMatrix4f rotationMatrix = NzMatrix4f::Rotate(NzEulerAnglesf(-90.f, -90.f, 0.f));
unsigned int stride = s_declaration.GetStride(nzElementStream_VertexData);
m_frames = new Frame[m_frameCount];
for (unsigned int i = 0; i < m_frameCount; ++i)
{
stream.Read(&frame.scale, sizeof(NzVector3f));
stream.Read(&frame.translate, sizeof(NzVector3f));
stream.Read(&frame.name, 16*sizeof(char));
stream.Read(&frame.vertices[0], header.num_vertices*sizeof(md2_vertex));
#if defined(NAZARA_BIG_ENDIAN)
NzByteSwap(&frame.scale.x, sizeof(float));
NzByteSwap(&frame.scale.y, sizeof(float));
NzByteSwap(&frame.scale.z, sizeof(float));
NzByteSwap(&frame.translate.x, sizeof(float));
NzByteSwap(&frame.translate.y, sizeof(float));
NzByteSwap(&frame.translate.z, sizeof(float));
#endif
m_frames[i].normal = new nzUInt8[m_vertexCount]; // Nous stockons l'indice MD2 de la normale plutôt que la normale (gain d'espace)
m_frames[i].vertices = new NzVector3f[m_vertexCount];
NzVector3f max, min;
for (unsigned int t = 0; t < header.num_tris; ++t)
{
for (unsigned int v = 0; v < 3; ++v)
{
const md2_vertex& vert = frame.vertices[triangles[t].vertices[v]];
NzVector3f vertex = rotationQuat * NzVector3f(vert.x * frame.scale.x + frame.translate.x, vert.y * frame.scale.y + frame.translate.y, vert.z * frame.scale.z + frame.translate.z);
// On fait en sorte d'avoir deux vertices de délimitation, définissant un rectangle dans l'espace
max.Maximize(vertex);
min.Minimize(vertex);
// Le MD2 ne définit pas ses vertices dans le bon ordre, il nous faut donc les ajouter dans l'ordre inverse
unsigned int index = m_vertexCount - (t*3 + v) - 1;
m_frames[i].normal[index] = vert.n;
m_frames[i].vertices[index] = vertex;
}
}
m_frames[i].aabb.SetExtends(min, max);
}
m_indexBuffer = nullptr; // Pas d'indexbuffer pour l'instant
m_vertexBuffer = new NzVertexBuffer(m_vertexCount, (3+3+2)*sizeof(float), parameters.storage, nzBufferUsage_Dynamic);
nzUInt8* ptr = reinterpret_cast<nzUInt8*>(m_vertexBuffer->Map(nzBufferAccess_WriteOnly));
if (!ptr)
{
NazaraError("Failed to map vertex buffer");
Destroy();
return false;
}
// On avance jusqu'aux dernières coordonnées de texture et on les définit dans l'ordre inverse
ptr += s_declaration.GetElement(nzElementStream_VertexData, nzElementUsage_TexCoord)->offset + stride * (m_vertexCount-1);
for (unsigned int t = 0; t < header.num_tris; ++t)
{
for (unsigned int v = 0; v < 3; ++v)
{
const md2_texCoord& texC = texCoords[triangles[t].texCoords[v]];
NzVector2f* coords = reinterpret_cast<NzVector2f*>(ptr);
coords->x = texC.u / static_cast<float>(header.skinwidth);
coords->y = 1.f - texC.v / static_cast<float>(header.skinheight);
ptr -= stride;
}
}
if (!m_vertexBuffer->Unmap())
{
NazaraError("Failed to unmap buffer");
Destroy();
return false;
}
m_vertexBuffer->AddResourceReference();
m_vertexBuffer->SetPersistent(false);
AnimateImpl(0, 0, 0.f);
return true;
}
void NzMD2Mesh::Destroy()
{
if (m_frames)
{
for (unsigned int i = 0; i < m_frameCount; ++i)
{
delete[] m_frames[i].normal;
delete[] m_frames[i].vertices;
}
delete[] m_frames;
m_frames = nullptr;
}
if (m_indexBuffer)
{
m_indexBuffer->RemoveResourceReference();
m_indexBuffer = nullptr;
}
if (m_vertexBuffer)
{
m_vertexBuffer->RemoveResourceReference();
m_vertexBuffer = nullptr;
}
}
const NzAxisAlignedBox& NzMD2Mesh::GetAABB() const
{
return m_aabb;
}
nzAnimationType NzMD2Mesh::GetAnimationType() const
{
if (m_frameCount > 1)
return nzAnimationType_Keyframe;
else
return nzAnimationType_Static;
}
unsigned int NzMD2Mesh::GetFrameCount() const
{
return m_frameCount;
}
const NzIndexBuffer* NzMD2Mesh::GetIndexBuffer() const
{
return nullptr;
//return m_indexBuffer;
}
nzPrimitiveType NzMD2Mesh::GetPrimitiveType() const
{
return nzPrimitiveType_TriangleList;
}
const NzVertexBuffer* NzMD2Mesh::GetVertexBuffer() const
{
return m_vertexBuffer;
}
const NzVertexDeclaration* NzMD2Mesh::GetVertexDeclaration() const
{
return &s_declaration;
}
void NzMD2Mesh::Initialize()
{
NzVertexElement elements[3];
elements[0].offset = 0;
elements[0].type = nzElementType_Float3;
elements[0].usage = nzElementUsage_Position;
elements[1].offset = 3*sizeof(float);
elements[1].type = nzElementType_Float3;
elements[1].usage = nzElementUsage_Normal;
elements[2].offset = 3*sizeof(float) + 3*sizeof(float);
elements[2].type = nzElementType_Float2;
elements[2].usage = nzElementUsage_TexCoord;
s_declaration.Create(elements, 3);
}
void NzMD2Mesh::Uninitialize()
{
s_declaration.Destroy();
}
void NzMD2Mesh::AnimateImpl(unsigned int frameA, unsigned int frameB, float interpolation)
{
nzUInt8* ptr = reinterpret_cast<nzUInt8*>(m_vertexBuffer->Map(nzBufferAccess_WriteOnly));
if (!ptr)
{
NazaraError("Failed to map vertex buffer");
return;
}
unsigned int stride = s_declaration.GetStride(nzElementStream_VertexData);
unsigned int positionOffset = s_declaration.GetElement(nzElementStream_VertexData, nzElementUsage_Position)->offset;
unsigned int normalOffset = s_declaration.GetElement(nzElementStream_VertexData, nzElementUsage_Normal)->offset;
Frame* fA = &m_frames[frameA];
Frame* fB = &m_frames[frameB];
for (unsigned int i = 0; i < m_vertexCount; ++i)
{
NzVector3f* position = reinterpret_cast<NzVector3f*>(ptr + positionOffset);
NzVector3f* normal = reinterpret_cast<NzVector3f*>(ptr + normalOffset);
*position = fA->vertices[i] + interpolation * (fB->vertices[i] - fA->vertices[i]);
*normal = md2Normals[fA->normal[i]] + interpolation * (md2Normals[fB->normal[i]] - md2Normals[fA->normal[i]]);
ptr += stride;
}
if (!m_vertexBuffer->Unmap())
NazaraWarning("Failed to unmap vertex buffer, expect mesh corruption");
// Interpolation de l'AABB
NzVector3f max1 = fA->aabb.GetMaximum();
NzVector3f min1 = fA->aabb.GetMinimum();
m_aabb.SetExtends(min1 + interpolation * (fB->aabb.GetMinimum() - min1), max1 + interpolation * (fB->aabb.GetMaximum() - max1));
}
NzVertexDeclaration NzMD2Mesh::s_declaration;

View File

@@ -1,346 +1,346 @@
// Copyright (C) 2012 Jérôme Leclercq
// This file is part of the "Nazara Engine - Utility module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Utility/Loaders/PCX.hpp>
#include <Nazara/Core/Endianness.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Core/File.hpp>
#include <Nazara/Core/InputStream.hpp>
#include <Nazara/Core/MemoryStream.hpp>
#include <Nazara/Utility/Image.hpp>
#include <Nazara/Utility/Debug.hpp>
// Auteur du loader original : David Henry
namespace
{
struct pcx_header
{
nzUInt8 manufacturer;
nzUInt8 version;
nzUInt8 encoding;
nzUInt8 bitsPerPixel;
nzUInt16 xmin, ymin;
nzUInt16 xmax, ymax;
nzUInt16 horzRes, vertRes;
nzUInt8 palette[48];
nzUInt8 reserved;
nzUInt8 numColorPlanes;
nzUInt16 bytesPerScanLine;
nzUInt16 paletteType;
nzUInt16 horzSize, vertSize;
nzUInt8 padding[54];
};
bool NzLoader_PCX_Check(NzInputStream& stream, const NzImageParams& parameters)
{
NazaraUnused(parameters);
nzUInt8 manufacturer;
if (stream.Read(&manufacturer, 1) != 1)
return false;
return manufacturer == 0x0a;
}
bool NzLoader_PCX_Load(NzImage* image, NzInputStream& stream, const NzImageParams& parameters)
{
NazaraUnused(parameters);
pcx_header header;
if (stream.Read(&header, sizeof(pcx_header)) != sizeof(pcx_header))
{
NazaraError("Failed to read header");
return false;
}
#if defined(NAZARA_BIG_ENDIAN)
// Les fichiers PCX sont en little endian
NzByteSwap(&header.xmin, sizeof(nzUInt16));
NzByteSwap(&header.ymin, sizeof(nzUInt16));
NzByteSwap(&header.xmax, sizeof(nzUInt16));
NzByteSwap(&header.ymax, sizeof(nzUInt16));
NzByteSwap(&header.horzRes, sizeof(nzUInt16));
NzByteSwap(&header.vertRes, sizeof(nzUInt16));
NzByteSwap(&header.bytesPerScanLine, sizeof(nzUInt16));
NzByteSwap(&header.paletteType, sizeof(nzUInt16));
NzByteSwap(&header.horzSize, sizeof(nzUInt16));
NzByteSwap(&header.vertSize, sizeof(nzUInt16));
#endif
unsigned int bitCount = header.bitsPerPixel * header.numColorPlanes;
unsigned int width = header.xmax - header.xmin+1;
unsigned int height = header.ymax - header.ymin+1;
if (!image->Create(nzImageType_2D, nzPixelFormat_RGB8, width, height, 1, (parameters.levelCount > 0) ? parameters.levelCount : 1))
{
NazaraError("Failed to create image");
return false;
}
nzUInt8* pixels = image->GetPixels();
int rle_value = 0;
unsigned int rle_count = 0;
switch (bitCount)
{
case 1:
{
for (unsigned int y = 0; y < height; ++y)
{
nzUInt8* ptr = &pixels[y * width * 3];
int bytes = header.bytesPerScanLine;
/* decode line number y */
while (bytes--)
{
if (rle_count == 0)
{
if (!stream.Read(&rle_value, 1))
{
NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')');
return false;
}
if (rle_value < 0xc0)
rle_count = 1;
else
{
rle_count = rle_value - 0xc0;
if (!stream.Read(&rle_value, 1))
{
NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')');
return false;
}
}
}
rle_count--;
for (int i = 7; i >= 0; --i)
{
int colorIndex = ((rle_value & (1 << i)) > 0);
*ptr++ = header.palette[colorIndex * 3 + 0];
*ptr++ = header.palette[colorIndex * 3 + 1];
*ptr++ = header.palette[colorIndex * 3 + 2];
}
}
}
break;
}
case 4:
{
nzUInt8* colorIndex = new nzUInt8[width];
nzUInt8* line = new nzUInt8[header.bytesPerScanLine];
for (unsigned int y = 0; y < height; ++y)
{
nzUInt8* ptr = &pixels[y * width * 3];
std::memset(colorIndex, 0, width);
for (unsigned int c = 0; c < 4; ++c)
{
nzUInt8* pLine = line;
int bytes = header.bytesPerScanLine;
/* decode line number y */
while (bytes--)
{
if (rle_count == 0)
{
if (!stream.Read(&rle_value, 1))
{
NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')');
delete[] colorIndex;
delete[] line;
return false;
}
if (rle_value < 0xc0)
rle_count = 1;
else
{
rle_count = rle_value - 0xc0;
if (!stream.Read(&rle_value, 1))
{
NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')');
delete[] colorIndex;
delete[] line;
return false;
}
}
}
rle_count--;
*(pLine++) = rle_value;
}
/* compute line's color indexes */
for (unsigned int x = 0; x < width; ++x)
{
if (line[x / 8] & (128 >> (x % 8)))
colorIndex[x] += (1 << c);
}
}
/* decode scan line. color index => rgb */
for (unsigned int x = 0; x < width; ++x)
{
*ptr++ = header.palette[colorIndex[x] * 3 + 0];
*ptr++ = header.palette[colorIndex[x] * 3 + 1];
*ptr++ = header.palette[colorIndex[x] * 3 + 2];
}
}
/* release memory */
delete[] colorIndex;
delete[] line;
break;
}
case 8:
{
nzUInt8 palette[768];
/* the palette is contained in the last 769 bytes of the file */
unsigned int curPos = stream.GetCursorPos();
stream.SetCursorPos(stream.GetSize()-769);
nzUInt8 magic;
if (!stream.Read(&magic, 1))
{
NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')');
return false;
}
/* first byte must be equal to 0x0c (12) */
if (magic != 0x0c)
{
NazaraError("Colormap's first byte must be 0x0c (0x" + NzString::Number(magic, 16) + ')');
return false;
}
/* read palette */
if (stream.Read(palette, 768) != 768)
{
NazaraError("Failed to read palette");
return false;
}
stream.SetCursorPos(curPos);
/* read pixel data */
for (unsigned int y = 0; y < height; ++y)
{
nzUInt8* ptr = &pixels[y * width * 3];
int bytes = header.bytesPerScanLine;
/* decode line number y */
while (bytes--)
{
if (rle_count == 0)
{
if (!stream.Read(&rle_value, 1))
{
NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')');
return false;
}
if (rle_value < 0xc0)
rle_count = 1;
else
{
rle_count = rle_value - 0xc0;
if (!stream.Read(&rle_value, 1))
{
NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')');
return false;
}
}
}
rle_count--;
*ptr++ = palette[rle_value * 3 + 0];
*ptr++ = palette[rle_value * 3 + 1];
*ptr++ = palette[rle_value * 3 + 2];
}
}
break;
}
case 24:
{
for (unsigned int y = 0; y < height; ++y)
{
/* for each color plane */
for (int c = 0; c < 3; ++c)
{
nzUInt8* ptr = &pixels[y * width * 4];
int bytes = header.bytesPerScanLine;
/* decode line number y */
while (bytes--)
{
if (rle_count == 0)
{
if (!stream.Read(&rle_value, 1))
{
NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')');
return false;
}
if (rle_value < 0xc0)
rle_count = 1;
else
{
rle_count = rle_value - 0xc0;
if (!stream.Read(&rle_value, 1))
{
NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')');
return false;
}
}
}
rle_count--;
ptr[c] = static_cast<nzUInt8>(rle_value);
ptr += 3;
}
}
}
break;
}
default:
NazaraError("Failed to load " + NzString::Number(bitCount) + " bitcount pcx files");
return false;
}
if (parameters.loadFormat != nzPixelFormat_Undefined)
image->Convert(parameters.loadFormat);
return true;
}
}
void NzLoaders_PCX_Register()
{
NzImageLoader::RegisterLoader("pcx", NzLoader_PCX_Check, NzLoader_PCX_Load);
}
void NzLoaders_PCX_Unregister()
{
NzImageLoader::UnregisterLoader("pcx", NzLoader_PCX_Check, NzLoader_PCX_Load);
}
// Copyright (C) 2012 Jérôme Leclercq
// This file is part of the "Nazara Engine - Utility module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Utility/Loaders/PCX.hpp>
#include <Nazara/Core/Endianness.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Core/File.hpp>
#include <Nazara/Core/InputStream.hpp>
#include <Nazara/Core/MemoryStream.hpp>
#include <Nazara/Utility/Image.hpp>
#include <Nazara/Utility/Debug.hpp>
// Auteur du loader original : David Henry
namespace
{
struct pcx_header
{
nzUInt8 manufacturer;
nzUInt8 version;
nzUInt8 encoding;
nzUInt8 bitsPerPixel;
nzUInt16 xmin, ymin;
nzUInt16 xmax, ymax;
nzUInt16 horzRes, vertRes;
nzUInt8 palette[48];
nzUInt8 reserved;
nzUInt8 numColorPlanes;
nzUInt16 bytesPerScanLine;
nzUInt16 paletteType;
nzUInt16 horzSize, vertSize;
nzUInt8 padding[54];
};
bool NzLoader_PCX_Check(NzInputStream& stream, const NzImageParams& parameters)
{
NazaraUnused(parameters);
nzUInt8 manufacturer;
if (stream.Read(&manufacturer, 1) != 1)
return false;
return manufacturer == 0x0a;
}
bool NzLoader_PCX_Load(NzImage* image, NzInputStream& stream, const NzImageParams& parameters)
{
NazaraUnused(parameters);
pcx_header header;
if (stream.Read(&header, sizeof(pcx_header)) != sizeof(pcx_header))
{
NazaraError("Failed to read header");
return false;
}
#if defined(NAZARA_BIG_ENDIAN)
// Les fichiers PCX sont en little endian
NzByteSwap(&header.xmin, sizeof(nzUInt16));
NzByteSwap(&header.ymin, sizeof(nzUInt16));
NzByteSwap(&header.xmax, sizeof(nzUInt16));
NzByteSwap(&header.ymax, sizeof(nzUInt16));
NzByteSwap(&header.horzRes, sizeof(nzUInt16));
NzByteSwap(&header.vertRes, sizeof(nzUInt16));
NzByteSwap(&header.bytesPerScanLine, sizeof(nzUInt16));
NzByteSwap(&header.paletteType, sizeof(nzUInt16));
NzByteSwap(&header.horzSize, sizeof(nzUInt16));
NzByteSwap(&header.vertSize, sizeof(nzUInt16));
#endif
unsigned int bitCount = header.bitsPerPixel * header.numColorPlanes;
unsigned int width = header.xmax - header.xmin+1;
unsigned int height = header.ymax - header.ymin+1;
if (!image->Create(nzImageType_2D, nzPixelFormat_RGB8, width, height, 1, (parameters.levelCount > 0) ? parameters.levelCount : 1))
{
NazaraError("Failed to create image");
return false;
}
nzUInt8* pixels = image->GetPixels();
int rle_value = 0;
unsigned int rle_count = 0;
switch (bitCount)
{
case 1:
{
for (unsigned int y = 0; y < height; ++y)
{
nzUInt8* ptr = &pixels[y * width * 3];
int bytes = header.bytesPerScanLine;
/* decode line number y */
while (bytes--)
{
if (rle_count == 0)
{
if (!stream.Read(&rle_value, 1))
{
NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')');
return false;
}
if (rle_value < 0xc0)
rle_count = 1;
else
{
rle_count = rle_value - 0xc0;
if (!stream.Read(&rle_value, 1))
{
NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')');
return false;
}
}
}
rle_count--;
for (int i = 7; i >= 0; --i)
{
int colorIndex = ((rle_value & (1 << i)) > 0);
*ptr++ = header.palette[colorIndex * 3 + 0];
*ptr++ = header.palette[colorIndex * 3 + 1];
*ptr++ = header.palette[colorIndex * 3 + 2];
}
}
}
break;
}
case 4:
{
nzUInt8* colorIndex = new nzUInt8[width];
nzUInt8* line = new nzUInt8[header.bytesPerScanLine];
for (unsigned int y = 0; y < height; ++y)
{
nzUInt8* ptr = &pixels[y * width * 3];
std::memset(colorIndex, 0, width);
for (unsigned int c = 0; c < 4; ++c)
{
nzUInt8* pLine = line;
int bytes = header.bytesPerScanLine;
/* decode line number y */
while (bytes--)
{
if (rle_count == 0)
{
if (!stream.Read(&rle_value, 1))
{
NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')');
delete[] colorIndex;
delete[] line;
return false;
}
if (rle_value < 0xc0)
rle_count = 1;
else
{
rle_count = rle_value - 0xc0;
if (!stream.Read(&rle_value, 1))
{
NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')');
delete[] colorIndex;
delete[] line;
return false;
}
}
}
rle_count--;
*(pLine++) = rle_value;
}
/* compute line's color indexes */
for (unsigned int x = 0; x < width; ++x)
{
if (line[x / 8] & (128 >> (x % 8)))
colorIndex[x] += (1 << c);
}
}
/* decode scan line. color index => rgb */
for (unsigned int x = 0; x < width; ++x)
{
*ptr++ = header.palette[colorIndex[x] * 3 + 0];
*ptr++ = header.palette[colorIndex[x] * 3 + 1];
*ptr++ = header.palette[colorIndex[x] * 3 + 2];
}
}
/* release memory */
delete[] colorIndex;
delete[] line;
break;
}
case 8:
{
nzUInt8 palette[768];
/* the palette is contained in the last 769 bytes of the file */
unsigned int curPos = stream.GetCursorPos();
stream.SetCursorPos(stream.GetSize()-769);
nzUInt8 magic;
if (!stream.Read(&magic, 1))
{
NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')');
return false;
}
/* first byte must be equal to 0x0c (12) */
if (magic != 0x0c)
{
NazaraError("Colormap's first byte must be 0x0c (0x" + NzString::Number(magic, 16) + ')');
return false;
}
/* read palette */
if (stream.Read(palette, 768) != 768)
{
NazaraError("Failed to read palette");
return false;
}
stream.SetCursorPos(curPos);
/* read pixel data */
for (unsigned int y = 0; y < height; ++y)
{
nzUInt8* ptr = &pixels[y * width * 3];
int bytes = header.bytesPerScanLine;
/* decode line number y */
while (bytes--)
{
if (rle_count == 0)
{
if (!stream.Read(&rle_value, 1))
{
NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')');
return false;
}
if (rle_value < 0xc0)
rle_count = 1;
else
{
rle_count = rle_value - 0xc0;
if (!stream.Read(&rle_value, 1))
{
NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')');
return false;
}
}
}
rle_count--;
*ptr++ = palette[rle_value * 3 + 0];
*ptr++ = palette[rle_value * 3 + 1];
*ptr++ = palette[rle_value * 3 + 2];
}
}
break;
}
case 24:
{
for (unsigned int y = 0; y < height; ++y)
{
/* for each color plane */
for (int c = 0; c < 3; ++c)
{
nzUInt8* ptr = &pixels[y * width * 4];
int bytes = header.bytesPerScanLine;
/* decode line number y */
while (bytes--)
{
if (rle_count == 0)
{
if (!stream.Read(&rle_value, 1))
{
NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')');
return false;
}
if (rle_value < 0xc0)
rle_count = 1;
else
{
rle_count = rle_value - 0xc0;
if (!stream.Read(&rle_value, 1))
{
NazaraError("Failed to read stream (byte " + NzString::Number(stream.GetCursorPos()) + ')');
return false;
}
}
}
rle_count--;
ptr[c] = static_cast<nzUInt8>(rle_value);
ptr += 3;
}
}
}
break;
}
default:
NazaraError("Failed to load " + NzString::Number(bitCount) + " bitcount pcx files");
return false;
}
if (parameters.loadFormat != nzPixelFormat_Undefined)
image->Convert(parameters.loadFormat);
return true;
}
}
void NzLoaders_PCX_Register()
{
NzImageLoader::RegisterLoader("pcx", NzLoader_PCX_Check, NzLoader_PCX_Load);
}
void NzLoaders_PCX_Unregister()
{
NzImageLoader::UnregisterLoader("pcx", NzLoader_PCX_Check, NzLoader_PCX_Load);
}