From bd17fb2e324db1a6a1fb4c872640b89e28ead5d5 Mon Sep 17 00:00:00 2001 From: XProger Date: Mon, 22 Apr 2019 00:56:14 +0300 Subject: [PATCH] add glTF exporter (debug config, models + first animation) --- src/core.h | 2 +- src/extension.h | 320 +++++++++++++++++++++ src/format.h | 2 +- src/gltf.h | 328 ++++++++++++++++++++++ src/json.h | 135 +++++++++ src/level.h | 8 + src/mesh.h | 3 + src/platform/win/OpenLara.vcxproj | 3 + src/platform/win/OpenLara.vcxproj.filters | 3 + src/utils.h | 60 ++-- 10 files changed, 836 insertions(+), 28 deletions(-) create mode 100644 src/extension.h create mode 100644 src/gltf.h create mode 100644 src/json.h diff --git a/src/core.h b/src/core.h index ebf1182..9e64b10 100644 --- a/src/core.h +++ b/src/core.h @@ -1005,9 +1005,9 @@ namespace Core { void setCullMode(CullMode mode) { renderState &= ~RS_CULL; switch (mode) { - case cmNone : break; case cmBack : renderState |= RS_CULL_BACK; break; case cmFront : renderState |= RS_CULL_FRONT; break; + default : ; } } diff --git a/src/extension.h b/src/extension.h new file mode 100644 index 0000000..98ef48e --- /dev/null +++ b/src/extension.h @@ -0,0 +1,320 @@ +#ifndef _H_EXTENSION +#define _H_EXTENSION + +#include "mesh.h" + +#if defined(_DEBUG) && defined(_OS_WIN) && defined(_GAPI_GL) && !defined(_GAPI_GLES) + #define GEOMETRY_EXPORT + + #include "animation.h" + #include "gltf.h" +#endif + +namespace Extension { + +#ifdef GEOMETRY_EXPORT + void exportTexture(const char *name, Texture *tex) { + char *data32 = new char[tex->width * tex->height * 4]; + + tex->bind(sDiffuse); + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, data32); + + Texture::SaveBMP(name, data32, tex->width, tex->height); + + delete[] data32; + } + + void exportModel(IGame *game, TR::Model &model) { + TR::Level *level = game->getLevel(); + MeshBuilder *mesh = game->getMesh(); + + struct ModelVertex { + vec3 coord; + vec3 normal; + vec2 texCoord; + ubyte4 joints; + ubyte4 weights; + }; + + char name[256]; + sprintf(name, "models/dump/%d.glb", int(model.type)); + + FILE *file = fopen(name, "wb"); + if (!file) { + LOG("dump: can't dump model to file \"%s\"!\n", name); + return; + } + + Index *indices = new Index[64 * 1024]; + Vertex *vertices = new Vertex[64 * 1024]; + + int iCount = 0, vCount = 0; + + int animRate = 1, animFrames = 0; + + Animation *anim = NULL; + if (model.animation != 0xFFFF) { + anim = new Animation(level, &model, true); + anim->setAnim(0); + animRate = max((int)(anim->anims + anim->index)->frameRate, 1); + animFrames = anim->framesCount / animRate; + } + + // get model geometry + MeshBuilder::Geometry geom[3]; + for (int transp = 0; transp < 3; transp++) { + int blendMask = mesh->getBlendMask(transp); + + for (int j = 0; j < model.mCount; j++) { + bool forceOpaque = false; + TR::Entity::fixOpaque(model.type, forceOpaque); + + int index = level->meshOffsets[model.mStart + j]; + if (index || model.mStart + j <= 0) { + TR::Mesh &m = level->meshes[index]; + mesh->buildMesh(geom[transp], blendMask, m, level, indices, vertices, iCount, vCount, 0, j, 0, 0, 0, 0, COLOR_WHITE, false, forceOpaque); + } + } + } + + for (int i = 0; i < iCount; i += 3) { // CCW -> CW + swap(indices[i], indices[i + 2]); + } + + int timelineOffset = 0; + int translationOffset = timelineOffset + animFrames * sizeof(float); + int rotationOffset = translationOffset + animFrames * sizeof(vec3); + int verticesOffset = rotationOffset + model.mCount * animFrames * sizeof(quat); + int indicesOffset = verticesOffset + vCount * sizeof(ModelVertex); + int bufferSize = indicesOffset + iCount * sizeof(Index); + + char *bufferData = new char[bufferSize]; + + vec4 vMin(+INF), vMax(-INF); + + mat4 flip = mat4( + -1, 0, 0, 0, + 0, -1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + ); + mat4 flipInv = flip.inverseOrtho(); + + ModelVertex *gVertices = (ModelVertex*)(bufferData + verticesOffset); + for (int i = 0; i < vCount; i++) { + Vertex &src = vertices[i]; + ModelVertex &dst = gVertices[i]; + + dst.coord = src.coord; + dst.normal = src.normal; + dst.texCoord = src.texCoord; + dst.normal = dst.normal.normal(); + + dst.coord = flip * dst.coord; + dst.normal = flip * dst.normal; + + dst.texCoord *= (1.0f / 32767.0f); + dst.joints = ubyte4(uint8(src.coord.w), 0, 0, 0); + dst.weights = ubyte4(255, 0, 0, 0); + + vMin.x = min(vMin.x, dst.coord.x); + vMin.y = min(vMin.y, dst.coord.y); + vMin.z = min(vMin.z, dst.coord.z); + + vMax.x = max(vMax.x, dst.coord.x); + vMax.y = max(vMax.y, dst.coord.y); + vMax.z = max(vMax.z, dst.coord.z); + } + memcpy(bufferData + indicesOffset, indices, sizeof(Index) * iCount); + + GLTF *gltf = new GLTF(); + + gltf->addSampler(GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, GL_REPEAT, GL_REPEAT); + gltf->addImage("objects.bmp"); + gltf->addTexture("texture", 0, 0); + gltf->addMaterial("material", 0, 0, 1.0f, 0.0f); + + delete[] vertices; + delete[] indices; + + gltf->addAccessor(0, 0, iCount, GLTF::SCALAR, GL_UNSIGNED_SHORT); // 0 + gltf->addAccessor(1, (int)OFFSETOF(ModelVertex, coord), vCount, GLTF::VEC3, GL_FLOAT, false, vMin, vMax); // 1 + gltf->addAccessor(1, (int)OFFSETOF(ModelVertex, normal), vCount, GLTF::VEC3, GL_FLOAT); // 2 + gltf->addAccessor(1, (int)OFFSETOF(ModelVertex, texCoord), vCount, GLTF::VEC2, GL_FLOAT); // 3 + gltf->addAccessor(1, (int)OFFSETOF(ModelVertex, joints), vCount, GLTF::VEC4, GL_UNSIGNED_BYTE); // 4 + gltf->addAccessor(1, (int)OFFSETOF(ModelVertex, weights), vCount, GLTF::VEC4, GL_UNSIGNED_BYTE, true); // 5 + + gltf->addBufferView(0, 0, indicesOffset, sizeof(Index) * iCount); // 0 + gltf->addBufferView(0, sizeof(ModelVertex), verticesOffset, sizeof(ModelVertex) * vCount); // 1 + + sprintf(name, "%d_mesh", int(model.type)); + gltf->addMesh(name, 0, 0, 1, 2, 3, -1, 4, 5); + + JSON *nodes; + gltf->addScene("Scene", &nodes); + + sprintf(name, "%d", int(model.type)); + gltf->addNode(name, 0, 0, vec3(0, 0, 0), quat(0, 0, 0, 1)); + nodes->add(NULL, 0); // mesh + nodes->add(NULL, 1); // skeleton + + int joints[64]; + bool links[32][32]; + memset(links, 0, sizeof(links)); + + if (anim) { + JSON *samplers; + JSON *channels; + gltf->addAnimation("0_anim", &samplers, &channels); + + { // timeline (input) + float *timeline = (float*)(bufferData + timelineOffset); + for (int i = 0; i < animFrames; i++) { + timeline[i] = i * animRate / 30.0f; + } + gltf->addBufferView(0, 0, timelineOffset, animFrames * sizeof(float)); // 2 + gltf->addAccessor(2, 0, animFrames, GLTF::SCALAR, GL_FLOAT, false, vec4(timeline[0], 0, 0, 0), vec4(timeline[animFrames - 1], 0, 0, 1)); // 6 + } + + { // root translation (output) + vec3 *translation = (vec3*)(bufferData + translationOffset); + gltf->addBufferView(0, 0, translationOffset, animFrames * sizeof(vec3)); // 3 + gltf->addAccessor(3, 0, animFrames, GLTF::VEC3, GL_FLOAT); // 7+ + + JSON *sampler = samplers->add(JSON::OBJECT); // 0 + sampler->add("input", 6); // timeline + sampler->add("output", 7); // translations for this node + + JSON *channel = channels->add(JSON::OBJECT); + channel->add("sampler", 0); + JSON *target = channel->add(JSON::OBJECT, "target"); + target->add("node", 1); // 0 - mesh, 1+ - skeleton (1 = root) + target->add("path", "translation"); + + for (int j = 0; j < animFrames; j++) { + TR::AnimFrame *frame = anim->getFrame(anim->anims + anim->index, j); + + translation[j] = frame->pos; + translation[j] = flip * translation[j]; + } + } + + { // rotations (output) + gltf->addBufferView(0, 0, rotationOffset, model.mCount * animFrames * sizeof(quat)); // 4 + + quat *rotation = (quat*)(bufferData + rotationOffset); + quat *ptr = rotation; + for (int i = 0; i < model.mCount; i++) { + for (int j = 0; j < animFrames; j++) { + TR::AnimFrame *frame = anim->getFrame(anim->anims + anim->index, j); + + vec3 angles = frame->getAngle(level->version, i); + + mat4 matrix; + matrix.identity(); + matrix.rotateYXZ(angles); + matrix = flip * matrix * flipInv; + + *ptr = matrix.getRot(); + ptr++; + } + gltf->addAccessor(4, i * animFrames * sizeof(quat), animFrames, GLTF::VEC4, GL_FLOAT); // 8+ + + JSON *sampler = samplers->add(JSON::OBJECT); // 1+ + sampler->add("input", 6); // timeline + sampler->add("output", 8 + i); // rotations for this node + + JSON *channel = channels->add(JSON::OBJECT); + channel->add("sampler", 1 + i); // 0 - translation, 1+ - rotation samplers + JSON *target = channel->add(JSON::OBJECT, "target"); + target->add("node", 1 + i); // 0 - mesh, 1+ - skeleton + target->add("path", "rotation"); + } + } + } + + gltf->addBuffer(bufferData, bufferSize); // 0 + + delete[] bufferData; + + { + int nIndex = 0; + int sIndex = 0; + int stack[16]; + + TR::Node *node = (TR::Node*)&level->nodesData[model.node]; + + for (int i = 0; i < model.mCount; i++) { + if (i > 0) { + links[nIndex][i] = true; + } + + nIndex = i; + if (node[i].flags & 0x01) nIndex = stack[--sIndex]; + if (node[i].flags & 0x02) stack[sIndex++] = nIndex; + } + } + + delete anim; + + vec3 jointPos = vec3(0); + + for (int i = 0; i < model.mCount; i++) { + sprintf(name, "joint_%d", i + 1); + + JSON* node = gltf->addNode(name, -1, -1, jointPos, quat(0, 0, 0, 1)); + JSON* children = new JSON(JSON::ARRAY, "children"); + + for (int j = 0; j < model.mCount; j++) { + if (links[i][j]) { + children->add(NULL, j + 1); + } + } + + if (children->nodes) { + node->add(children); + } else { + delete children; + } + + joints[i] = i + 1; + TR::Node &t = *((TR::Node*)&level->nodesData[model.node] + i); + jointPos = flip * vec3((float)t.x, (float)t.y, (float)t.z); + } + + gltf->addSkin("skin", -1, 1, joints, model.mCount); + + + char *buffer = new char[gltf->getBufferSize()]; + int size = gltf->save(buffer); + delete gltf; + + fwrite(buffer, 1, size, file); + + delete[] buffer; + + LOG("export model: %s\n", name); + + fclose(file); + } + + void exportGeometry(IGame *game, Texture *atlasRooms, Texture *atlasObjects, Texture *atlasSprites) { + CreateDirectory("models", NULL); + CreateDirectory("models/dump/", NULL); + + exportTexture("models/dump/rooms", atlasRooms); + exportTexture("models/dump/objects", atlasObjects); + exportTexture("models/dump/sprites", atlasSprites); + + TR::Level *level = game->getLevel(); + MeshBuilder *mesh = game->getMesh(); + + for (int i = 0; i < level->modelsCount; i++) { + exportModel(game, level->models[i]); + } + } +#endif + +} + +#endif diff --git a/src/format.h b/src/format.h index 3deb182..04dc506 100644 --- a/src/format.h +++ b/src/format.h @@ -5011,7 +5011,7 @@ namespace TR { n = mesh.vertices[face.vertices[fn]].normal;\ continue;\ }\ - vec3 o(mesh.vertices[face.vertices[0]].coord);\ + vec3 o = mesh.vertices[face.vertices[0]].coord;\ vec3 a = o - mesh.vertices[face.vertices[1]].coord;\ vec3 b = o - mesh.vertices[face.vertices[2]].coord;\ o = b.cross(a).normal() * 16300.0f;\ diff --git a/src/gltf.h b/src/gltf.h new file mode 100644 index 0000000..6430cca --- /dev/null +++ b/src/gltf.h @@ -0,0 +1,328 @@ +#ifndef _H_GLTF +#define _H_GLTF + +#include "json.h" + +#define ACCESSOR_TYPES(E) \ + E( SCALAR ) \ + E( VEC2 ) \ + E( VEC3 ) \ + E( VEC4 ) \ + E( MAT2 ) \ + E( MAT3 ) \ + E( MAT4 ) + +struct GLTF { + enum AccessorType { ACCESSOR_TYPES(DECL_ENUM) ACCESSOR_TYPE_MAX }; + + JSON *root; + JSON *asset; + JSON *buffers; + JSON *bufferViews; + JSON *accessors; + JSON *meshes; + JSON *images; + JSON *samplers; + JSON *textures; + JSON *materials; + JSON *nodes; + JSON *skins; + JSON *animations; + JSON *scenes; + + char *binaryData; + int binarySize; + + GLTF() : binaryData(NULL), binarySize(0) { + root = new JSON(JSON::OBJECT); + + asset = root->add(JSON::OBJECT, "asset"); + images = root->add(JSON::ARRAY, "images"); + samplers = root->add(JSON::ARRAY, "samplers"); + textures = root->add(JSON::ARRAY, "textures"); + materials = root->add(JSON::ARRAY, "materials"); + buffers = root->add(JSON::ARRAY, "buffers"); + bufferViews = root->add(JSON::ARRAY, "bufferViews"); + accessors = root->add(JSON::ARRAY, "accessors"); + meshes = root->add(JSON::ARRAY, "meshes"); + nodes = root->add(JSON::ARRAY, "nodes"); + skins = root->add(JSON::ARRAY, "skins"); + animations = root->add(JSON::ARRAY, "animations"); + scenes = root->add(JSON::ARRAY, "scenes"); + + asset->add("generator", "OpenLara"); + asset->add("version", "2.0"); + + root->add("scene", 0); + } + + ~GLTF() { + delete[] binaryData; + delete root; + } + + int getBufferSize() { + return 8 * 1024 * 1024 + binarySize; + } + + int save(char *buffer) { + struct Header { + uint32 magic; + uint32 version; + uint32 length; + }; + + struct Chunk { + uint32 length; + uint32 type; + }; + + char *start = buffer; + + Header *header = (Header*)buffer; + buffer += sizeof(Header); + + Chunk *chunk = (Chunk*)buffer; + buffer += sizeof(Chunk); + + root->save(buffer); + + chunk->length = strlen(buffer); + chunk->type = FOURCC("JSON"); + + buffer += chunk->length; + + while (( int(buffer - start) % 4) != 0) { + *buffer++ = ' '; + chunk->length++; + } + + if (binaryData) { + Chunk *chunk = (Chunk*)buffer; + buffer += sizeof(Chunk); + chunk->length = binarySize; + chunk->type = FOURCC("BIN\0"); + + memcpy(buffer, binaryData, binarySize); + + buffer += binarySize; + + while (( (buffer - start) % 4) != 0) { + *buffer++ = 0; + chunk->length++; + } + } + + header->magic = FOURCC("glTF"); + header->version = 2; + header->length = buffer - start; + + return header->length; + } + + JSON* addAccessor(int bufferView, int byteOffset, int count, AccessorType type, int format, bool normalized = false, const vec4 &vMin = vec4(0.0f), const vec4 &vMax = vec4(0.0f)) { + static const char *AccessorTypeName[ACCESSOR_TYPE_MAX] = { ACCESSOR_TYPES(DECL_STR) }; + + JSON *item = accessors->add(JSON::OBJECT); + + item->add("bufferView", bufferView); + + if (byteOffset) { + item->add("byteOffset", byteOffset); + } + + item->add("count", count); + item->add("type", AccessorTypeName[type]); + item->add("componentType", format); + + if (normalized) { + item->add("normalized", true); + } + + if (vMin != vMax || vMin != vec4(0.0f)) { + JSON *itemMin = item->add(JSON::ARRAY, "min"); + JSON *itemMax = item->add(JSON::ARRAY, "max"); + + int k; + switch (type) { + case SCALAR : k = 1; break; + case VEC2 : k = 2; break; + case VEC3 : k = 3; break; + case VEC4 : k = 4; break; + default : k = 0; + } + + for (int i = 0; i < k; i++) { + itemMin->add(NULL, vMin[i]); + itemMax->add(NULL, vMax[i]); + } + } + + return item; + } + + JSON* addBuffer(void *data, int size) { + JSON *item = buffers->add(JSON::OBJECT); + + item->add("byteLength", size); + + binarySize = size; + binaryData = new char[size]; + memcpy(binaryData, data, size); + + return item; + } + + JSON* addBufferView(int buffer, int byteStride, int byteOffset, int byteLength) { + JSON *item = bufferViews->add(JSON::OBJECT); + + item->add("buffer", buffer); + if (byteStride) { + item->add("byteStride", byteStride); + } + if (byteOffset) { + item->add("byteOffset", byteOffset); + } + item->add("byteLength", byteLength); + + return item; + } + + JSON* addMesh(const char *name, int material, int INDICES, int POSITION = -1, int NORMAL = -1, int TEXCOORD = -1, int COLOR = -1, int JOINTS = -1, int WEIGHTS = -1) { + JSON *item = meshes->add(JSON::OBJECT); + + if (name) item->add("name", name); + JSON *primitives = item->add(JSON::ARRAY, "primitives"); + JSON *part = primitives->add(JSON::OBJECT); + JSON *attributes = part->add(JSON::OBJECT, "attributes"); + if (POSITION >= 0) attributes->add("POSITION", POSITION); + if (NORMAL >= 0) attributes->add("NORMAL", NORMAL); + if (TEXCOORD >= 0) attributes->add("TEXCOORD_0", TEXCOORD); + if (COLOR >= 0) attributes->add("COLOR_0", COLOR); + if (JOINTS >= 0) attributes->add("JOINTS_0", JOINTS); + if (WEIGHTS >= 0) attributes->add("WEIGHTS_0", WEIGHTS); + part->add("indices", INDICES); + part->add("material", material); + + return item; + } + + JSON* addImage(const char *uri) { + JSON *item = images->add(JSON::OBJECT); + + item->add("uri", uri); + + return item; + } + + JSON* addSampler(int magFilter, int minFilter, int wrapS, int wrapT) { + JSON *item = samplers->add(JSON::OBJECT); + + item->add("magFilter", magFilter); + item->add("minFilter", minFilter); + item->add("wrapS", wrapS); + item->add("wrapT", wrapT); + + return item; + } + + JSON* addTexture(const char *name, int sampler, int source) { + JSON *item = textures->add(JSON::OBJECT); + + if (name) item->add("name", name); + item->add("sampler", sampler); + item->add("source", source); + + return item; + } + + JSON* addMaterial(const char *name, int baseColorTextureIndex, int baseColorTextureTexCoord, float roughnessFactor, float metallicFactor) { + JSON *item = materials->add(JSON::OBJECT); + + if (name) item->add("name", name); + + item->add("alphaMode", "MASK"); + + JSON *pbr = item->add(JSON::OBJECT, "pbrMetallicRoughness"); + pbr->add("roughnessFactor", roughnessFactor); + pbr->add("metallicFactor", metallicFactor); + + JSON *baseTex = pbr->add(JSON::OBJECT, "baseColorTexture"); + baseTex->add("index", baseColorTextureIndex); + baseTex->add("texCoord", baseColorTextureTexCoord); + + return item; + } + + JSON* addNode(const char *name, int mesh, int skin, const vec3 &translation, const quat &rotation) { + JSON *item = nodes->add(JSON::OBJECT); + + if (name) item->add("name", name); + if (mesh >= 0) item->add("mesh", mesh); + if (skin >= 0) item->add("skin", skin); + + if (translation != vec3(0.0f)) { + JSON *v = item->add(JSON::ARRAY, "translation"); + v->add(NULL, translation.x); + v->add(NULL, translation.y); + v->add(NULL, translation.z); + } + + if (rotation != quat(0.0f, 0.0f, 0.0f, 1.0f)) { + JSON *v = item->add(JSON::ARRAY, "rotation"); + v->add(NULL, rotation.x); + v->add(NULL, rotation.y); + v->add(NULL, rotation.z); + v->add(NULL, rotation.w); + } + + return item; + } + + JSON *addSkin(const char *name, int inverseBindMatrices, int skeleton, int *joints, int jointsCount) { + JSON *item = skins->add(JSON::OBJECT); + + if (name) { + item->add("name", name); + } + + if (inverseBindMatrices >= 0) { + item->add("inverseBindMatrices", inverseBindMatrices); + } + + item->add("skeleton", skeleton); + + JSON *v = item->add(JSON::ARRAY, "joints"); + for (int i = 0; i < jointsCount; i++) { + v->add(NULL, joints[i]); + } + + return item; + } + + JSON* addAnimation(const char *name, JSON **samplers, JSON **channels) { + JSON *item = animations->add(JSON::OBJECT); + + if (name) item->add("name", name); + if (samplers) *samplers = item->add(JSON::ARRAY, "samplers"); + if (channels) *channels = item->add(JSON::ARRAY, "channels"); + + return item; + } + + + JSON* addScene(const char *name, JSON **nodes) { + JSON *item = scenes->add(JSON::OBJECT); + + item->add("name", name); + if (nodes) { + *nodes = item->add(JSON::ARRAY, "nodes"); + } + + return item; + } +}; + +#undef ACCESSOR_TYPES + +#endif diff --git a/src/json.h b/src/json.h new file mode 100644 index 0000000..a433e3f --- /dev/null +++ b/src/json.h @@ -0,0 +1,135 @@ +#ifndef _H_JSON +#define _H_JSON + +#include "utils.h" + +struct JSON { + + enum Type { EMPTY, OBJECT, ARRAY, STRING, NUMBER, FLOAT, BOOL }; + + JSON *prev; + JSON *next; + + char *name; + + union { + JSON *nodes; + char *sValue; + int iValue; + float fValue; + bool bValue; + }; + + Type type; + + JSON(Type type, const char *name = NULL) : nodes(NULL), prev(NULL), next(NULL), type(type) { + this->name = String::copy(name); + } + + ~JSON() { + switch (type) { + case OBJECT : + case ARRAY : { + JSON *node = nodes; + while (node) { + JSON *next = node->next; + delete node; + node = next; + } + break; + } + case STRING : { + delete[] sValue; + break; + } + default : ; + } + + delete[] name; + } + + JSON* add(Type type, const char *name = NULL) { + ASSERT(this->type != ARRAY || (name == NULL && (!nodes || nodes->type == type))); + return add(new JSON(type, name)); + } + + JSON* add(JSON *node) { + node->prev = NULL; + node->next = nodes; + if (nodes) { + nodes->prev = node; + } + nodes = node; + + return node; + } + + + void add(const char *name, const char *value) { + add(STRING, name)->sValue = String::copy(value); + } + + void add(const char *name, int value) { + add(NUMBER, name)->iValue = value; + } + + void add(const char *name, float value) { + add(FLOAT, name)->fValue = value; + } + + void add(const char *name, bool value) { + add(BOOL, name)->bValue = value; + } + + void save(char *buffer) { + *buffer = 0; + + if (name) { + strcat(buffer, "\""); + strcat(buffer, name); + strcat(buffer, "\":"); + } + + if (type == EMPTY) { + strcat(buffer, "null"); + } else if (type == OBJECT || type == ARRAY) { + bool isObject = (type == OBJECT); + strcat(buffer, isObject ? "{" : "["); + + JSON *node = nodes; + while (node && node->next) { + node = node->next; + } + while (node) { + node->save(buffer + strlen(buffer)); + node = node->prev; + if (node) { + strcat(buffer, ","); + } + } + strcat(buffer, isObject ? "}" : "]"); + } else if (type == STRING) { + strcat(buffer, "\""); + if (sValue) { + strcat(buffer, sValue); + } + strcat(buffer, "\""); + } else if (type == NUMBER) { + char buf[64]; + _itoa(iValue, buf, 10); + strcat(buffer, buf); + } else if (type == FLOAT) { + char buf[64]; + sprintf(buf, "%.9g", fValue); + strcat(buffer, buf); + + //_gcvt(fValue, 8, buf); + //strcat(buffer, buf); + //strcat(buffer, "0"); + } else if (type == BOOL) { + strcat(buffer, bValue ? "true" : "false"); + } + } +}; + +#endif diff --git a/src/level.h b/src/level.h index 7034d4a..fcdaef2 100644 --- a/src/level.h +++ b/src/level.h @@ -12,6 +12,7 @@ #include "inventory.h" #include "savegame.h" #include "network.h" +#include "extension.h" #if defined(_DEBUG) && defined(_GAPI_GL) && !defined(_GAPI_GLES) #define DEBUG_RENDER @@ -2135,6 +2136,13 @@ struct Level : IGame { } #endif #endif + + #ifdef GEOMETRY_EXPORT + if (Input::down[ikF1]) { + Extension::exportGeometry(this, atlasRooms, atlasObjects, atlasSprites); + Input::down[ikF1] = false; + } + #endif } void updateEffect() { diff --git a/src/mesh.h b/src/mesh.h index c247154..5eabba5 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -988,6 +988,9 @@ struct MeshBuilder { for (int j = 0; j < mesh.fCount; j++) { TR::Face &f = mesh.faces[j]; ASSERT(f.colored || f.flags.texture < level->objectTexturesCount); + if (level->version & TR::VER_PSX) { + f.colored = false; // PSX version has colored textures + } TR::TextureInfo &t = f.colored ? (useRoomTex ? whiteRoom : whiteObject) : level->objectTextures[f.flags.texture]; int texAttrib = forceOpaque ? 0 : t.attribute; diff --git a/src/platform/win/OpenLara.vcxproj b/src/platform/win/OpenLara.vcxproj index 386c98e..a8410ab 100644 --- a/src/platform/win/OpenLara.vcxproj +++ b/src/platform/win/OpenLara.vcxproj @@ -199,6 +199,7 @@ + @@ -212,6 +213,8 @@ + + diff --git a/src/platform/win/OpenLara.vcxproj.filters b/src/platform/win/OpenLara.vcxproj.filters index d179386..de46a49 100644 --- a/src/platform/win/OpenLara.vcxproj.filters +++ b/src/platform/win/OpenLara.vcxproj.filters @@ -116,6 +116,9 @@ lang + + + diff --git a/src/utils.h b/src/utils.h index bfc37d4..daae02c 100644 --- a/src/utils.h +++ b/src/utils.h @@ -483,6 +483,8 @@ struct vec4 { vec4(const vec3 &xyz, float w) : x(xyz.x), y(xyz.y), z(xyz.z), w(w) {} vec4(const vec2 &xy, const vec2 &zw) : x(xy.x), y(xy.y), z(zw.x), w(zw.y) {} + inline float& operator [] (int index) const { ASSERT(index >= 0 && index <= 3); return ((float*)this)[index]; } + inline bool operator == (const vec4 &v) const { return x == v.x && y == v.y && z == v.z && w == v.w; } inline bool operator != (const vec4 &v) const { return !(*this == v); } @@ -515,6 +517,9 @@ struct quat { w = c; } + inline bool operator == (const quat &q) const { return x == q.x && y == q.y && z == q.z && w == q.w; } + inline bool operator != (const quat &v) const { return !(*this == v); } + quat operator - () const { return quat(-x, -y, -z, -w); } @@ -1065,7 +1070,11 @@ struct short4 { short4() {} short4(int16 x, int16 y, int16 z, int16 w) : x(x), y(y), z(z), w(w) {} + operator vec2() const { return vec2((float)x, (float)y); }; operator vec3() const { return vec3((float)x, (float)y, (float)z); }; + operator vec4() const { return vec4((float)x, (float)y, (float)z, (float)w); }; + + operator short2() const { return *((short2*)this); } operator short3() const { return *((short3*)this); } inline bool operator == (const short4 &v) const { return x == v.x && y == v.y && z == v.z && w == v.w; } @@ -1386,6 +1395,28 @@ union Color16 { // RGBA5551 }; +namespace String { + + void toLower(char *str) { + if (!str) return; + + while (char &c = *str++) { + if (c >= 'A' && c <= 'Z') + c -= 'Z' - 'z'; + } + } + + char* copy(const char *str) { + if (str == NULL) { + return NULL; + } + char *res = new char[strlen(str) + 1]; + strcpy(res, str); + return res; + } +} + + struct Stream; extern void osCacheWrite (Stream *stream); @@ -1418,10 +1449,7 @@ struct Stream { int bufferIndex; Stream(const char *name, const void *data, int size, Callback *callback = NULL, void *userData = NULL) : callback(callback), userData(userData), f(NULL), data((char*)data), name(NULL), size(size), pos(0), buffer(NULL) { - if (name) { - this->name = new char[strlen(name) + 1]; - strcpy(this->name, name); - } + this->name = String::copy(name); } Stream(const char *name, Callback *callback = NULL, void *userData = NULL) : callback(callback), userData(userData), f(NULL), data(NULL), name(NULL), size(-1), pos(0), buffer(NULL) { @@ -1446,10 +1474,7 @@ struct Stream { if (!f) { #ifdef _OS_WEB - if (name) { - this->name = new char[strlen(name) + 1]; - strcpy(this->name, name); - } + this->name = String::copy(name); osDownload(this); #else LOG("error loading file \"%s\"\n", name); @@ -1468,10 +1493,7 @@ struct Stream { bufferIndex = -1; - if (name) { - this->name = new char[strlen(name) + 1]; - strcpy(this->name, name); - } + this->name = String::copy(name); if (callback) callback(this, userData); @@ -1907,20 +1929,6 @@ struct BitStream { }; -namespace String { - - void toLower(char *str) { - if (!str) return; - - while (char &c = *str++) { - if (c >= 'A' && c <= 'Z') - c -= 'Z' - 'z'; - } - } - -} - - template struct FixedStr { char data[N];