From 7809c48144ce143f3343aaded77b5918fb4abfbc Mon Sep 17 00:00:00 2001 From: hybrid Date: Tue, 30 Dec 2008 11:13:22 +0000 Subject: [PATCH] Merged revisions 1972:2004 from 1.5 branch, except for some revisions which seemed to be merged already. MD2 loader updated, terrain node fixed, opengl texture handling fixed. git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@2005 dfc29bdd-3216-0410-991c-e03cc46cb475 --- changes.txt | 5 + source/Irrlicht/CAnimatedMeshMD2.cpp | 366 ++---------------- source/Irrlicht/CAnimatedMeshMD2.h | 54 ++- source/Irrlicht/CMD2MeshFileLoader.cpp | 322 ++++++++++++++- source/Irrlicht/CMD2MeshFileLoader.h | 6 + source/Irrlicht/COpenGLTexture.cpp | 6 +- source/Irrlicht/CTerrainSceneNode.cpp | 259 +++++++------ tests/main.cpp | 286 +++++++------- .../Direct3D 9.0-textureRenderStates.png | Bin 0 -> 30383 bytes ...oftware Device 1.0-textureRenderStates.png | Bin 0 -> 21298 bytes tests/media/OpenGL-textureRenderStates.png | Bin 0 -> 30383 bytes ...rnings video 0.39b-textureRenderStates.png | Bin 0 -> 30383 bytes tests/tests_vc9.vcproj | 6 + tests/textureRenderStates.cpp | 61 +++ tools/GUIEditor/CGUIEditWorkspace.cpp | 8 +- 15 files changed, 754 insertions(+), 625 deletions(-) create mode 100644 tests/media/Direct3D 9.0-textureRenderStates.png create mode 100644 tests/media/Irrlicht Software Device 1.0-textureRenderStates.png create mode 100644 tests/media/OpenGL-textureRenderStates.png create mode 100644 tests/media/burnings video 0.39b-textureRenderStates.png create mode 100644 tests/textureRenderStates.cpp diff --git a/changes.txt b/changes.txt index 81444e63..372005d3 100644 --- a/changes.txt +++ b/changes.txt @@ -8,6 +8,11 @@ Changes in version 1.6 - Add a hitPosition out parameter to ISceneCollisionManager::getCollisionResultPosition() - this is a (small) API breaking change. +------------------------------------- +Changes in version 1.5.1 (??.?? 2009) + + - MD2 mesh loader: Now uses much less memory, reduced number of allocations when loading meshes. + ----------------------------------- Changes in version 1.5 (15.12.2008) diff --git a/source/Irrlicht/CAnimatedMeshMD2.cpp b/source/Irrlicht/CAnimatedMeshMD2.cpp index 0742dcb9..91d2bd1a 100644 --- a/source/Irrlicht/CAnimatedMeshMD2.cpp +++ b/source/Irrlicht/CAnimatedMeshMD2.cpp @@ -6,9 +6,7 @@ #ifdef _IRR_COMPILE_WITH_MD2_LOADER_ #include "CAnimatedMeshMD2.h" -#include "os.h" #include "SColor.h" -#include "IReadFile.h" #include "irrMath.h" namespace irr @@ -16,85 +14,8 @@ namespace irr namespace scene { -#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__) -# pragma pack( push, packing ) -# pragma pack( 1 ) -# define PACK_STRUCT -#elif defined( __GNUC__ ) -# define PACK_STRUCT __attribute__((packed)) -#else -# error compiler not supported -#endif - - // structs needed to load the md2-format - - const s32 MD2_MAGIC_NUMBER = 844121161; - const s32 MD2_VERSION = 8; - const s32 MD2_MAX_VERTS = 2048; - - // TA: private - const s32 MD2_FRAME_SHIFT = 2; - const f32 MD2_FRAME_SHIFT_RECIPROCAL = 1.f / ( 1 << MD2_FRAME_SHIFT ); - - struct SMD2Header - { - s32 magic; - s32 version; - s32 skinWidth; - s32 skinHeight; - s32 frameSize; - s32 numSkins; - s32 numVertices; - s32 numTexcoords; - s32 numTriangles; - s32 numGlCommands; - s32 numFrames; - s32 offsetSkins; - s32 offsetTexcoords; - s32 offsetTriangles; - s32 offsetFrames; - s32 offsetGlCommands; - s32 offsetEnd; - } PACK_STRUCT; - - struct SMD2Vertex - { - u8 vertex[3]; - u8 lightNormalIndex; - } PACK_STRUCT; - - struct SMD2Frame - { - f32 scale[3]; - f32 translate[3]; - c8 name[16]; - SMD2Vertex vertices[1]; - } PACK_STRUCT; - - struct SMD2Triangle - { - u16 vertexIndices[3]; - u16 textureIndices[3]; - } PACK_STRUCT; - - struct SMD2TextureCoordinate - { - s16 s; - s16 t; - } PACK_STRUCT; - - struct SMD2GLCommand - { - f32 s, t; - s32 vertexIndex; - } PACK_STRUCT; - -// Default alignment -#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__) -# pragma pack( pop, packing ) -#endif - -#undef PACK_STRUCT +const s32 MD2_FRAME_SHIFT = 2; +const f32 MD2_FRAME_SHIFT_RECIPROCAL = 1.f / ( 1 << MD2_FRAME_SHIFT ); const s32 Q2_VERTEX_NORMAL_TABLE_SIZE = 162; @@ -299,7 +220,7 @@ static const SMD2AnimationType MD2AnimationTypeList[21] = //! constructor CAnimatedMeshMD2::CAnimatedMeshMD2() -: InterpolationBuffer(0), FrameList(0), FrameCount(0), TriangleCount(0) +: InterpolationBuffer(0), FrameList(0), FrameCount(0) { #ifdef _DEBUG IAnimatedMesh::setDebugName("CAnimatedMeshMD2 IAnimatedMesh"); @@ -317,7 +238,6 @@ CAnimatedMeshMD2::~CAnimatedMeshMD2() InterpolationBuffer->drop(); } - //! returns the amount of frames in milliseconds. If the amount is 1, it is a static (=non animated) mesh. u32 CAnimatedMeshMD2::getFrameCount() const { @@ -342,7 +262,7 @@ IMesh* CAnimatedMeshMD2::getMesh(s32 frame, s32 detailLevel, s32 startFrameLoop, } -//! returns amount of mesh buffers. +//! returns amount of mesh buffers. MD2 meshes only have one buffer u32 CAnimatedMeshMD2::getMeshBufferCount() const { return 1; @@ -352,7 +272,10 @@ u32 CAnimatedMeshMD2::getMeshBufferCount() const //! returns pointer to a mesh buffer IMeshBuffer* CAnimatedMeshMD2::getMeshBuffer(u32 nr) const { - return InterpolationBuffer; + if (nr == 0) + return InterpolationBuffer; + else + return 0; } @@ -371,6 +294,7 @@ void CAnimatedMeshMD2::updateInterpolationBuffer(s32 frame, s32 startFrameLoop, { u32 firstFrame, secondFrame; f32 div; + core::vector3df* NormalTable = (core::vector3df*)&Q2_VERTEX_NORMAL_TABLE; // TA: resolve missing ipol in loop between end-start @@ -398,16 +322,24 @@ void CAnimatedMeshMD2::updateInterpolationBuffer(s32 frame, s32 startFrameLoop, } video::S3DVertex* target = static_cast(InterpolationBuffer->getVertices()); - video::S3DVertex* first = FrameList[firstFrame].pointer(); - video::S3DVertex* second = FrameList[secondFrame].pointer(); + SMD2Vert* first = FrameList[firstFrame].pointer(); + SMD2Vert* second = FrameList[secondFrame].pointer(); // interpolate both frames const u32 count = FrameList[firstFrame].size(); for (u32 i=0; iPos = (second->Pos - first->Pos) * div + first->Pos; - target->Normal = (second->Normal - first->Normal) * div + first->Normal; + core::vector3df one, two; + one.X = f32(first->Pos.X) * FrameTransforms[firstFrame].scale.X + FrameTransforms[firstFrame].translate.X; + one.Y = f32(first->Pos.Y) * FrameTransforms[firstFrame].scale.Y + FrameTransforms[firstFrame].translate.Y; + one.Z = f32(first->Pos.Z) * FrameTransforms[firstFrame].scale.Z + FrameTransforms[firstFrame].translate.Z; + two.X = f32(second->Pos.X) * FrameTransforms[secondFrame].scale.X + FrameTransforms[secondFrame].translate.X; + two.Y = f32(second->Pos.Y) * FrameTransforms[secondFrame].scale.Y + FrameTransforms[secondFrame].translate.Y; + two.Z = f32(second->Pos.Z) * FrameTransforms[secondFrame].scale.Z + FrameTransforms[secondFrame].translate.Z; + target->Pos = (two - one) * div + one; + target->Normal = (NormalTable[second->NormalIdx] - NormalTable[first->NormalIdx]) * div + + NormalTable[first->NormalIdx]; ++target; ++first; ++second; @@ -418,242 +350,6 @@ void CAnimatedMeshMD2::updateInterpolationBuffer(s32 frame, s32 startFrameLoop, InterpolationBuffer->setDirty(); } - -//! loads an md2 file -bool CAnimatedMeshMD2::loadFile(io::IReadFile* file) -{ - if (!file) - return false; - - SMD2Header header; - - file->read(&header, sizeof(SMD2Header)); - -#ifdef __BIG_ENDIAN__ - header.magic = os::Byteswap::byteswap(header.magic); - header.version = os::Byteswap::byteswap(header.version); - header.skinWidth = os::Byteswap::byteswap(header.skinWidth); - header.skinHeight = os::Byteswap::byteswap(header.skinHeight); - header.frameSize = os::Byteswap::byteswap(header.frameSize); - header.numSkins = os::Byteswap::byteswap(header.numSkins); - header.numVertices = os::Byteswap::byteswap(header.numVertices); - header.numTexcoords = os::Byteswap::byteswap(header.numTexcoords); - header.numTriangles = os::Byteswap::byteswap(header.numTriangles); - header.numGlCommands = os::Byteswap::byteswap(header.numGlCommands); - header.numFrames = os::Byteswap::byteswap(header.numFrames); - header.offsetSkins = os::Byteswap::byteswap(header.offsetSkins); - header.offsetTexcoords = os::Byteswap::byteswap(header.offsetTexcoords); - header.offsetTriangles = os::Byteswap::byteswap(header.offsetTriangles); - header.offsetFrames = os::Byteswap::byteswap(header.offsetFrames); - header.offsetGlCommands = os::Byteswap::byteswap(header.offsetGlCommands); - header.offsetEnd = os::Byteswap::byteswap(header.offsetEnd); -#endif - - if (header.magic != MD2_MAGIC_NUMBER || header.version != MD2_VERSION) - { - os::Printer::log("MD2 Loader: Wrong file header", file->getFileName(), ELL_WARNING); - return false; - } - - // create Memory for indices and frames - - TriangleCount = header.numTriangles; - if (FrameList) - delete [] FrameList; - FrameList = new core::array[header.numFrames]; - FrameCount = header.numFrames; - - s32 i; - - for (i=0; iseek(header.offsetTexcoords); - SMD2TextureCoordinate* textureCoords = new SMD2TextureCoordinate[header.numTexcoords]; - - if (!file->read(textureCoords, sizeof(SMD2TextureCoordinate)*header.numTexcoords)) - { - os::Printer::log("MD2 Loader: Error reading TextureCoords.", file->getFileName(), ELL_ERROR); - return false; - } - -#ifdef __BIG_ENDIAN__ - for (i=0; iseek(header.offsetTriangles); - - SMD2Triangle *triangles = new SMD2Triangle[header.numTriangles]; - if (!file->read(triangles, header.numTriangles *sizeof(SMD2Triangle))) - { - os::Printer::log("MD2 Loader: Error reading triangles.", file->getFileName(), ELL_ERROR); - return false; - } - -#ifdef __BIG_ENDIAN__ - for (i=0; i* vertices = new core::array< core::vector3df >[header.numFrames]; - core::array< core::vector3df >* normals = new core::array< core::vector3df >[header.numFrames]; - - file->seek(header.offsetFrames); - - for (i = 0; iread(frame, header.frameSize); - -#ifdef __BIG_ENDIAN__ - frame->scale[0] = os::Byteswap::byteswap(frame->scale[0]); - frame->scale[1] = os::Byteswap::byteswap(frame->scale[1]); - frame->scale[2] = os::Byteswap::byteswap(frame->scale[2]); - frame->translate[0] = os::Byteswap::byteswap(frame->translate[0]); - frame->translate[1] = os::Byteswap::byteswap(frame->translate[1]); - frame->translate[2] = os::Byteswap::byteswap(frame->translate[2]); -#endif - // store frame data - - SFrameData fdata; - fdata.begin = i; - fdata.end = i; - fdata.fps = 7; - - if (frame->name[0]) - { - for (s32 s = 0; frame->name[s]!=0 && (frame->name[s] < '0' || - frame->name[s] > '9'); ++s) - fdata.name += frame->name[s]; - - if (!FrameData.empty() && FrameData[FrameData.size()-1].name == fdata.name) - ++FrameData[FrameData.size()-1].end; - else - FrameData.push_back(fdata); - } - - // add vertices - - vertices[i].reallocate(header.numVertices); - for (s32 j=0; jvertices[j].vertex[0] * frame->scale[0] + frame->translate[0]; - v.Z = frame->vertices[j].vertex[1] * frame->scale[1] + frame->translate[1]; - v.Y = frame->vertices[j].vertex[2] * frame->scale[2] + frame->translate[2]; - - vertices[i].push_back(v); - - u8 normalidx = frame->vertices[j].lightNormalIndex; - if (normalidx < Q2_VERTEX_NORMAL_TABLE_SIZE) - { - v.X = Q2_VERTEX_NORMAL_TABLE[normalidx][0]; - v.Z = Q2_VERTEX_NORMAL_TABLE[normalidx][1]; - v.Y = Q2_VERTEX_NORMAL_TABLE[normalidx][2]; - } - - normals[i].push_back(v); - } - - // calculate bounding boxes - if (header.numVertices) - { - core::aabbox3d box; - box.reset(vertices[i][0]); - - for (s32 j=1; j& vert = vertices[f]; - - for (s32 t=0; tIndices.reallocate(header.numVertices); - const u32 count = TriangleCount*3; - for (u32 n=0; nIndices.push_back(n); - InterpolationBuffer->Indices.push_back(n+1); - InterpolationBuffer->Indices.push_back(n+2); - } - - // reallocate interpolate buffer - if (header.numFrames) - { - const u32 currCount = FrameList[0].size(); - InterpolationBuffer->Vertices.set_used(currCount); - - for (u32 num=0; numVertices[num].TCoords = FrameList[0].pointer()[num].TCoords; - InterpolationBuffer->Vertices[num].Color = vtx.Color; - } - } - - // clean up - - delete [] normals; - delete [] vertices; - delete [] triangles; - delete [] textureCoords; - - // return - - calculateBoundingBox(); - - return true; -} - - //! calculates the bounding box void CAnimatedMeshMD2::calculateBoundingBox() { @@ -666,8 +362,8 @@ void CAnimatedMeshMD2::calculateBoundingBox() if (defaultFrame>=FrameCount) defaultFrame = 0; - for (u32 j=0; jBoundingBox.addInternalPoint(FrameList[defaultFrame].pointer()[j].Pos); +// for (u32 j=0; jBoundingBox.addInternalPoint(FrameList[defaultFrame].pointer()[j].Pos); } } @@ -736,14 +432,14 @@ void CAnimatedMeshMD2::getFrameLoop(EMD2_ANIMATION_TYPE l, bool CAnimatedMeshMD2::getFrameLoop(const c8* name, s32& outBegin, s32&outEnd, s32& outFPS) const { - for (u32 i=0; i= FrameData.size()) + if ((u32)nr >= AnimationData.size()) return 0; - return FrameData[nr].name.c_str(); + return AnimationData[nr].name.c_str(); } diff --git a/source/Irrlicht/CAnimatedMeshMD2.h b/source/Irrlicht/CAnimatedMeshMD2.h index 9307f45c..bfa25d89 100644 --- a/source/Irrlicht/CAnimatedMeshMD2.h +++ b/source/Irrlicht/CAnimatedMeshMD2.h @@ -28,9 +28,6 @@ namespace scene //! destructor virtual ~CAnimatedMeshMD2(); - //! loads an md2 file - virtual bool loadFile(io::IReadFile* file); - //! returns the amount of frames in milliseconds. If the amount is 1, it is a static (=non animated) mesh. virtual u32 getFrameCount() const; @@ -82,19 +79,16 @@ namespace scene //! \param nr: Zero based index of animation. virtual const c8* getAnimationName(s32 nr) const; - private: - //! updates the interpolation buffer - void updateInterpolationBuffer(s32 frame, s32 startFrame, s32 endFrame); - - //! calculates the bounding box - virtual void calculateBoundingBox(); + // + // exposed for loader + // + //! the buffer that contains the most recent animation SMeshBuffer* InterpolationBuffer; - core::array *FrameList; - core::array > BoxList; - struct SFrameData + //! named animations + struct SAnimationData { core::stringc name; s32 begin; @@ -102,10 +96,44 @@ namespace scene s32 fps; }; - core::array< SFrameData > FrameData; + //! scale and translations for keyframes + struct SKeyFrameTransform + { + core::vector3df scale; + core::vector3df translate; + }; + + //! md2 vertex data + struct SMD2Vert + { + core::vector3d Pos; + u8 NormalIdx; + }; + + //! keyframe transformations + core::array FrameTransforms; + + //! keyframe vertex data + core::array *FrameList; + + //! bounding boxes for each keyframe + core::array > BoxList; + + //! named animations + core::array< SAnimationData > AnimationData; + + //! calculates the bounding box + virtual void calculateBoundingBox(); u32 FrameCount; s32 TriangleCount; + + private: + + //! updates the interpolation buffer + void updateInterpolationBuffer(s32 frame, s32 startFrame, s32 endFrame); + + }; } // end namespace scene diff --git a/source/Irrlicht/CMD2MeshFileLoader.cpp b/source/Irrlicht/CMD2MeshFileLoader.cpp index 5652d571..a529de3f 100644 --- a/source/Irrlicht/CMD2MeshFileLoader.cpp +++ b/source/Irrlicht/CMD2MeshFileLoader.cpp @@ -7,20 +7,96 @@ #include "CMD2MeshFileLoader.h" #include "CAnimatedMeshMD2.h" +#include "os.h" namespace irr { namespace scene { + +#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__) +# pragma pack( push, packing ) +# pragma pack( 1 ) +# define PACK_STRUCT +#elif defined( __GNUC__ ) +# define PACK_STRUCT __attribute__((packed)) +#else +# error compiler not supported +#endif + + // structs needed to load the md2-format + + const s32 MD2_MAGIC_NUMBER = 844121161; + const s32 MD2_VERSION = 8; + const s32 MD2_MAX_VERTS = 2048; + + struct SMD2Header + { + s32 magic; // four character code "IDP2" + s32 version; // must be 8 + s32 skinWidth; // width of the texture + s32 skinHeight; // height of the texture + s32 frameSize; // size in bytes of an animation frame + s32 numSkins; // number of textures + s32 numVertices; // total number of vertices + s32 numTexcoords; // number of vertices with texture coords + s32 numTriangles; // number of triangles + s32 numGlCommands; // number of opengl commands (triangle strip or triangle fan) + s32 numFrames; // animation keyframe count + s32 offsetSkins; // offset in bytes to 64 character skin names + s32 offsetTexcoords; // offset in bytes to texture coordinate list + s32 offsetTriangles; // offset in bytes to triangle list + s32 offsetFrames; // offset in bytes to frame list + s32 offsetGlCommands;// offset in bytes to opengl commands + s32 offsetEnd; // offset in bytes to end of file + } PACK_STRUCT; + + struct SMD2Vertex + { + u8 vertex[3]; // [0] = X, [1] = Z, [2] = Y + u8 lightNormalIndex; // index in the normal table + } PACK_STRUCT; + + struct SMD2Frame + { + f32 scale[3]; // first scale the vertex position + f32 translate[3]; // then translate the position + c8 name[16]; // the name of the animation that this key belongs to + SMD2Vertex vertices[1]; // vertex 1 of SMD2Header.numVertices + } PACK_STRUCT; + + struct SMD2Triangle + { + u16 vertexIndices[3]; + u16 textureIndices[3]; + } PACK_STRUCT; + + struct SMD2TextureCoordinate + { + s16 s; + s16 t; + } PACK_STRUCT; + + struct SMD2GLCommand + { + f32 s, t; + s32 vertexIndex; + } PACK_STRUCT; + +// Default alignment +#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__) +# pragma pack( pop, packing ) +#endif + +#undef PACK_STRUCT + //! Constructor CMD2MeshFileLoader::CMD2MeshFileLoader() { - #ifdef _DEBUG setDebugName("CMD2MeshFileLoader"); #endif - } @@ -41,7 +117,7 @@ IAnimatedMesh* CMD2MeshFileLoader::createMesh(io::IReadFile* file) IAnimatedMesh* msh = new CAnimatedMeshMD2(); if (msh) { - if (((CAnimatedMeshMD2*)msh)->loadFile(file)) + if (loadFile(file, (CAnimatedMeshMD2*)msh) ) return msh; msh->drop(); @@ -50,6 +126,246 @@ IAnimatedMesh* CMD2MeshFileLoader::createMesh(io::IReadFile* file) return 0; } +//! loads an md2 file +bool CMD2MeshFileLoader::loadFile(io::IReadFile* file, CAnimatedMeshMD2* mesh) +{ + if (!file) + return false; + + SMD2Header header; + + file->read(&header, sizeof(SMD2Header)); + +#ifdef __BIG_ENDIAN__ + header.magic = os::Byteswap::byteswap(header.magic); + header.version = os::Byteswap::byteswap(header.version); + header.skinWidth = os::Byteswap::byteswap(header.skinWidth); + header.skinHeight = os::Byteswap::byteswap(header.skinHeight); + header.frameSize = os::Byteswap::byteswap(header.frameSize); + header.numSkins = os::Byteswap::byteswap(header.numSkins); + header.numVertices = os::Byteswap::byteswap(header.numVertices); + header.numTexcoords = os::Byteswap::byteswap(header.numTexcoords); + header.numTriangles = os::Byteswap::byteswap(header.numTriangles); + header.numGlCommands = os::Byteswap::byteswap(header.numGlCommands); + header.numFrames = os::Byteswap::byteswap(header.numFrames); + header.offsetSkins = os::Byteswap::byteswap(header.offsetSkins); + header.offsetTexcoords = os::Byteswap::byteswap(header.offsetTexcoords); + header.offsetTriangles = os::Byteswap::byteswap(header.offsetTriangles); + header.offsetFrames = os::Byteswap::byteswap(header.offsetFrames); + header.offsetGlCommands = os::Byteswap::byteswap(header.offsetGlCommands); + header.offsetEnd = os::Byteswap::byteswap(header.offsetEnd); +#endif + + if (header.magic != MD2_MAGIC_NUMBER || header.version != MD2_VERSION) + { + os::Printer::log("MD2 Loader: Wrong file header", file->getFileName(), ELL_WARNING); + return false; + } + + // + // prepare mesh and allocate memory + // + + mesh->FrameCount = header.numFrames; + + // create keyframes + mesh->FrameTransforms.set_used(header.numFrames); + + // create vertex arrays for each keyframe + if (mesh->FrameList) + delete [] mesh->FrameList; + mesh->FrameList = new core::array[header.numFrames]; + + // allocate space in vertex arrays + s32 i; + for (i=0; iFrameList[i].reallocate(header.numVertices); + + // allocate interpolation buffer vertices + mesh->InterpolationBuffer->Vertices.set_used(header.numTriangles*3); + + // populate triangles + mesh->InterpolationBuffer->Indices.reallocate(header.numTriangles*3); + const s32 count = header.numTriangles*3; + for (i=0; iInterpolationBuffer->Indices.push_back(i); + mesh->InterpolationBuffer->Indices.push_back(i+1); + mesh->InterpolationBuffer->Indices.push_back(i+2); + } + + // + // read texture coordinates + // + + file->seek(header.offsetTexcoords); + SMD2TextureCoordinate* textureCoords = new SMD2TextureCoordinate[header.numTexcoords]; + + if (!file->read(textureCoords, sizeof(SMD2TextureCoordinate)*header.numTexcoords)) + { + os::Printer::log("MD2 Loader: Error reading TextureCoords.", file->getFileName(), ELL_ERROR); + return false; + } + +#ifdef __BIG_ENDIAN__ + for (i=0; iseek(header.offsetTriangles); + + SMD2Triangle *triangles = new SMD2Triangle[header.numTriangles]; + if (!file->read(triangles, header.numTriangles *sizeof(SMD2Triangle))) + { + os::Printer::log("MD2 Loader: Error reading triangles.", file->getFileName(), ELL_ERROR); + return false; + } + +#ifdef __BIG_ENDIAN__ + for (i=0; iseek(header.offsetFrames); + + for (i = 0; iread(frame, header.frameSize); + +#ifdef __BIG_ENDIAN__ + frame->scale[0] = os::Byteswap::byteswap(frame->scale[0]); + frame->scale[1] = os::Byteswap::byteswap(frame->scale[1]); + frame->scale[2] = os::Byteswap::byteswap(frame->scale[2]); + frame->translate[0] = os::Byteswap::byteswap(frame->translate[0]); + frame->translate[1] = os::Byteswap::byteswap(frame->translate[1]); + frame->translate[2] = os::Byteswap::byteswap(frame->translate[2]); +#endif + // + // store frame data + // + + CAnimatedMeshMD2::SAnimationData adata; + adata.begin = i; + adata.end = i; + adata.fps = 7; + + // Add new named animation if necessary + if (frame->name[0]) + { + // get animation name + for (s32 s = 0; s < 16 && frame->name[s]!=0 && (frame->name[s] < '0' || frame->name[s] > '9'); ++s) + { + adata.name += frame->name[s]; + } + + // Does this keyframe have the same animation name as the current animation? + if (!mesh->AnimationData.empty() && mesh->AnimationData[mesh->AnimationData.size()-1].name == adata.name) + { + // Increase the length of the animation + ++mesh->AnimationData[mesh->AnimationData.size() - 1].end; + } + else + { + // Add the new animation + mesh->AnimationData.push_back(adata); + } + } + + // save keyframe scale and translation + + mesh->FrameTransforms[i].scale.X = frame->scale[0]; + mesh->FrameTransforms[i].scale.Z = frame->scale[1]; + mesh->FrameTransforms[i].scale.Y = frame->scale[2]; + mesh->FrameTransforms[i].translate.X = frame->translate[0]; + mesh->FrameTransforms[i].translate.Z = frame->translate[1]; + mesh->FrameTransforms[i].translate.Y = frame->translate[2]; + + // add vertices + for (s32 j=0; jvertices[num].vertex[0]; + v.Pos.Z = frame->vertices[num].vertex[1]; + v.Pos.Y = frame->vertices[num].vertex[2]; + v.NormalIdx = frame->vertices[num].lightNormalIndex; + + mesh->FrameList[i].push_back(v); + } + } + + // calculate bounding boxes + if (header.numVertices) + { + core::aabbox3d box; + core::vector3df pos; + pos.X = f32(mesh->FrameList[i] [0].Pos.X) * mesh->FrameTransforms[i].scale.X + mesh->FrameTransforms[i].translate.X; + pos.Y = f32(mesh->FrameList[i] [0].Pos.Y) * mesh->FrameTransforms[i].scale.Y + mesh->FrameTransforms[i].translate.Y; + pos.Z = f32(mesh->FrameList[i] [0].Pos.Z) * mesh->FrameTransforms[i].scale.Z + mesh->FrameTransforms[i].translate.Z; + + box.reset(pos); + + for (s32 j=1; jFrameList[i] [j].Pos.X) * mesh->FrameTransforms[i].scale.X + mesh->FrameTransforms[i].translate.X; + pos.Y = f32(mesh->FrameList[i] [j].Pos.Y) * mesh->FrameTransforms[i].scale.Y + mesh->FrameTransforms[i].translate.Y; + pos.Z = f32(mesh->FrameList[i] [j].Pos.Z) * mesh->FrameTransforms[i].scale.Z + mesh->FrameTransforms[i].translate.Z; + + box.addInternalPoint(pos); + } + mesh->BoxList.push_back(box); + } + } + + // populate interpolation buffer with texture coordinates and colours + if (header.numFrames) + { + f32 dmaxs = 1.0f/(header.skinWidth); + f32 dmaxt = 1.0f/(header.skinHeight); + + for (s32 t=0; tInterpolationBuffer->Vertices[t*3 + n].TCoords.X = (textureCoords[triangles[t].textureIndices[n]].s + 0.5f) * dmaxs; + mesh->InterpolationBuffer->Vertices[t*3 + n].TCoords.Y = (textureCoords[triangles[t].textureIndices[n]].t + 0.5f) * dmaxt; + mesh->InterpolationBuffer->Vertices[t*3 + n].Color = video::SColor(255,255,255,255); + } + } + } + + // clean up + delete [] triangles; + delete [] textureCoords; + + // return + + mesh->calculateBoundingBox(); + + return true; +} + } // end namespace scene } // end namespace irr diff --git a/source/Irrlicht/CMD2MeshFileLoader.h b/source/Irrlicht/CMD2MeshFileLoader.h index 24ec8d56..d233298e 100644 --- a/source/Irrlicht/CMD2MeshFileLoader.h +++ b/source/Irrlicht/CMD2MeshFileLoader.h @@ -11,6 +11,8 @@ namespace irr { namespace scene { + +class CAnimatedMeshMD2; //! Meshloader capable of loading MD2 files class CMD2MeshFileLoader : public IMeshLoader @@ -30,6 +32,10 @@ public: //! See IReferenceCounted::drop() for more information. virtual IAnimatedMesh* createMesh(io::IReadFile* file); +private: + //! Loads the file data into the mesh + bool loadFile(io::IReadFile* file, CAnimatedMeshMD2* mesh); + }; } // end namespace scene diff --git a/source/Irrlicht/COpenGLTexture.cpp b/source/Irrlicht/COpenGLTexture.cpp index 6130d8a1..f3c5c60d 100644 --- a/source/Irrlicht/COpenGLTexture.cpp +++ b/source/Irrlicht/COpenGLTexture.cpp @@ -178,7 +178,7 @@ void COpenGLTexture::copyTexture(bool newTexture) break; } - glBindTexture(GL_TEXTURE_2D, TextureName); + Driver->setTexture(0, this); if (Driver->testGLError()) os::Printer::log("Could not bind Texture", ELL_ERROR); @@ -416,7 +416,7 @@ void COpenGLTexture::bindRTT() //! Unbind Render Target Texture void COpenGLTexture::unbindRTT() { - glBindTexture(GL_TEXTURE_2D, getOpenGLTextureName()); + Driver->setTexture(0, this); // Copy Our ViewPort To The Texture glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, getSize().Width, getSize().Height); @@ -456,7 +456,7 @@ COpenGLFBOTexture::COpenGLFBOTexture(const core::dimension2d& size, // generate color texture glGenTextures(1, &TextureName); - glBindTexture(GL_TEXTURE_2D, TextureName); + Driver->setTexture(0, this); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); diff --git a/source/Irrlicht/CTerrainSceneNode.cpp b/source/Irrlicht/CTerrainSceneNode.cpp index c1873a7e..4a00b370 100644 --- a/source/Irrlicht/CTerrainSceneNode.cpp +++ b/source/Irrlicht/CTerrainSceneNode.cpp @@ -48,11 +48,13 @@ namespace scene #endif RenderBuffer = new CDynamicMeshBuffer(video::EVT_2TCOORDS, video::EIT_16BIT); + RenderBuffer->setHardwareMappingHint(scene::EHM_STATIC, scene::EBT_VERTEX); + RenderBuffer->setHardwareMappingHint(scene::EHM_DYNAMIC, scene::EBT_INDEX); if (FileSystem) FileSystem->grab(); - setAutomaticCulling( scene::EAC_OFF ); + setAutomaticCulling(scene::EAC_OFF); } @@ -73,7 +75,7 @@ namespace scene bool CTerrainSceneNode::loadHeightMap(io::IReadFile* file, video::SColor vertexColor, s32 smoothFactor) { - if( !file ) + if (!file) return false; Mesh.MeshBuffers.clear(); @@ -91,34 +93,34 @@ namespace scene // Get the dimension of the heightmap data TerrainData.Size = heightMap->getDimension().Width; - switch( TerrainData.PatchSize ) + switch (TerrainData.PatchSize) { case ETPS_9: - if( TerrainData.MaxLOD > 3 ) + if (TerrainData.MaxLOD > 3) { TerrainData.MaxLOD = 3; } break; case ETPS_17: - if( TerrainData.MaxLOD > 4 ) + if (TerrainData.MaxLOD > 4) { TerrainData.MaxLOD = 4; } break; case ETPS_33: - if( TerrainData.MaxLOD > 5 ) + if (TerrainData.MaxLOD > 5) { TerrainData.MaxLOD = 5; } break; case ETPS_65: - if( TerrainData.MaxLOD > 6 ) + if (TerrainData.MaxLOD > 6) { TerrainData.MaxLOD = 6; } break; case ETPS_129: - if( TerrainData.MaxLOD > 7 ) + if (TerrainData.MaxLOD > 7) { TerrainData.MaxLOD = 7; } @@ -153,17 +155,17 @@ namespace scene s32 index = 0; float fx=0.f; float fx2=0.f; - for( s32 x = 0; x < TerrainData.Size; ++x ) + for (s32 x = 0; x < TerrainData.Size; ++x) { float fz=0.f; float fz2=0.f; - for( s32 z = 0; z < TerrainData.Size; ++z ) + for (s32 z = 0; z < TerrainData.Size; ++z) { video::S3DVertex2TCoords& vertex= static_cast(mb->getVertexBuffer().pointer())[index++]; vertex.Normal.set(0.0f, 1.0f, 0.0f); vertex.Color = vertexColor; vertex.Pos.X = fx; - vertex.Pos.Y = (f32) heightMap->getPixel(TerrainData.Size-x,z).getLuminance(); + vertex.Pos.Y = (f32) heightMap->getPixel(TerrainData.Size-x-1,z).getLuminance(); vertex.Pos.Z = fz; vertex.TCoords.X = vertex.TCoords2.X = 1.f-fx2; @@ -190,7 +192,7 @@ namespace scene // We copy the data to the renderBuffer, after the normals have been calculated. RenderBuffer->getVertexBuffer().set_used(numVertices); - for( u32 i = 0; i < numVertices; ++i ) + for (u32 i = 0; i < numVertices; ++i) { RenderBuffer->getVertexBuffer()[i] = mb->getVertexBuffer()[i]; RenderBuffer->getVertexBuffer()[i].Pos *= TerrainData.Scale; @@ -233,7 +235,9 @@ namespace scene //! Initializes the terrain data. Loads the vertices from the heightMapFile - bool CTerrainSceneNode::loadHeightMapRAW( io::IReadFile* file, s32 bitsPerPixel, bool signedData, bool floatVals, s32 width, video::SColor vertexColor, s32 smoothFactor ) + bool CTerrainSceneNode::loadHeightMapRAW(io::IReadFile* file, + s32 bitsPerPixel, bool signedData, bool floatVals, + s32 width, video::SColor vertexColor, s32 smoothFactor) { if (!file) return false; @@ -250,7 +254,7 @@ namespace scene // Get the dimension of the heightmap data const s32 filesize = file->getSize(); if (!width) - TerrainData.Size = core::floor32(sqrtf( (f32)( filesize / bytesPerPixel ) )); + TerrainData.Size = core::floor32(sqrtf((f32)(filesize / bytesPerPixel))); else { if ((filesize-file->getPos())/bytesPerPixel>width*width) @@ -261,34 +265,34 @@ namespace scene TerrainData.Size = width; } - switch( TerrainData.PatchSize ) + switch (TerrainData.PatchSize) { case ETPS_9: - if( TerrainData.MaxLOD > 3 ) + if (TerrainData.MaxLOD > 3) { TerrainData.MaxLOD = 3; } break; case ETPS_17: - if( TerrainData.MaxLOD > 4 ) + if (TerrainData.MaxLOD > 4) { TerrainData.MaxLOD = 4; } break; case ETPS_33: - if( TerrainData.MaxLOD > 5 ) + if (TerrainData.MaxLOD > 5) { TerrainData.MaxLOD = 5; } break; case ETPS_65: - if( TerrainData.MaxLOD > 6 ) + if (TerrainData.MaxLOD > 6) { TerrainData.MaxLOD = 6; } break; case ETPS_129: - if( TerrainData.MaxLOD > 7 ) + if (TerrainData.MaxLOD > 7) { TerrainData.MaxLOD = 7; } @@ -323,17 +327,17 @@ namespace scene const f32 tdSize = 1.0f/(f32)(TerrainData.Size-1); float fx=0.f; float fx2=0.f; - for( s32 x = 0; x < TerrainData.Size; ++x ) + for (s32 x = 0; x < TerrainData.Size; ++x) { float fz=0.f; float fz2=0.f; - for( s32 z = 0; z < TerrainData.Size; ++z ) + for (s32 z = 0; z < TerrainData.Size; ++z) { bool failure=false; vertex.Pos.X = fx; if (floatVals) { - if( file->read( &vertex.Pos.Y, bytesPerPixel ) != bytesPerPixel ) + if (file->read(&vertex.Pos.Y, bytesPerPixel) != bytesPerPixel) failure=true; } else if (signedData) @@ -343,7 +347,7 @@ namespace scene case 1: { s8 val; - if( file->read( &val, bytesPerPixel ) != bytesPerPixel ) + if (file->read(&val, bytesPerPixel) != bytesPerPixel) failure=true; vertex.Pos.Y=val; } @@ -351,7 +355,7 @@ namespace scene case 2: { s16 val; - if( file->read( &val, bytesPerPixel ) != bytesPerPixel ) + if (file->read(&val, bytesPerPixel) != bytesPerPixel) failure=true; vertex.Pos.Y=val/256.f; } @@ -359,7 +363,7 @@ namespace scene case 4: { s32 val; - if( file->read( &val, bytesPerPixel ) != bytesPerPixel ) + if (file->read(&val, bytesPerPixel) != bytesPerPixel) failure=true; vertex.Pos.Y=val/16777216.f; } @@ -373,7 +377,7 @@ namespace scene case 1: { u8 val; - if( file->read( &val, bytesPerPixel ) != bytesPerPixel ) + if (file->read(&val, bytesPerPixel) != bytesPerPixel) failure=true; vertex.Pos.Y=val; } @@ -381,7 +385,7 @@ namespace scene case 2: { u16 val; - if( file->read( &val, bytesPerPixel ) != bytesPerPixel ) + if (file->read(&val, bytesPerPixel) != bytesPerPixel) failure=true; vertex.Pos.Y=val/256.f; } @@ -389,7 +393,7 @@ namespace scene case 4: { u32 val; - if( file->read( &val, bytesPerPixel ) != bytesPerPixel ) + if (file->read(&val, bytesPerPixel) != bytesPerPixel) failure=true; vertex.Pos.Y=val/16777216.f; } @@ -407,7 +411,7 @@ namespace scene vertex.TCoords.X = vertex.TCoords2.X = 1.f-fx2; vertex.TCoords.Y = vertex.TCoords2.Y = fz2; - mb->getVertexBuffer().push_back( vertex ); + mb->getVertexBuffer().push_back(vertex); ++fz; fz2 += tdSize; } @@ -418,16 +422,16 @@ namespace scene smoothTerrain(mb, smoothFactor); // calculate smooth normals for the vertices - calculateNormals( mb ); + calculateNormals(mb); // add the MeshBuffer to the mesh - Mesh.addMeshBuffer( mb ); + Mesh.addMeshBuffer(mb); const u32 vertexCount = mb->getVertexCount(); // We copy the data to the renderBuffer, after the normals have been calculated. - RenderBuffer->getVertexBuffer().set_used( vertexCount ); + RenderBuffer->getVertexBuffer().set_used(vertexCount); - for( u32 i = 0; i < vertexCount; i++ ) + for (u32 i = 0; i < vertexCount; i++) { RenderBuffer->getVertexBuffer()[i] = mb->getVertexBuffer()[i]; RenderBuffer->getVertexBuffer()[i].Pos *= TerrainData.Scale; @@ -448,11 +452,12 @@ namespace scene // Rotate the vertices of the terrain by the rotation specified. Must be done // after calculating the terrain data, so we know what the current center of the // terrain is. - setRotation( TerrainData.Rotation ); + setRotation(TerrainData.Rotation); // Pre-allocate memory for indices - RenderBuffer->getIndexBuffer().set_used( TerrainData.PatchCount * TerrainData.PatchCount * - TerrainData.CalcPatchSize * TerrainData.CalcPatchSize * 6 ); + RenderBuffer->getIndexBuffer().set_used( + TerrainData.PatchCount*TerrainData.PatchCount* + TerrainData.CalcPatchSize*TerrainData.CalcPatchSize*6); const u32 endTime = os::Timer::getTime(); @@ -489,7 +494,7 @@ namespace scene //! Sets the pivot point for rotation of this node. This is useful for the TiledTerrainManager to //! rotate all terrain tiles around a global world point. //! NOTE: The default for the RotationPivot will be the center of the individual tile. - void CTerrainSceneNode::setRotationPivot( const core::vector3df& pivot ) + void CTerrainSceneNode::setRotationPivot(const core::vector3df& pivot) { UseDefaultRotationPivot = false; TerrainData.RotationPivot = pivot; @@ -506,28 +511,28 @@ namespace scene } - //! Apply transformation changes( scale, position, rotation ) + //! Apply transformation changes(scale, position, rotation) void CTerrainSceneNode::applyTransformation() { - if( !Mesh.getMeshBufferCount() ) + if (!Mesh.getMeshBufferCount()) return; TerrainData.Position = TerrainData.Position; video::S3DVertex2TCoords* meshVertices = (video::S3DVertex2TCoords*)Mesh.getMeshBuffer(0)->getVertices(); - s32 vtxCount = Mesh.getMeshBuffer( 0 )->getVertexCount(); + s32 vtxCount = Mesh.getMeshBuffer(0)->getVertexCount(); core::matrix4 rotMatrix; - rotMatrix.setRotationDegrees( TerrainData.Rotation ); + rotMatrix.setRotationDegrees(TerrainData.Rotation); - for( s32 i = 0; i < vtxCount; ++i ) + for (s32 i = 0; i < vtxCount; ++i) { RenderBuffer->getVertexBuffer()[i].Pos = meshVertices[i].Pos * TerrainData.Scale + TerrainData.Position; RenderBuffer->getVertexBuffer()[i].Pos -= TerrainData.RotationPivot; - rotMatrix.inverseRotateVect( RenderBuffer->getVertexBuffer()[i].Pos ); + rotMatrix.inverseRotateVect(RenderBuffer->getVertexBuffer()[i].Pos); RenderBuffer->getVertexBuffer()[i].Pos += TerrainData.RotationPivot; } - calculateDistanceThresholds( true ); + calculateDistanceThresholds(true); calculatePatchData(); RenderBuffer->setDirty(EBT_VERTEX); @@ -596,22 +601,22 @@ namespace scene // Determine each patches LOD based on distance from camera (and whether or not they are in // the view frustum). const s32 count = TerrainData.PatchCount * TerrainData.PatchCount; - for( s32 j = 0; j < count; ++j ) + for (s32 j = 0; j < count; ++j) { - if( frustum->getBoundingBox().intersectsWithBox( TerrainData.Patches[j].BoundingBox ) ) + if (frustum->getBoundingBox().intersectsWithBox(TerrainData.Patches[j].BoundingBox)) { const f32 distance = (cameraPosition.X - TerrainData.Patches[j].Center.X) * (cameraPosition.X - TerrainData.Patches[j].Center.X) + (cameraPosition.Y - TerrainData.Patches[j].Center.Y) * (cameraPosition.Y - TerrainData.Patches[j].Center.Y) + (cameraPosition.Z - TerrainData.Patches[j].Center.Z) * (cameraPosition.Z - TerrainData.Patches[j].Center.Z); - for( s32 i = TerrainData.MaxLOD - 1; i >= 0; --i ) + for (s32 i = TerrainData.MaxLOD - 1; i >= 0; --i) { - if( distance >= TerrainData.LODDistanceThreshold[i] ) + if (distance >= TerrainData.LODDistanceThreshold[i]) { TerrainData.Patches[j].CurrentLOD = i; break; } - //else if( i == 0 ) + //else if (i == 0) { // If we've turned off a patch from viewing, because of the frustum, and now we turn around and it's // too close, we need to turn it back on, at the highest LOD. The if above doesn't catch this. @@ -647,12 +652,12 @@ namespace scene IndicesToRender = 0; // Then generate the indices for all patches that are visible. - for( s32 i = 0; i < TerrainData.PatchCount; ++i ) + for (s32 i = 0; i < TerrainData.PatchCount; ++i) { - for( s32 j = 0; j < TerrainData.PatchCount; ++j ) + for (s32 j = 0; j < TerrainData.PatchCount; ++j) { const s32 index = i * TerrainData.PatchCount + j; - if( TerrainData.Patches[index].CurrentLOD >= 0 ) + if (TerrainData.Patches[index].CurrentLOD >= 0) { s32 x = 0; s32 z = 0; @@ -661,12 +666,12 @@ namespace scene const s32 step = 1 << TerrainData.Patches[index].CurrentLOD; // Loop through patch and generate indices - while( z < TerrainData.CalcPatchSize ) + while (z < TerrainData.CalcPatchSize) { - const s32 index11 = getIndex( j, i, index, x, z ); - const s32 index21 = getIndex( j, i, index, x + step, z ); - const s32 index12 = getIndex( j, i, index, x, z + step ); - const s32 index22 = getIndex( j, i, index, x + step, z + step ); + const s32 index11 = getIndex(j, i, index, x, z); + const s32 index21 = getIndex(j, i, index, x + step, z); + const s32 index12 = getIndex(j, i, index, x, z + step); + const s32 index22 = getIndex(j, i, index, x + step, z + step); IndexBuffer[IndicesToRender++]= static_cast(index12); IndexBuffer[IndicesToRender++]= static_cast(index11); @@ -691,7 +696,7 @@ namespace scene RenderBuffer->setDirty(EBT_INDEX); - if ( DynamicSelectorUpdate && TriangleSelector ) + if (DynamicSelectorUpdate && TriangleSelector) { CTerrainTriangleSelector* selector = (CTerrainTriangleSelector*)TriangleSelector; selector->setTriangleData(this, -1); @@ -718,7 +723,7 @@ namespace scene // For use with geomorphing driver->drawMeshBuffer(RenderBuffer); - RenderBuffer->getIndexBuffer().set_used( RenderBuffer->getIndexBuffer().allocated_size() ); + RenderBuffer->getIndexBuffer().set_used(RenderBuffer->getIndexBuffer().allocated_size()); // for debug purposes only: if (DebugDataVisible) @@ -727,15 +732,15 @@ namespace scene m.Lighting = false; driver->setMaterial(m); if (DebugDataVisible & scene::EDS_BBOX) - driver->draw3DBox( TerrainData.BoundingBox, video::SColor(255,255,255,255)); + driver->draw3DBox(TerrainData.BoundingBox, video::SColor(255,255,255,255)); const s32 count = TerrainData.PatchCount * TerrainData.PatchCount; s32 visible = 0; if (DebugDataVisible & scene::EDS_BBOX_BUFFERS) { - for( s32 j = 0; j < count; ++j ) + for (s32 j = 0; j < count; ++j) { - driver->draw3DBox( TerrainData.Patches[j].BoundingBox, video::SColor(255,255,0,0)); + driver->draw3DBox(TerrainData.Patches[j].BoundingBox, video::SColor(255,255,0,0)); visible += (TerrainData.Patches[j].CurrentLOD >= 0); } } @@ -746,9 +751,9 @@ namespace scene "__debugnormal", 0xFFECEC00, 0xFF999900, 4, 8, 1.f, 0.6f, 0.05f, 0.3f); - if ( 0 == arrow ) + if (0 == arrow) { - arrow = SceneManager->getMesh( "__debugnormal" ); + arrow = SceneManager->getMesh("__debugnormal"); } IMesh *mesh = arrow->getMesh(0); @@ -758,7 +763,7 @@ namespace scene // draw normals driver->setTransform(video::ETS_WORLD, core::IdentityMatrix); - for ( u32 i=0; i != RenderBuffer->getVertexCount(); ++i ) + for (u32 i=0; i != RenderBuffer->getVertexCount(); ++i) { const core::vector3df& v = RenderBuffer->getNormal(i); // align to v->Normal @@ -777,7 +782,7 @@ namespace scene m2=AbsoluteTransformation*m2; driver->setTransform(video::ETS_WORLD, m2 ); - for ( u32 a = 0; a != mesh->getMeshBufferCount(); ++a ) + for (u32 a = 0; a != mesh->getMeshBufferCount(); ++a) driver->drawMeshBuffer(mesh->getMeshBuffer(a)); } driver->setTransform(video::ETS_WORLD, AbsoluteTransformation); @@ -806,7 +811,7 @@ namespace scene //! Return the bounding box of a patch - const core::aabbox3d& CTerrainSceneNode::getBoundingBox( s32 patchX, s32 patchZ ) const + const core::aabbox3d& CTerrainSceneNode::getBoundingBox(s32 patchX, s32 patchZ) const { return TerrainData.Patches[patchX * TerrainData.PatchCount + patchZ].BoundingBox; } @@ -829,7 +834,7 @@ namespace scene for (u32 n=0; ngetIndexBuffer().getType() ); + mb.getIndexBuffer().setType(RenderBuffer->getIndexBuffer().getType()); // calculate the step we take for all patches, since LOD is the same const s32 step = 1 << LOD; @@ -846,17 +851,17 @@ namespace scene // Loop through patch and generate indices while (z < TerrainData.CalcPatchSize) { - const s32 index11 = getIndex( j, i, index, x, z ); - const s32 index21 = getIndex( j, i, index, x + step, z ); - const s32 index12 = getIndex( j, i, index, x, z + step ); - const s32 index22 = getIndex( j, i, index, x + step, z + step ); + const s32 index11 = getIndex(j, i, index, x, z); + const s32 index21 = getIndex(j, i, index, x + step, z); + const s32 index12 = getIndex(j, i, index, x, z + step); + const s32 index22 = getIndex(j, i, index, x + step, z + step); - mb.getIndexBuffer().push_back( index12 ); - mb.getIndexBuffer().push_back( index11 ); - mb.getIndexBuffer().push_back( index22 ); - mb.getIndexBuffer().push_back( index22 ); - mb.getIndexBuffer().push_back( index11 ); - mb.getIndexBuffer().push_back( index21 ); + mb.getIndexBuffer().push_back(index12); + mb.getIndexBuffer().push_back(index11); + mb.getIndexBuffer().push_back(index22); + mb.getIndexBuffer().push_back(index22); + mb.getIndexBuffer().push_back(index11); + mb.getIndexBuffer().push_back(index21); // increment index position horizontally x += step; @@ -883,17 +888,18 @@ namespace scene //! \return: Number if indices put into the buffer. s32 CTerrainSceneNode::getIndicesForPatch(core::array& indices, s32 patchX, s32 patchZ, s32 LOD) { - if ( patchX < 0 || patchX > TerrainData.PatchCount - 1 || patchZ < 0 || patchZ > TerrainData.PatchCount - 1 ) + if (patchX < 0 || patchX > TerrainData.PatchCount-1 || + patchZ < 0 || patchZ > TerrainData.PatchCount-1) return -1; - if ( LOD < -1 || LOD > TerrainData.MaxLOD - 1 ) + if (LOD < -1 || LOD > TerrainData.MaxLOD - 1) return -1; core::array cLODs; bool setLODs = false; // If LOD of -1 was passed in, use the CurrentLOD of the patch specified - if ( LOD == -1 ) + if (LOD == -1) { LOD = TerrainData.Patches[patchX * TerrainData.PatchCount + patchZ].CurrentLOD; } @@ -904,7 +910,7 @@ namespace scene setLODs = true; } - if ( LOD < 0 ) + if (LOD < 0) return -2; // Patch not visible, don't generate indices. // calculate the step we take for this LOD @@ -922,10 +928,10 @@ namespace scene s32 rv=0; while (z TerrainData.MaxLOD - 1 ) + if (LOD < 0 || LOD > TerrainData.MaxLOD - 1) return false; TerrainData.LODDistanceThreshold[LOD] = newDistance * newDistance; @@ -1016,7 +1022,7 @@ namespace scene if (RenderBuffer->getVertexType()==video::EVT_2TCOORDS) { - if ( resolution2 == 0 ) + if (resolution2 == 0) { ((video::S3DVertex2TCoords&)RenderBuffer->getVertexBuffer()[index]).TCoords2 = RenderBuffer->getVertexBuffer()[index].TCoords; } @@ -1048,47 +1054,47 @@ namespace scene { if (TerrainData.Patches[PatchIndex].Top && TerrainData.Patches[PatchIndex].CurrentLOD < TerrainData.Patches[PatchIndex].Top->CurrentLOD && - (vX % ( 1 << TerrainData.Patches[PatchIndex].Top->CurrentLOD)) != 0 ) + (vX % (1 << TerrainData.Patches[PatchIndex].Top->CurrentLOD)) != 0 ) { - vX -= vX % ( 1 << TerrainData.Patches[PatchIndex].Top->CurrentLOD ); + vX -= vX % (1 << TerrainData.Patches[PatchIndex].Top->CurrentLOD); } } else - if ( vZ == (u32)TerrainData.CalcPatchSize ) // bottom border + if (vZ == (u32)TerrainData.CalcPatchSize) // bottom border { if (TerrainData.Patches[PatchIndex].Bottom && TerrainData.Patches[PatchIndex].CurrentLOD < TerrainData.Patches[PatchIndex].Bottom->CurrentLOD && - (vX % ( 1 << TerrainData.Patches[PatchIndex].Bottom->CurrentLOD)) != 0) + (vX % (1 << TerrainData.Patches[PatchIndex].Bottom->CurrentLOD)) != 0) { - vX -= vX % ( 1 << TerrainData.Patches[PatchIndex].Bottom->CurrentLOD ); + vX -= vX % (1 << TerrainData.Patches[PatchIndex].Bottom->CurrentLOD); } } // left border - if ( vX == 0 ) + if (vX == 0) { if (TerrainData.Patches[PatchIndex].Left && TerrainData.Patches[PatchIndex].CurrentLOD < TerrainData.Patches[PatchIndex].Left->CurrentLOD && - ( vZ % ( 1 << TerrainData.Patches[PatchIndex].Left->CurrentLOD ) ) != 0) + (vZ % (1 << TerrainData.Patches[PatchIndex].Left->CurrentLOD)) != 0) { - vZ -= vZ % ( 1 << TerrainData.Patches[PatchIndex].Left->CurrentLOD ); + vZ -= vZ % (1 << TerrainData.Patches[PatchIndex].Left->CurrentLOD); } } else - if ( vX == (u32)TerrainData.CalcPatchSize ) // right border + if (vX == (u32)TerrainData.CalcPatchSize) // right border { if (TerrainData.Patches[PatchIndex].Right && TerrainData.Patches[PatchIndex].CurrentLOD < TerrainData.Patches[PatchIndex].Right->CurrentLOD && - ( vZ % ( 1 << TerrainData.Patches[PatchIndex].Right->CurrentLOD ) ) != 0) + (vZ % (1 << TerrainData.Patches[PatchIndex].Right->CurrentLOD)) != 0) { - vZ -= vZ % ( 1 << TerrainData.Patches[PatchIndex].Right->CurrentLOD ); + vZ -= vZ % (1 << TerrainData.Patches[PatchIndex].Right->CurrentLOD); } } - if ( vZ >= (u32)TerrainData.PatchSize ) + if (vZ >= (u32)TerrainData.PatchSize) vZ = TerrainData.CalcPatchSize; - if ( vX >= (u32)TerrainData.PatchSize ) + if (vX >= (u32)TerrainData.PatchSize) vX = TerrainData.CalcPatchSize; return (vZ + ((TerrainData.CalcPatchSize) * PatchZ)) * TerrainData.Size + @@ -1119,7 +1125,7 @@ namespace scene //! calculate smooth normals - void CTerrainSceneNode::calculateNormals( CDynamicMeshBuffer* mb ) + void CTerrainSceneNode::calculateNormals(CDynamicMeshBuffer* mb) { s32 count; core::vector3df a, b, c, t; @@ -1233,7 +1239,7 @@ namespace scene } else { - normal.set( 0.0f, 1.0f, 0.0f ); + normal.set(0.0f, 1.0f, 0.0f); } mb->getVertexBuffer()[x * TerrainData.Size + z].Normal = normal; @@ -1245,7 +1251,7 @@ namespace scene //! create patches, stuff that needs to be done only once for patches goes here. void CTerrainSceneNode::createPatches() { - TerrainData.PatchCount = (TerrainData.Size - 1) / ( TerrainData.CalcPatchSize ); + TerrainData.PatchCount = (TerrainData.Size - 1) / (TerrainData.CalcPatchSize); if (TerrainData.Patches) delete [] TerrainData.Patches; @@ -1260,9 +1266,9 @@ namespace scene // Reset the Terrains Bounding Box for re-calculation TerrainData.BoundingBox = core::aabbox3df(999999.9f, 999999.9f, 999999.9f, -999999.9f, -999999.9f, -999999.9f); - for( s32 x = 0; x < TerrainData.PatchCount; ++x ) + for (s32 x = 0; x < TerrainData.PatchCount; ++x) { - for( s32 z = 0; z < TerrainData.PatchCount; ++z ) + for (s32 z = 0; z < TerrainData.PatchCount; ++z) { const s32 index = x * TerrainData.PatchCount + z; TerrainData.Patches[index].CurrentLOD = 0; @@ -1271,38 +1277,38 @@ namespace scene TerrainData.Patches[index].BoundingBox = core::aabbox3df(999999.9f, 999999.9f, 999999.9f, -999999.9f, -999999.9f, -999999.9f); - for( s32 xx = x*(TerrainData.CalcPatchSize); xx <= ( x + 1 ) * TerrainData.CalcPatchSize; ++xx ) - for( s32 zz = z*(TerrainData.CalcPatchSize); zz <= ( z + 1 ) * TerrainData.CalcPatchSize; ++zz ) - TerrainData.Patches[index].BoundingBox.addInternalPoint( RenderBuffer->getVertexBuffer()[xx * TerrainData.Size + zz].Pos ); + for (s32 xx = x*(TerrainData.CalcPatchSize); xx <= (x + 1) * TerrainData.CalcPatchSize; ++xx) + for (s32 zz = z*(TerrainData.CalcPatchSize); zz <= (z + 1) * TerrainData.CalcPatchSize; ++zz) + TerrainData.Patches[index].BoundingBox.addInternalPoint(RenderBuffer->getVertexBuffer()[xx * TerrainData.Size + zz].Pos); // Reconfigure the bounding box of the terrain as a whole - TerrainData.BoundingBox.addInternalBox( TerrainData.Patches[index].BoundingBox ); + TerrainData.BoundingBox.addInternalBox(TerrainData.Patches[index].BoundingBox); // get center of Patch TerrainData.Patches[index].Center = TerrainData.Patches[index].BoundingBox.getCenter(); // Assign Neighbours // Top - if( x > 0 ) + if (x > 0) TerrainData.Patches[index].Top = &TerrainData.Patches[(x-1) * TerrainData.PatchCount + z]; else TerrainData.Patches[index].Top = 0; // Bottom - if( x < TerrainData.PatchCount - 1 ) + if (x < TerrainData.PatchCount - 1) TerrainData.Patches[index].Bottom = &TerrainData.Patches[(x+1) * TerrainData.PatchCount + z]; else TerrainData.Patches[index].Bottom = 0; // Left - if( z > 0 ) + if (z > 0) TerrainData.Patches[index].Left = &TerrainData.Patches[x * TerrainData.PatchCount + z - 1]; else TerrainData.Patches[index].Left = 0; // Right - if( z < TerrainData.PatchCount - 1 ) + if (z < TerrainData.PatchCount - 1) TerrainData.Patches[index].Right = &TerrainData.Patches[x * TerrainData.PatchCount + z + 1]; else TerrainData.Patches[index].Right = 0; @@ -1313,7 +1319,7 @@ namespace scene TerrainData.Center = TerrainData.BoundingBox.getCenter(); // if the default rotation pivot is still being used, update it. - if( UseDefaultRotationPivot ) + if (UseDefaultRotationPivot) { TerrainData.RotationPivot = TerrainData.Center; } @@ -1365,28 +1371,29 @@ namespace scene f32 height = -999999.9f; core::matrix4 rotMatrix; - rotMatrix.setRotationDegrees( TerrainData.Rotation ); - core::vector3df pos( x, 0.0f, z ); - rotMatrix.rotateVect( pos ); + rotMatrix.setRotationDegrees(TerrainData.Rotation); + core::vector3df pos(x, 0.0f, z); + rotMatrix.rotateVect(pos); pos -= TerrainData.Position; pos /= TerrainData.Scale; - s32 X(core::floor32( pos.X )); - s32 Z(core::floor32( pos.Z )); + s32 X(core::floor32(pos.X)); + s32 Z(core::floor32(pos.Z)); - if( X >= 0 && X < TerrainData.Size && Z >= 0 && Z < TerrainData.Size ) + if (X >= 0 && X < TerrainData.Size-1 && + Z >= 0 && Z < TerrainData.Size-1) { const video::S3DVertex2TCoords* Vertices = (const video::S3DVertex2TCoords*)Mesh.getMeshBuffer(0)->getVertices(); const core::vector3df& a = Vertices[X * TerrainData.Size + Z].Pos; const core::vector3df& b = Vertices[(X + 1) * TerrainData.Size + Z].Pos; - const core::vector3df& c = Vertices[X * TerrainData.Size + ( Z + 1 )].Pos; - const core::vector3df& d = Vertices[(X + 1) * TerrainData.Size + ( Z + 1 )].Pos; + const core::vector3df& c = Vertices[X * TerrainData.Size + (Z + 1)].Pos; + const core::vector3df& d = Vertices[(X + 1) * TerrainData.Size + (Z + 1)].Pos; // offset from integer position const f32 dx = pos.X - X; const f32 dz = pos.Z - Z; - if( dx > dz ) + if (dx > dz) height = a.Y + (d.Y - b.Y)*dz + (b.Y - a.Y)*dx; else height = a.Y + (d.Y - c.Y)*dx + (c.Y - a.Y)*dz; diff --git a/tests/main.cpp b/tests/main.cpp index 915ac76d..a34e9d90 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -1,140 +1,146 @@ -// This is the entry point for the Irrlicht test suite. -#define _CRT_SECURE_NO_WARNINGS - -#include "testUtils.h" -#include -#include -#include -#include - -// This is an MSVC pragma to link against the Irrlicht library. -// Other builds must link against it in the project files. -#if defined(_MSC_VER) -#pragma comment(lib, "Irrlicht.lib") -#endif // _MSC_VER - -typedef struct _STestDefinition -{ - bool(*testSignature)(void); - const char * testName; -} STestDefinition; - -//! This is the main entry point for the Irrlicht test suite. -/** \return The number of test that failed, i.e. 0 is success. */ -int main(int argumentCount, char * arguments[]) -{ - bool logFileOpened = openTestLog(1 == argumentCount); - assert(logFileOpened); - - if(argumentCount > 3) - { - logTestString("\nUsage: %s [testNumber] [totalFails]\n"); - closeTestLog(); - return 9999; - } - - - #define TEST(x)\ - {\ - extern bool x(void);\ - STestDefinition newTest;\ - newTest.testSignature = x;\ - newTest.testName = #x;\ - tests.push_back(newTest);\ - } - - std::vector tests; - - // Note that to interactively debug a test, you will generally want to move it - // (temporarily) to the beginning of the list, since each test runs in its own - // process. - TEST(disambiguateTextures); // Normally you should run this first, since it validates the working directory. - TEST(vectorPositionDimension2d); - TEST(irrCoreEquals); - TEST(sceneNodeAnimator); - TEST(sceneCollisionManager); - TEST(collisionResponseAnimator); - TEST(exports); - TEST(testVector3d); - TEST(testVector2d); - TEST(planeMatrix); - TEST(fast_atof); - TEST(line2dIntersectWith); - TEST(testDimension2d); - TEST(drawPixel); - TEST(md2Animation); - TEST(guiDisabledMenu); - TEST(softwareDevice); - TEST(b3dAnimation); - TEST(terrainSceneNode); - - const unsigned int numberOfTests = tests.size(); - - unsigned int testToRun = 0; - unsigned int fails = 0; - - if(argumentCount > 1) - { - testToRun = (unsigned int)atoi(arguments[1]); - if(testToRun >= numberOfTests) - { - logTestString("\nError: invalid test %d (maximum %d)\n", - testToRun, numberOfTests - 1); - closeTestLog(); - return 9999; - } - } - - if(argumentCount > 2) - fails = (unsigned int)atoi(arguments[2]); - - logTestString("\nStarting test %d, '%s'\n", - testToRun, tests[testToRun].testName); - - bool success = tests[testToRun].testSignature(); - - if(!success) - { - logTestString("\n\n\n******** Test failure ********\nTest %d '%s' failed\n"\ - "******** Test failure ********\n", - testToRun, tests[testToRun].testName); - fails++; - } - - testToRun++; - if(testToRun < numberOfTests) - { - closeTestLog(); - char runNextTest[256]; - (void)sprintf(runNextTest, "\"%s\" %d %d", arguments[0], testToRun, fails); - fails = system(runNextTest); - } - - if(1 == testToRun) - { - (void)openTestLog(false); - const int passed = numberOfTests - fails; - - logTestString("\nTests finished. %d test%s of %d passed.\n", - passed, 1 == passed ? "" : "s", numberOfTests); - - if(0 == fails) - { - time_t rawtime; - struct tm * timeinfo; - (void)time(&rawtime); - timeinfo = gmtime(&rawtime); - (void)printf("\nTest suite pass at GMT %s\n", asctime(timeinfo)); - FILE * testsLastPassedAtFile = fopen("tests-last-passed-at.txt", "w"); - if(testsLastPassedAtFile) - { - (void)fprintf(testsLastPassedAtFile, "Test suite pass at GMT %s\n", asctime(timeinfo)); - (void)fclose(testsLastPassedAtFile); - } - } - closeTestLog(); - } - - return fails; -} - +// This is the entry point for the Irrlicht test suite. +// This is an MSVC pragma to link against the Irrlicht library. +// Other builds must link against it in the project files. +#if defined(_MSC_VER) +#pragma comment(lib, "Irrlicht.lib") +#define _CRT_SECURE_NO_WARNINGS +#endif // _MSC_VER + +#include "testUtils.h" +#include +#include +#include + +/* Each test must have the same signature. Test should (but are not + * required to) live in a .cpp file of the same name. There is no + * need to #include anything since the test entry points can be + * declared as extern before calling them. + */ +#define RUN_TEST(testEntryPoint)\ + extern bool testEntryPoint(void);\ + logTestString("\nStarting test '" #testEntryPoint "'\n");\ + if(!testEntryPoint()) \ + {\ + (void)printf("\n\n\n******** Test failure ********\nTest '" #testEntryPoint "' failed\n"\ + "******** Test failure ********\n\nPress return to continue\n");\ + (void)getc(stdin);\ + fails++;\ + } + +//! This is the main entry point for the Irrlicht test suite. +/** \return The number of test that failed, i.e. 0 is success. */ +int main(int argumentCount, char * arguments[]) +{ + bool logFileOpened = openTestLog(1 == argumentCount); + assert(logFileOpened); + + if(argumentCount > 3) + { + logTestString("\nUsage: %s [testNumber] [totalFails]\n"); + closeTestLog(); + return 9999; + } + + extern bool disambiguateTextures(void); + extern bool softwareDevice(void); + extern bool exports(void); + extern bool testVector3d(void); + extern bool testVector2d(void); + extern bool planeMatrix(void); + extern bool fast_atof(void); + extern bool line2dIntersectWith(void); + extern bool drawPixel(void); + extern bool md2Animation(void); + extern bool b3dAnimation(void); + extern bool guiDisabledMenu(void); + extern bool textureRenderStates(void); + + typedef struct _STest + { + bool(*testSignature)(void); + const char * testName; + } STest; + + #define TEST(x) { x, #x } + + static const STest tests[] = + { + TEST(disambiguateTextures), // Run this first, since it validates the WD. + TEST(exports), + TEST(testVector3d), + TEST(testVector2d), + TEST(planeMatrix), + TEST(fast_atof), + TEST(line2dIntersectWith), + TEST(drawPixel), + TEST(md2Animation), + TEST(guiDisabledMenu), + TEST(softwareDevice), + TEST(b3dAnimation), + TEST(textureRenderStates) + }; + static const unsigned int numberOfTests = sizeof tests / sizeof tests[0]; + + unsigned int testToRun = 0; + unsigned int fails = 0; + + if(argumentCount > 1) + { + testToRun = (unsigned int)atoi(arguments[1]); + if(testToRun >= numberOfTests) + { + logTestString("\nError: invalid test %d (maximum %d)\n", + testToRun, numberOfTests - 1); + closeTestLog(); + return 9999; + } + } + + if(argumentCount > 2) + fails = (unsigned int)atoi(arguments[2]); + + logTestString("\nStarting test %d, '%s'\n", + testToRun, tests[testToRun].testName); + + bool success = tests[testToRun].testSignature(); + + if(!success) + { + logTestString("\n\n\n******** Test failure ********\nTest %d '%s' failed\n"\ + "******** Test failure ********\n", + testToRun, tests[testToRun].testName); + fails++; + } + + testToRun++; + + if(testToRun == numberOfTests) + { + logTestString("\nTests finished. %d test%s failed.\n", fails, 1 == fails ? "" : "s"); + if(0 == fails) + { + time_t rawtime; + struct tm * timeinfo; + (void)time(&rawtime); + timeinfo = gmtime(&rawtime); + (void)printf("\nTest suite pass at GMT %s\n", asctime(timeinfo)); + FILE * testsLastPassedAtFile = fopen("tests-last-passed-at.txt", "w"); + if(testsLastPassedAtFile) + { + (void)fprintf(testsLastPassedAtFile, "Test suite pass at GMT %s\n", asctime(timeinfo)); + (void)fclose(testsLastPassedAtFile); + } + } + closeTestLog(); + } + else + { + closeTestLog(); + char runNextTest[256]; + (void)sprintf(runNextTest, "\"%s\" %d %d", arguments[0], testToRun, fails); + fails = system(runNextTest); + } + + return fails; +} + diff --git a/tests/media/Direct3D 9.0-textureRenderStates.png b/tests/media/Direct3D 9.0-textureRenderStates.png new file mode 100644 index 0000000000000000000000000000000000000000..98282fa1ab04f5f9a038f4c1b9563b53c9c4f4fd GIT binary patch literal 30383 zcmXt9TRhYM{~xJTs*%buwy5SjXHujQbILHxDd(8;oX?fRoDa=0R5B7Da+vd>944nJ zhsn7b_LW$LocZs6@w@o!bF+)>{dm1z&)4(tPIzee;QU#>vmg-Yysi!c#eBbFKEdp) z%+I|KUdkYlFi011&osC|zs<7R!_x2C6;AAI7~P-JY;yk2b4lX{`wueb^Io0Qc`t0j z<@u%i(o2u~kza&lzFj?*ZJ&-ibU0CLP}@2+Vbi$o6Ibx>{qpboPA(JozWcUkl?f9Vcpt@P~gmwV-@odmh_}#+;derZgjeQyEi`zk+N5j=YLAM#I6%$8G zM7w4s?~c%|<)qxP_-zKu-xV42<&8mFYY-#ZcmOOHwfFnzs`~M+z~7_bmZ+`Yb<&-o z`&(Bbe>VDzKAkh)IoLe7c=RmlI9xTkuRL8*yK{cFGh+K+&_eh|@ZHMzwUNjpT29Dr z8aIyn~?+WUsS zWyw{n9F*I2P6LdImj`duBLcR2Ym-UB?t9PF50<=w%>9a%$7BnEFNg2>6!~4}LIg-R z7(-D9ea8t@u`>D8viy;T6~^JAI_7-!wQb)BM#oYA!v0iR)aIWmr z)ZY5UT!aOguwkzzWJI6;Y2DSfh`I#(rE2tn(--3c>|Bic^FUIoOt+bW-QRe5@N0%~ zH-Bq-5l?MQ^!VcTuGfpvu-f|ZVfglX$3(JZd*pU+_lvE-ulJ;=S#$p)Prktf;a1k; z%=u`tS~Bz7rE!n-N%Q6C!Zp`t-s|h;&u$BTTUny{?Tm+5&#lP@j%|*Pj{9}HTqwgy z{@I|p_Ux^%jijcHB=V%tPEmt!QjsK;s8BvM|3q`wQkh-E?!V!j&Yj&6>xJH>*csMO zg{9g2SOj2&%sOHEP)58Xb2tk@! zTA9%Ce-*w05tD1{>&qbx+F`Ef`;(a6gB3?ZH$HlZ+T7Z;F0T2G)@o5g!Mc!bEH?qS z5OJWv$A9aM{w;-`ziUbhJxk64M(W2?BZX^ye`cq5{>`N=sLe)fxJiJ%`nfIcP0$%d z7o-S;=AeV`tC*5r{Wmy(X>+tw$>u=|}cPpZWE9%R+Cd%juRk7#@0qg0$b?HLdfg)THoxjX z%6n3+UKsW@$I*QA)1)`PFZqOvT)QijW}jF2w^{33>CwT+(Tk8H_Rzzh!!yj`vAv!o z6mhWQR=0k1BptOodCahj*x4!EF8q?8TlOco(F(rt{rBG$T0Ck%O$bxbx~524S?TWM z$yxJK8m=h`j|}k;?F`#CTXN9tEHL`;dvmjXdT(v+#nxN}PDihRzaoCJI&y!1d;d@% z`_Ga<Li4Ss|*uGL~yJ0r8ENRWun; zx=r4bzqUOx0!gN)m(d*Ew_4K}X+|_P!Movmsg?%xtFkR}_hGJUab&II_1)HZgM}ZP z4>}KMeL1x6yW{RV2P;e7$8&uP2YcQo!`=1M^9%7jMK6mApO7CXQ$(WM#JAmzTl)VOg1ssjz*D^D4qpV%c-2FiH3_UtbF8RBy92dUwN-R(t&WyR-T-N(bhd z!l0faC~YanXX>%wFRuv<^W|g;ZA(j@I5IlkTR8ZmesF1Z_WS#n_iMF})sMH2ox%K9 z0WdgnbIWMYe&=AP1dkac`C&-})l`~StBUlXS1Y!;aOA=2Y+B?dZEQte+A-0s-BQWL zl|y!RZqD2b>HShDsye{RyOEez@J;O+u+_4vmyEz;?nHzhFRz*Rb(`YujZxdp5s;;Y zh>dv+pQ^dv&;3;1WOyG9vWUVekU<&?0qsF!!4%j&WjU_eu3hqB4fmUeoJ-UA^gNbV zBs7*qf z`WVi?CiAZJE~U_YH|AP+&`S^wXHqr)I7W+5ofr&FDW z?l+)aQP4F;qYt}yR$4BL62}AXQ!HU-SS=u(w<$|t&;D)f{@MkW*V&XQsyp5oHlQ+i zfpj)ST<$gGP09iDgjb?eTaO%;NO_Nt$_yx`hjCq9_rI~b2?bA^fDz}+L z4G`qyf`O^iV;f$HusLUTIUYC(NU3Vl}Z)WoxwO|37jfJ zF{ruJHy^jO(d472y~?Ah-_LU~dQjF9i)RNdhg6*>yA7zsgQ?x=I7wm;7^sV&8__n8 zXpV_BtF2?oS>b0Pj{&_D?I6t9kb=Y|*-5i5^cm%xVib~8LNF}zZM2F9TS)>F1z033 z<{{Ei8|z8o1#y^AvYHL_*kR|ZZVPVPT4}r$Ei`=a0e$Ye1J(dL=boSkbu_DVE-fHtKDAD@L7R$4B+*rG%!5 zoI?{V{*y}2{+yvW(pR;Rk{E31f+z1LuEK3toM>>(v z_jNMPX8KWjb1{xCIxs#OK(e7*b%!LRSW#AdRJra=0vBkbI+s%yB92~Ar(Nn;qQO@X z5~5|?Stym1EPhUy7M!9Yoq2kGziDV_HlT7H8Z-6Z4IosHSOQwox0cKFKJjdkusR9#99`7JeZyTZ8~ zMDp`I?@+0u#Xl!pOzoo?qf1s*z1|N&$OPQoYiSV+CqSScYd=cVPxZ10khmjvqhH+ zTB~N(ez}v*KxP*@QAhwSe&+hU%EQP`iI0ypL5zrnsj*$PrAGh>DLKgPC|pe7Vjahf z&~Xdfd*9d1)c>=0bbkV-(Z=W_X-4}wIW#vTq{^gQ%9uQf8fUKq117Op03MoOS~a8N z@)%%|A-*H|Qg*A(K@{J@VlxN2yPpNzpbIz>3Uld}iFvbh*_8R+t$$vY%LR23L{nK8 zYGTt<<9f^U5N+p_F_-*ORN^latR!6`OPPF2trsI6WdO6p(nF+9o&-52X`>g?X$Rs2!GE_C*J925(gQM=h*ma1n}Y93Z&^ONT=q$+|Ei zA=^2xRQGHh=xbzW4r92P&rvfO3Bww&f2Z|r<1LWlS>_cJYH6^OpwGzu%gD766|Ad% zx576miqM7KoukA3zP>V^#O+9utsX3E#tR5x?02YNOB<}Mrn##`z(m!Wnvu@eCU+u%`FHC&NT+~M#${ud zRJbbr!iaS3X>xACbx;f#uY)$gdM4Yuz&0|YC@R2~d8Z8Bxji$6h$QXs_l0ycm%Q6 zOI(r}@5+CbjpCOcbRbEQmNOM!Oaz8qj2=3l;qJ-K&OuYoLbYv^eDUSkPw|xL6}4*@ zPCxO&0w_%OGFqY#H_EHm`MqGZMT#gQTux9VyL;-mA>_5OM5HeMY(~KbhW`oC>X~@D zcpl57&{QO#kcouNci8-Wpz(G!l<1GGB%l-7^cIosK*YjxSR|DyMs5TN6aVyxza*XPAxDN2>*)~vf zpENHOR2zJ84)Q)i1w}yEZxv~|}wP1(K!!v}975}1T;&_Wh(l=2~ zDk{E1n#e9_jMP0ib7esEOpEnu=sQv{gFyW$boeLA)Nhk=M@?;U0Q7q^6%^M%81mn3;kksy6nL$=*7x4}8Ln_>fr?K~FVnWB(@odI{H7 zv^vUpRe>|i9L0!RNv+HWM7GWZ%fW1%Xigx|;EM2?WFo z(;&pGS0f1Y0fR;SYN23W;CRs$!!9QL+ufgYQ%HDIWB`TVk-AE<)@RpChR{usH(EYx z!|soUV1Xq$(O1>~jk-S@&G-81V2FPxCUhZw0fKVkK>N1JRWB`UAvPoS z(#~Jz0fJtkmK&5{!~$CP;WO&b-KD%wI{gIyoJ8?*2}UQe@K;Sw6ZetTWy1OUJsZ2$ zNI2%)ghmheDZGS~}melkY@XhSa>a->4vqORz_j3zzng6|1SLa(L zC=v-(tEFxZaXcGyb5u+Y>fb!pfP{XtRjc0x5y@Y=0)~^D<|<20;_aofoDJ}J9hSHD zBp1+DS=8UM4=KE;C7D8A-}WhCjS9&ezn~!LmpUcbrCi5_i?y!+g}A|KUM#OMf9n); z*_kNF!+K`%R{copX}LFwbVHoAcLTCB+v~dQlhGfnk(ZZYw{C7#l1?G0O=ByjdK22u ziwA~p)7v{mYku{K)=14R)_xpS)ytgwt$q82gLU?!6fF-t=mXLinZaIIH-GySssux$ zD%0O`FL9rI?)=(Hl2l8L<3*NhBB~@!&e|uymr?91;n#9RW$xqM@=G^%3vhQ|rozdQ zJSZuevL&{)Y77&$!25=?qimr|*ZJdq$JIN=FdL*KiF8hAyxA062seZ+RgM(~VYU$j z6APfR`-*x*q(B*wvA-V=iPISf>@KzQCj0t#JeSCjQlztjk~K;&6;w_#*luPsAIdlBN9dr2h3s|R+U3fUX4T& zymYk%{FGZTBG1s|HETX_)+-b>@#MqE>h3)%FBRC1;E9R`1jQf$0;s)(nPs?md$clT6D^eW# z+6Nnktk8TBZ~uE9Lu4BnZp zI$M^!{y}hHpIqJwTfFL@5v|=eOEZ9ERH(ij0Z6FbLFvw4E%%^XRL`?If`;_bVBM7} z22hF``0fM#b+2bD^m=0H>?`?Ur5X3SsYsN!FFGCW&k^%n^n&yh0~) zh@>XGpJf67<`JSsb!C*e|jvrAM~A#tn)~PPNEV`i8@!bc*jCI zrz$yvFKlo@i@8po0-=&2F$GvyfFb>?Bp_e7XO|) zy&G9KHMBu+`~)X(D;W2`CZVlPXu+1;_~JXRqh=F(vhk1S!-!cy=pW24vWJ1~Gw`6d ztE!AcChj?(66oXQOxI!U*m$-#*m)o%t}z)@$Vh+0p}r?0?+awU>txf%Wy0fp3KW8az0bp^5rnFzwBDD+JLHifW_r(F!;$RP2bW>`-^eUTt4yDn|}3KZfnFQ)TJffhi=&X1%yug2C^lW8%a zkipZG)nd7YF<#gn;)piC@ev9tAy`#B<@J+RT8nLtZ@arVlEOneSv%mZj_$&!3(;_F2uJH-;gR~C|Y^K~%kO0&zZliSap3J?g9 z`RMOF!*^o{yU0;NQKiRdAQQ0W{E^EHC+eeYl%TOi501BlP#aqx|&UJCRqj zwK#n%(Bg025=N0OAMAPfzR4SaY6Tp{&mxGF39 z^&>7Er8&@`hbiKYC}I3zK|n%m7VfTKn$qd6FzJYf4`0u#gKHFg<|i>(0dF_Cf0UJV z>Xmy?v1ok+8Y^suso+@MYGKmcGxqk{EW1n?nZV4nCcWY|fkHLZ1pBNj)<#3yRf@D- z1o~8n+g+!YL#Wg5)Q`Q7!>Xt0v<)(Fv{!xnGkDrNu=S!)(efYA5Np<}Pbz68AXuq{ zIM@A0BET(TF0O$ZXk&*RY_3P=<<)CG&E(p#v&L>V$nq3jH#RWwJpt1M0}8jv7!;=9v-LUV5}4r2@}nT}ErxZO z$xRfA&&Dv5d6l%7Gm?@ zfb9cD+|tZA1=;`8?$A8yXpMe&IP;S6vSW91WvsiK-z7Nm$P{Td4MM4NR7`{xxk6T% zRqyjcTSl?>766g!$weV4Dj+lxQeJ?oFbL%2)D?&RKx5l|p1-IPZ^9rm5^o(6MQRpf z4kREF5S>P!!-p+ygGdq1=m~eT=DA-iC?xD1P)bNfp$2>ZKJW51(emP|f3-O`SGZL= z!PS+Cb5ALA_IJ+7f^b!Cc}XbR(<(zzkS-i*v9`9DmO@~}z{ zlYkH(Vo&al2pD}$rl>v|# z)Zgzu={{-jqYn+NE04jtvvMILfI?2J7NWh~%G;!Mrt0n+3Pdn%VTS_s%H8!>T`K7F zWs7cYy^WA1bA4Iy=W?o>zQwj{?N}i4Q<+i>jRhkDo>z^B*&*$@&CY;m)Bc*)fZkYwLTtV(qv9xIa zNxYc6L6Wk1#RQV~UGr?>-tbVip?-RxbjZ84rd7 z2j4`<-7*2mw#Pb8yqLZDCw0-f{p!GO*i>2`ry1<2phiiSCa=XJPwW}-fbR+pd$vxl2AEZW zVpKl6$-w!C*z=!te38(R{Hkq$w4t1+{hGbUkEwj;(p7lgEVgQSj^+CeXnyI1Wk~gg zNoTc^^rC#DqNLZx+({7Vqf^WK`(bXEG}+-pGp&td0x#7g-qdT$407!uPYd1rUl!nF z2)!Qt86hX!i8%+C5KW=oTI)c_f&R^j#SocmfK|3p$rkWq{{8RevVL*Cw=c4D%IK~D zMxBGcvHR!GAEp$l_-$)-vcxQPWqSH5kfL813)a*Js2z+R&5V()`NmUE8=tC$3ud)A zTTKRx&dho|f>bpg?JRpC$Fy?3Yi0Wh(_l*X-VvVqXIvz@v4DiLuuo?F42v_{+kRP9 zP(ycp08-HU^ms8U5j;bZ)#ducb((Y zd*N7>oSQAG6dBw!bJwE)hY7%lFEr+A7P!7%hm*lBZImZISe+h@X46qaV-w^F;V!K! zS&@i&i4QlLa#5orBVoNuTdU^G_W5(Lxyh}WzMo+c93DPC?c<5TI#$a4+ugOqep0~_ zTPLP@vZ6jmixWX&8YX9f3F3$0nT)@};XBJ`XZfsQ-|5drfjlQ7kyOLOvF{ zCxfdvcF)0~u>%-+6%cn1M#l!f_@3ok08!-hvuuoAHGC{b2z@QwnEOFOmXUmwJw^6(F<3#tNSY*jh|&xX zntMgyIx27|C*ebc`6N!rSFQadej3%G%y}4}aes|zockgE9W@ZV0Dp$U1jqlXA_#?O zHCW2zabmH}<0$FR%nx83Tj|iHeCaZ>$)T zFxBGHTsPFvlB#8d(Pv%iI8m&iL9XQxFF^NRhFc5MgJD9%Rn1Y%b^A#`X?1wx$Ru|J zlksIQTs5FD=chO#A20G4+=NDx8_3!)ms8(RwP4%+D^rs*Pwi+HEBmc$Woc<-5sKOf z4(TQ^(&g+F*=t2F*z$TkCLu7S>>weTA6mbt7>=%Sf@e>UB!CHflZ zlSWBR=|-WlN(yAj)atyW#v@04Af)KQsCGjj&nYKf2T44LRJ8{of5kvz$K+w7nJg+f zrtfeUnu!GG#ir~Vf9f&SGtOWItkzqSCaZ9ClKH~B^2uFm*m)(Jho$z5BUWCzzohiI z*OKvyFH)lGWTH_6G8`3VX%C-l$Pbema{tO2jX;f{%P7Jr_09P}c&uAK0a`pb?gm`` zB=fY6`WT1hBaPM;vIY;@cioIz^mgBNwsSeKSkU~Wvichn zIH+;#$mc=V+-c}S1#0e?a{sV0z0?iL+A~PCmDE}*(9JC>qiwdxF8+`v)HS6(a~#8CHqlruD4 z4h#+_LW4SI)~>0mr{W%gKon826bwJ;il-Nb^?E?}z-{S{+E0I#WJTwnNf31I_`$n& zVswRz_k@nNIul?yrkHXBPZ!sHwr~6>H-OgyHp~H$K(3to=k5szj6AiH|9$ZQ{KpLL>SBsXNYyOD05m1dY&9wuwUB*NcO#lA{8{Y6FgkXta^K80m&z*51J59uu~ zH}Q4nbV^FB{wrJsaW>h65;mp=!(dtR8@IdpYw$74Nc^x!=m1HSLOxJZy zc8CPP+j4<4`v|FdP5Ob(Pk=Cw@$}>#Puex9dOo2p59hqcj4haDl6eZ{7B)Q{3tl+` zBvMkR-#M742Xh!N~p=8YuM${NA zZ927&x!JF7g%*sws(;MU;>?a~&@SWFgAub)we@x?bkP!?H)Go30i@!^dZRHuM{Qm9 zehhJZ#s3Nl*erK?L2dgN@r)L>M5j*C2hu+;`%rS~zj`&+ERM;)8{EmYu+ui4 znC5$?pF~xSDe2Ly%Cb6Bvuuh52BE8OfF+T(%<=o%MMKd2+C_07C9s-1mt3p490rUhn+U{0y3=UthH9kN=qu%%8y%6yW(l* z{=fF1e_rhqb{`iG=aSi>j%Vj>_3waq-7|`v3t!SzV*YEVp?~|y)G9kxPOzXjjKzs0 z-o82+iT-O+v3adkw+jt3-6IzCNML0#Q9W_ppMKFM?7((Jvai4h5|$VKyTki~XfHLF z-TMwxHQG?yo-Z76WkgH{5W_F}B7XX-LI&D;dMna*VN)E&xy1C_^l>wR6ebl+;D-KO z{leo`0D4RCyZdriHAUpXom}aL!WP8^652Th%7;GDkd3=nycOCWBR{+$X=#NeUFl)m zZs+g6S%Sexo_~Te9OWijEU!p-Q&dj8Pd;V_BPX{-}84<|HcQe)eqmw z0WYa<+PLZn8r>ug_dfig$DQH zFl9R)&99Y>({BYF0BfXsGBrfQ6xqLH#94;7{<^fzmA=QH@KqK7n=Nd!>H%3ij^&oH-t&n0G0b(PrhHNJ8=_+o z2H4&qQCH98q15F9PD|l?$?^P`TZ-C2uyiiA50&+{+0HyvG$$+>-sPMdqX%%*jNd5J zSiB^1dhvdYHv7Rq@RHWcN7}@^kpCt{?Ls5IRGs8Y&^he2JD7C;FKz!KGp@HSEu98O zAdr)9c)KWd1vx;hl?l?0o+T`il$gOs`LQD@qp8T0<320d{O1FRPF}Tzvb)&4=6l!=IwgcJiB7+k15JOs$5=bDF+&;sc@~aC=YOV9Q=AKs zH#(m>X{q~zeh7U2yj`4-jo~wYWRWfG-_v>_V==uR$4sD*RTnJjyJD|%Fzx`p>qZLn z9kJ(q{i;9=oXkh8HcaT~Wa>B1LU7@?OWW{bfpYvMfu)#aDmxwzM|@LEy~*%|4P&nR zNT!QBWmVHvyhroH37t7)WFVbyFBtN$dYhe z)%u&OL*V;LOO-0WR;Ys;qSNjc%t}dII!1$y8uXN9FR1hQ#xO5OTod~#DyH+n#wr|Q zAeNO|M@ZqAMQG}*ICxd8T0i0DXu9?vb`nAjy^{j%dR;)1nxxu^tzVfB2$>vTVbaGN=+Ies~RvhbG-(+;j1`oICYc)b;UxWO~3?kA{slnOCC& zVrIw;T^CNN9?5hWF4(-PxgHA&M#P-+Ax-P*b}?Q`f5z)zTlyKQrTT5kogq^XI3otO z>}9z4o=!kwz%F@LSK4kC^T=x4XU(f}cJVcr^0-T%d4laQd^G1^P;0=P0DRwm99N^o z8IsV!Zp$hof&1{EO#QgTnoH?3X7Hms?>3=2+Z_L2l5|Jr%$0=}KRvizoJSMJQ}lL) z{z#CZ92RiU@yoDY^^7axbmkUc`ZPK40O2sC`?i;dH$O-!yZAG-c<5_Pla9SUj8{&_ z=(d5}E$3v>k`5v3Nl3K94KiLwsqPk8$L!i))*q_kM6y5Bl*~i-08Zw4#ZR52m%OqF zv$r7g+%XtjKMJV2{L04FG33k>E4iE#Yk&XC7aMUmIm?I3xUP!8*ctlFUi;BI1atM| zIAL9DxB9|1_{1fVT8i%IAe%7gQ@_58JUMGrlP3k9o~cl^(f*~E?`FD>9O&E6xn*l( zw<<|`0ZPM^YtCj;u*hyPZ0S_*^-R*G>DhonICX#UYh32kRS@WwO47lPl!-!rFMl(t z6zwmZTrg9MaFl>3E{7BqG?IB9v6VD3I+9`fv`X$uC974b>nBLrNm{2q;4Rg{wMb|! z;hRVT%HFm~(J@(}YT)CqIJS2XIb2n29{+nq4A)g&K5H(_VZZv`h&!#X%(cP*SPEAi z9?aCEA`6}k%`WbGP*gZOyib_S_kSB)(p6-IIlN&`k*3sI!!eOW$M(CDS`us_7fdOZ zY%JgXl>`k-?t*?tOzuh%>!&{m9`@U`OzEvm-(j)PAx3s-;EnDNatYmC}|f*EsZ)#bmW2 zi3L1Cp0+sE23fl3+6ww`+dElH#O;D}BtRH#%0e~ywsN@J6iEyJeCi~}`l77^CcQ_cQ_H3534cP~>R^K2sC zuR!lpM$5IO8_<=q*gL9|bCpufykBNwjwA+A!^GoDGXJ$0iXD1toCYncD7%6!v5lDO z{i+;Iup^=TD}U7hLut&QV>8gn=KZP9z?bho*8=Z_;7)kURE!Vrx&%aEp2SdOILe(s!cEDBvmROeu3U;H zn9I)Q(oZ_~{svf(7Y>ganNf{c8D@xdwZ+#O|CmRW2#&^7;LTvdsG$^IL`2rkWiL@6 z2&TpVY_cwL7x|7TG7HX%lHrs5O_|g!##E#`y$&Pi&N!Cr$ol$*eE2$y;jA0vA(GG- zx#;9&yM)FLW4Woh)pXW{3vO_E6rP=81tZJWPGg2e?dFZxqaj2q)45b?_r+^t-wUKk z@O1dHS}N$j0{*y;2JN1or!QijpYOQSW`J#%NSgf?YH>#9)B1qFOR_QV9fPsYTYv)Z z16^2u?pMzrQn5OFelgbS5!;sL^Fl)E5y3*;qwde)ehiK%_10K&@UDZSfz%YHB5OcJ z)^6FJB}00wIsp&<_Se4~<6Pm@lKb_{Qm7^ZHZkoyYJrs$Otlb2AhhYnFW&{NW;j_< zx_{Jqg_OTrpTbd=Rb;>4Jag(mge8+@hU?MC_f8qyB6tGiOT(tuhx9|-nXhrMjdWK= z-pcyBfteP>4n!80wpacIw^Esf?M>YIvEN})r$E{3P}h@f2KBU^>{>^s468asXcM}td+pvRyr>gR)*zzg>f*7|7HnbXFe zVz1s=QCw2(jaBK%%EF^E9Pj|KX+d4TJKR9rn<+*L+%U}*-t~F`mg$o9M1LfNE5q3; zFC3Pol1B6SBv{){^RuAy${;_5RIIPj_`^cN!^Ks+XPD0S!c7+r$dn1@*XH4aqoo%+uqJj(}FtqHNm4iTTAAWrDeO5)lp=pRi^z_ zxhGVN#KS4?#$09clIUB`HpF+@1P#4gD@!$R0Nz_jXLp}iqc`bJ z!%gU4atYvswtO?#@`s89zIQ+p70)5-X9fZQMbNLf@1gAz7{>NK?Z_uJ$2sDz5b4St zl-e7jAl~Br;pQEydqIE7-qxQGd7x)c3H#G@qg+um&}QMeBj>}DbmmPBez>Sv)(tDg zv^Rox{>3j8x3_uD>{ZDXXPP}eh2t{5N>c9on8P!P1UAlA$-aPU$ z^3g>d@P0R9Mt?qz2roZY|Z=4`8DXw`nglkZhv#IqbD$$a3#rPr66bl&X? zCV-j=f-jPNXB3~?6&djBVTZeil}8)S=d?jG((FSi%qWM8TS)7>h8ru^AM zmM-NqR1LPHki!GL)dNlle||r}R*&L5Xq9X;k9N5*|61OBGC#b{wmRz1)hK(FGTyJh zzgwS<*PL`9;KJNW7QQ?H8yh95m<1uq-10X++%|gKd!gd;Fxq75j+4@}_TJC#lL0Z@ zj{4?%ljOh3L68q;~sY=Vq z3Q8sp8#+lvNljkgF8h{yQ0GgW{ zpB(^@|K16=k_f0Tpa|_vWBmGGBziDo&CI}S%-4YSCncv6PNctsfI;~A(`p7WbilwqA}kJX;FV4L?M2GpMWm^ z%W;C-6+)w3VK!Ks$&?F9ib?9NsVBC9dissNRdrqAokCb&9<*q`JM`}IojClXX##ym)5$KReCPfY~LJoNQ%%4Hj`_`xrCO- zD_H=SP&O&_5#Oo8<$_^O(O_vnWqD;6%rq<3x6?w3@(I2I1vIqmF)eB*Eplh8I`ZH< zF1AH#^)OOhq5R6G=j8RwT?I7kpX?eivyYp%nsW)s6YOb!E3CP{9oF1G)8e)24%;!V z&%k|DxycSgJc9lR$v64QBI9j|vD!F=kAC~=^yLuSNuq*5d3WLB!vtkIm*d%*t0%AD znC_N^To3I2j5#6tuo57&aYDh$B5tT4UnS|mf(EU{H6I5Z*$4a*oot=Vg}#@(pw*gOKpt0i#H3u+UVTrFVbCa@Dz2` zNbU#8GSeJa(i0rm=lAV-z#pFuZw1mix3`bHfXkfA0mD{D|JL}g8BA@&+cP!~+C zVk-8blc#^W5Pw@5{(=}!s3>u?W-R2@axxm?!k(_FL2HBbMP7{#3CQr7x|t~mk~e0G zN)=XV)4Sc9k7~Q-Yx+sy{vtcUAQ*v?xl=tu_m|2+sQ?Z(cUb>^LIFNP{=yD>w%Ax z1HG#ah|pJwTn?}o?|Q!Gsb}^N{u)pVxNY#SM*dymCl_6(-6%?|AnViZ4!tk8Prv$N zVLX2VD&Vr<*K`T$fGkIEd<4;Fm;5A&!Hl+`E2GTBEM6t}zQ@I@L7kjBwU3VM>F>xqCX*9P*iX&NH9+%R}g8I`e?l$9RrVgor)&QpA`!0O&}AqTgs zw_PPJtSS)48>!JN1yalHN+Dv=31@F<2WzAmuyQRJ(rS87q=L5zccq??nR7TQgSEclFQt;^-(+f3kV*&RLlrT)&w=Vhl=8X)27-Bdr^JnIu9S!0pT z#+v2U+PQK~jxz%3;#LNN?_ z8u^`44h+NpKx&w)sGumm+U!NA?0R*+Q=In4(5=R!X^(q>;7SDYmVB9QuW!UH0AQjs zV5q6NB)V^Ltl^vMJ=C6B-+{R%yCm|z3WD!__tB%-h+~g#=1=2UT{z}0LbHj;;RMIn z2$smrKf_%*)`jLMrhz(R#l2)PQ&rpJ9M|DAo89Q#YSG%vF*DaXnR zEmmsRr5HAMYglKHhyTU6mOFn(P6l*@a^6zCJ2fIAg{2+{2*XE+1rS|v-rAOP2N_3V z{)>P6A4NbP(+?i^or#Uo%ERrgp1gNbR7*s@89Qj0jzPkpsbX@*a!7T@Mj*2+F?Y)Q5nlY!HQ%Qymm^~ z!g4*=LlND6XU&`jb#AlzeBvoSvG{oSi@bz-9O!!Gqp5; z|7t$Nf{b3Dye7&{*`Gs|1e_2~^VUoid51J?^ug@gZV}n`lBzGy=Tzb9mXs zS&bik7IdYrb(8yyeF>T7GdvNr4=>Q?1V4WMmJU3dy!Sa>B0rB>SFFd?~m!48Z1fP=nRsI#Xs|&M^2*F=<@$2hv)( z3>M)fp}kZNasAps4&C%b+HDhSexGM^d7X#{0A(I5AbJfjrvuxkf>*oW4z~Ljg za;n*B$j_PM0$_mAHE9mOMmJ52|8*e~-fJ@y|;nJ>{(y9yD zzC4A=Mx7Uwl@2zyJ?yLhAF9_q-3>)~6x5JuGK|0s8)p71zc?V90C!s>7+Q^5wB!^= z%*&VNZ*GR$=X!_^^L+{}9C7UbPbnN`tC)ZsYQp4u7E@tz8xHIlpxUQE#MtCNWzC1$ zof0+@VpHc*7d|AALe26#6qkcwsHa;(k&9VC)vyWwTjiDbk4e!M1TmcrU3QXKIZ)(yN5?$a<EIs@!UH~tgi=o6!x`i? z1WB?=UvleoXarWsN)8D1uimKmbi8a(6efeGg>^V!6=n!}A8&YR%0XwaknDTbhXH?d zFw#|m0DrXbw{r>Aat4O3X)>?7&kZ)5bq*h?eltYy^?N#COaj-)+SKt|z@CZK-gCI%vlo=0N7 zrzHSxsQA=o#<}zW67p}``IW8H=We?$X(s_B?$lZ9Tk%iu{acM<0_xW>(^{EiG#zRB zTry!e%=wUwXzKB@4ig}IGgte|&BpTM{gt=gMr>9tgVG+*JoXnc&4OsruLDQN0mV;U zgoc(Z{9y1)HY|EG+;^Ncw)u_Ux8pfF8sYnpaCr zG`Z#(a%GOG!naGew152h%8xESX1!(IL$A|x!!7Cq1rNtOEH;oD7?|9l(sofHByzKz zntr{99^@^>wa^apI3=7nm3y3N^J-I;OY-LHmvWjZ8=V0XK+ng1x37sa^Vdub=C64O zW;ar;Dhau?q0H0?U!i^%&foGm_5$P%(_rXhfh3uCPuP>4Zm66$5h(h9US7};-;wKY z#^K2$nd>m(`z!htP@G|}ta3-Z_;?Q2IsomhzGeDV-Z$BWvs|UU7#tZ`)vHRW<7Tsj zRb0HC{y0ZJP11a_Ef;kUao^JHOGP!E|BS!hE76lU`qA(icMbi!T0H%o4FnIhTkk_< zn93uTHji<;ks3c6DJG+(2KmChgPM97TA^q);WZiGP)yRlI z7Ig4xOBSB9#utdOr1`qtMaZEEamMCn0TRFGSPcezqLgWy`)B`3qE0>m=WWD)Vgi6? z|C1K!OZIy$c!c*X7G{0U!2-NPEp9S8jGM(kOjL(+F`E8~-dC4SqEM2N!(8Jgcn@|d z=RWzi>ka63NQ~YD1cS;Y_`2*!1y<$sQppPsM{?RRB;Tu<4m&I+?1145PvhfHJ=2-N z>4k%4DLiB>{9x7ltIK`2ylyyX)MtcrjKItD%2tIz|MH}~j1#T5y4#zUc9U9C686@o zI|0I9!8c984H0m@dlN>v01{r({#>To?>a^2-zGywNU zOrmN-SaUcthN}~sGKt~Medx)XK+`(j$M%U0Z>NaIeFi_6+zpA|BgG%@XUsC`t-o5@ zp=YsW8&;v$d#Fbf5;ea;`~-;2YsRfBqQs;g;kEx>+JboG5(;WkJrE*&NeMwrG{Sx3&#FzTQ0Qv3a zjDM6Y1*1uwf!{Xd6FMw6h5p&z-vI*cNkh0Q3J0#7$$6CR!U$uweFc={;b>t7GV~b* zt<7I3%bn|*K#(9L;;MPld&iql-`@1Zx`AH$2<67>m)2DTh2#&0vGKn< z9hs81e~JxHiJ;W0?i+UI=!x+2Ti{niE+1r_t~{85AN@O^Eh82Q3~O-{3hh%``K49S zzrTrBI&FSKV)#yx6dNliJ~}+;eR}Er34*)^=$2fjTlz-HW!KV-vM^+#A1c8l3Ex%+ z@UE>|83o(0JtvZm_!387Hl-{yO{}68KbuvU4_Y)29t!L$dxrRIJ%PSyXw;N1IS}o8 zY8XNs<(cR-y8$pchVxi?;0G$LjrpTZjGZ)~g#7yR)$@S5&lrW= zY`%%*j-($QpK~V6uYgq?R10&At{N;T1Puv2rLT4PI*bUtrQe6Yk$PA6uO@z< z49=r$9zO0nlB51TK3>?;XAR4&!g$!_`ZQMgQ|5i8KENFxN?tMW+h|lR`MepfSz?#F&Tk^Oyn~W# zLx7i%=Z2OdsXLv=FKfWphri#|3A^o?5t^RMgF1Lfz0&3hZ|L&u8=@?oJ?!cD=+iNQxVX5^om(#I>JrE zHavj$Z+CAum$;Fq+d z%Hs}Wv`j`vL=6@VFUY>VdR^*LzdGZ(zw?=BffUOe-( zBoqMgRZZanNa$x*`wFuEN-u7P5ow^g$tKEr_jYuIQIkZa;;IMQVeMMq7s;qx14Cx|a z;9JS0JRnL^-%sHX4_4DI6{qqN2VxgzatlCBxVsAd<5|n9Y_41%3)Y(@Z@36ZhUstc zW2c~g(eh(iHW-WWDFMJn#TiMaGO4h22i=VTr4fJ7$aw=5#;6kDelCq5<&Nnnx3Y)p zyuUD+*W~o#T9vzRfi&E{W_}-mAhc+l(GH+1%w{|>XJ?FmX1MF#rFq_~(_|U28=C-Q z<#DxJuVd3}u`qi_H7}r>H5+>EZPk`^Kogooji`UVajR#M>J>4^7p^YkZpb-u%jC24 z6Cpdma-(ft-$L0Cnq~?zp7~Yk|90 zf$w&(P2!|32ZD=`ym~`SIwSInz~UavXHL zG)ZY40U-dkYonqxfSytPttqj0>zz7Pj+KhI&c=h~(<)OvKX{odU7JLb+eG zY}PR_PFoY7Zx?PeiCX+Q*|>(K<^))X2;75pSl3^@n80h~q8S6;_!3h8F(v1vY%XEe zPOuL12Z$ZS#RYI~5>A^-ORcZq+wu zdQkX0iGNZ(9USN{53QWvkX`y_k}f6?5qozWP?jsG?`z%F%Q6D|TeSG^*HhjW)0Wdn z9r<8^i_$MJS!1G#c{b1KTPJ_{d`4_ddL0|_Y@S|awldFB2|m6GH6KdPFqvsP_%VLA zMr?SHc?o;7TGvL3;J5sVbW6jef@GIJI{8l?&typsuz!s|A)jvD$fUG3QVvf}Jbx;f zj&ZHKd&ZlYUE-IzHvGAg6EziOyTey;tG0trLbO}-DtAe1CK2~Mlw`;ym1mjtdh`_< zE%f%$gDY7a0HOY=@b=gDYN|hO^}(TjbLMj>D( z%~*av(&hq0-x`j&e8=h=J1GSwXG2i4C&Q8AF+{ z4B8Z-Q^aWZN?ZF7PkGr-F5irO8qbg;p)&=N@nC75)tA=OWe~UrM-jp9Tc8E{@>I;K-ull~VjC8GMVUgn< ze4ipa#>;^b10Z(XYcr7lIWO_qPHVGhV@^rsS5-XwKqKMx(xc*r0rB{MEjd38l&K12 z2Qz4THeV{k&->$TL^Sl5rH9JpSo#Tr2Ew1i>Gp@AqTd=={`LN@VfRA7rrtsixmc~}wR>CL zR~LhDo?O0KG=?vlY+1jGn`xqHDN#P1PRG--j+Ugtw+`N82?RK!v`{dGHg}ECXMn2? zzOc-aM?vE&Itd*)(!v%E9rM=>lW04}GH3MvI9hkRh5mD0Dg~pj8LFeforB3}-pP|L&}(ye2s#Wv#egw%Ub%0=K!k zw>J%@N9ypSO%u%tR?T*4ulbG8XaYTU#;M*reE~;dB(F_SGT~M2H+N+QhnHDkYuig9 zrYRBC!D$6^?<|J)L+}yMHx}LqNoKZ!QU>&*W*zUk6Sp7%5emYy{Md`?;<_}UG4^WP z*H5Nmo_Pd9z+sQ>=pnC@vz&-aZK zi#I*t%%t#i}5V{QUEXTd{u3l^{WzR&^uFj**3Yb#b8M^U^g{u0$!Xk12$@(9BtlQc&gJ!~W}+^kFY)^N`dD z87nGF!g)qY72onS97--ZCo^s1SKn~~Osc=Qb(A;}mlbokYm$W1+JjoaD4mQ7Qm@rR z4;P4lM)YhEKJOE_cZ2Yt;8zUR*rIr@?O#6eF)x$Y_0Cs4nq14`CrjcG$^O%wv6J1X zd$jHVkE;XoUlEre=dH{}fv(dEnmeTS5bJ67O}*M@UsCw|hhU>@yw*mRl1VTkvROPi zr-GTQe|CP0Yqj`j_csmWVQ5mD*dP2U`T?ix*)`rLn&smfkSL*f$S0pXn?FzZvTLcD zIAaAK^U2BKak}=W(%B}QtDY{PRYmtd)v7E14JdA+u2H+DiekNG&;+eax3m0bxZP(7 z=v;`ek9q#@@U8=DWP@ZBc@`{qQ5uui-slID3N%$9-W(O2qJfxy@QD(uKWJ{ z5_$fg!oNcSb;$pm*Y{JgfQP2&=Lg%7fBKG{PGc9=^RP`gcY!U$3s#F|rQ1p>(Ku_ve|8IP!Xbuq^pZ^YD2bJUb=e9y(&U_GFR z<$m7|%V{4~YoU22j{K6WPJS-cy;Lp(-7Z@c`iBE-^v-|ZbY@3V_>`8tFJ;gbX9)0P z9IoC)KDR`})0HeLLX)hjJPSmckvKxjZc17xituX;_Xlm0&b|B3_;uW`{0`AIBLHUt zsbz@=8F-x3#P2G7pwTEI(Ov~!u)XczK-q(QFlUO) zUDEY~4SzNDcBdNFuUqB$6}r3bW?W?-ISgIHLG0nBsb)d}ESInfH97i0b^7i>U`=w` zsKq~g+~7u{ytl}UO5niJQnR!7Lzr4viz%%&$0m`tUs<$K?C9cEU_}t~eDcKszLYr5 zOf0-sipBWSE3Xs)v6IkhL^WE!@n9qXSO4XWf9k%6olS(gS|k+$lYv*|WYKw>M+-x_ zT!an@^Ark-5+wj@wzC39vlqXBZzO^{-=9F zi|6UE`E;-x7q7(^ykG-sy{KX&fzbyhF-DB}iVDLq{~6w#6a){MrW*S(CvYaqpO<8= zK%N)4A-T1si3d}b{N*(GuXzU=)s-P4+IU+=%D@Ts7)^Le58BoJ`$xq-SE_33 zBv|cUaQb#ghpiv(=RZaEjp%643Mj@mAbI3_Rik*+(ZNma(7EDe2aL83(q*J@=fmj0o$FEYmz4~6yZf+&{4Zvb+y(2M_p9bi(?GW-gxnVCi@mW&r28cW zrwow9{Jxq>EvM$<1wQ(%^u6opf>`-;r%I=5@es&sRFWdHaQ5}wB0aua9d;wVd#Rz+!0sO%a zgYTFQie?wHFM~}k4roX~5JTq7ATDdnxfW7IirpSXgHbmuFSoQOd#XOhbdM%W7V-Gy~1sY#ot$ zy1(m`+UX_VY&Pi6=iAmupIl%+Q~zuj#)!;>k_3Vmd6DCL3$60dH@?!xlc&w{)0G#35> z?SWZbn7N9^b5N zpMHw8e)n8BwskGVTXm!nedkw0o6eXDvoQ<*pEx?D4W?!;z-OYsvob6me^cXUXCvqW zaTq$!AwHXV``cu11$s@Cx&3JPXf^6NNS*AuQTV~;hZOgMG?lv8;QJq1B0x*Pmz_}oO3w+~|G3vl4mKmEKidv%+WHIIvwT~?|I5K?&Xg@k#lB8VKvV%7|u zPR}C0eZo^)WJB2oC$4V@juZH{8Lqxh2CxoH!+7af&Dn712m;q(OD2+bgDyKM&cI44 zFUmcU|5MCe5iI)P$j?m@@?IJUboRW~tGi_m8U1j<&~KNt^}F#XWA$EO;G%{63}w3U z8a@7aIFLU46RhXB>C+n<-F7tnpS$kyZks}wS|=pvvY9Qj<$+%WKU_Ya^ z>g?%uyb|G_cX-=6o<=;~n4~qVgQZOSN zkdgw*_D0Jjby1KDY`M4}TStb58wL+y^x@+hj;r~frr-;$C?WeX7Q zTH$LoJlHi*^&;?mg)z%DcIK6QMV)KZPKP{PWGaY6r3(r02N3res|*%f>>H>@Sy{Yv zjyz(>!47RK;@Zs;BN=-WA7Z%Yvn9J_^#I&P@ljaU-tiA)z%uf^A8sRkzC>96hgcc@ zSiO-JcGx#ZY?W*Tds*JI-*<1BkLGb!3Mlm7ewD zC!>Fe$8T7N*$Mi!*cIpJX#!%74+qt=gZ>Uds2}Hb_Idc;8yAs{taW3fz_D@+8LW~) zcT+|r-T8HhmusJ-6{H&c0cxTKZvgDSSh7(I6=@qi*p|kvAP1s?x_&dthp!Y__Q@uV z_DkE*Dek!V%2_@9>H6f}-fGo!NY{c@HYb;8JIv+ov(x_5gZO{(@du}IQ61V>1hR1m zOCb4Yt|?4^neerWsb z*|H2yqp72JNYs|Nf2k+H-SwhENB)zB%lemNa%r9it zjS-0;p=9aJxyP3Joz#_ zeB8~6zjGZhkTX&d34N^PxlMrZ(?8~RcNtJ~8F8rFbJy*j6{NK2g0Iy$Ndgb~lH-1L z{Sp%0wA5@k-=f-LZ&1WAYcW2)G9V4t%8*!3Qf2b@o!bd*Oo4 z?7@-L(e3?&&_pETNbfB#_rLHxV(0ghqi1C`IrWvU9-SGcxZF{Y1r6}M-Fv&CUblW6 zhlRQGd-tVt&UHDMHcCB<{b1n7nDy+H+?0_bQpA(X#Sectd^z`#B+$_HThn$9Hv7DA z`w0#Cy>QB=D`)oEb_nXJ(i&+7lR z?%+yZB=z+i{*A$-IgMqi;$mWKg@1-CMyjv|7?-$65zYss$bJ#l@%-+`K>_^v;Cox+ zaR1hYsc78Za;o$C8P|N@3)cQQc>mtetchW=BrLXOyFX~BzxCq%sJ(PS`1tVpc8`m0&yQPI& za?MD#Cc@-U?X8Z77Xq#d#G2X1$I4XoE?mbNUFUAX&UlPsA2f+m>3pANnw$tPog5uU zQZ^2XDYm&xU}t6?;5&Y~ZtiF(NwVr+0=`Hy*&Kdk zDy_HELhal;8Ero#=8mH7JOUU!F+q*!92#drmLbn+=m)yVI!PbKj#9EN0bR)fd2+Mk zqLa(#-hV7_Ltv3e+Km*Ui>bww|9k3#PFEKXlzL+jBe?ya{mUnh>=i6s))={R5hZ}L zhBx@%&;}a5{9y#OfXbjnYWY`unEO-ic7;Hfk_Yy7cW-QjED`5+DH_0W)e<$=l z>d}>0<=XcPy`&>V+>j6@H`}V*oK}d9iS98$Np>O-Lqap~j)j1VIub~QIR`GdViAIZ z+0R5>cv4GA$GM9Oj6YlPSeWhD-Ye`3faX$gP5vV_NWfWUETLj=5}C;BXKzIrcI$8q zL~qkz0SGzVH*W2Oozs)PV~$7HvVI(F2XiDaOgumkJL_lg7|op77S>)cN7PgVI@YK1@M2#ngaEK>=(ObT5I*QP=*lTA%ie4<7)th)3 zx)=c(5&{C*#gQYio}u`+X+C#82j^+%d0QZ3Olosm8|?k`2k;1c0~MVNnP;;LI>)}< zy+c#kUefnvz*u65CYkF7mc4uLvuSOt?z=kL_ko?s@c2_(f>EVv?bhvaX!OrbrPG}z zmM)#@Me@Ic$(lg%Ksb)@vbb98t=jl>jn&LF=S4Q!t`;ar+`v+@5&`|^VqMAcW+3D;%cqqeeV5Auo26Ci zZ-o|e9nA(r#>a~%KhG%KQ55uvUA)>#hY@oL^9-tGzeoF+wFyΜ?bU&9qP_9GbG! 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 literal 0 HcmV?d00001 diff --git a/tests/media/Irrlicht Software Device 1.0-textureRenderStates.png b/tests/media/Irrlicht Software Device 1.0-textureRenderStates.png new file mode 100644 index 0000000000000000000000000000000000000000..81853f215eec9b2b0022ca625a5be296e23ddb34 GIT binary patch literal 21298 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Μ?bU&9qP_9GbG! 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 literal 0 HcmV?d00001 diff --git a/tests/media/burnings video 0.39b-textureRenderStates.png b/tests/media/burnings video 0.39b-textureRenderStates.png new file mode 100644 index 0000000000000000000000000000000000000000..98282fa1ab04f5f9a038f4c1b9563b53c9c4f4fd GIT binary patch literal 30383 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Μ?bU&9qP_9GbG! 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 literal 0 HcmV?d00001 diff --git a/tests/tests_vc9.vcproj b/tests/tests_vc9.vcproj index 8fa04ccc..98cb6be7 100644 --- a/tests/tests_vc9.vcproj +++ b/tests/tests_vc9.vcproj @@ -59,6 +59,7 @@ /> + + diff --git a/tests/textureRenderStates.cpp b/tests/textureRenderStates.cpp new file mode 100644 index 00000000..79a9374e --- /dev/null +++ b/tests/textureRenderStates.cpp @@ -0,0 +1,61 @@ +// Copyright (C) 2008 Christian Stehno, Colin MacDonald +// No rights reserved: this software is in the public domain. + +#include "irrlicht.h" +#include "testUtils.h" + +using namespace irr; +using namespace core; +using namespace scene; +using namespace video; +using namespace io; +using namespace gui; + +//! Tests interleaved loading and rendering of textures +/** The test loads a texture, renders it using draw2dimage, loads another + texture and renders the first one again. Due to the texture cache this + can lead to rendering of the second texture in second place. */ +static bool runTestWithDriver(E_DRIVER_TYPE driverType) +{ + IrrlichtDevice *device = createDevice( driverType, dimension2d(160, 120), 32); + if (!device) + return true; // Treat a failure to create a driver as benign; this saves a lot of #ifdefs + + IVideoDriver* driver = device->getVideoDriver(); + ISceneManager * smgr = device->getSceneManager(); + + ITexture* tex1 = driver->getTexture("../media/wall.bmp"); + + (void)smgr->addCameraSceneNode(); + + driver->beginScene(true, true, SColor(255,100,101,140)); + driver->draw2DImage(tex1, position2di(0,0)); + driver->endScene(); + + driver->getTexture("../media/tools.png"); + + driver->beginScene(true, true, SColor(255,100,101,140)); + driver->draw2DImage(tex1, position2di(0,0)); + driver->endScene(); + + bool result = takeScreenshotAndCompareAgainstReference(driver, "-textureRenderStates.png", 100); + + device->drop(); + + return result; +} + + +bool textureRenderStates(void) +{ + bool passed = true; + + passed &= runTestWithDriver(EDT_SOFTWARE); + passed &= runTestWithDriver(EDT_BURNINGSVIDEO); + passed &= runTestWithDriver(EDT_DIRECT3D9); + passed &= runTestWithDriver(EDT_DIRECT3D8); + passed &= runTestWithDriver(EDT_OPENGL); + + return passed; +} + diff --git a/tools/GUIEditor/CGUIEditWorkspace.cpp b/tools/GUIEditor/CGUIEditWorkspace.cpp index ede497b4..8e86dd82 100644 --- a/tools/GUIEditor/CGUIEditWorkspace.cpp +++ b/tools/GUIEditor/CGUIEditWorkspace.cpp @@ -860,13 +860,11 @@ void CGUIEditWorkspace::PasteXMLToSelectedElement() // rewind file memWrite->seek(0, false); - io::IXMLReader* xmlReader = (io::IXMLReader*) Environment->getFileSystem()->createXMLReader(memWrite); - // read xml - Environment->readGUIElement(xmlReader, SelectedElement); + Environment->loadGUI(memWrite, SelectedElement); - // drop the xml reader - xmlReader->drop(); + // reset focus + Environment->setFocus(this); // drop the read file memWrite->drop();