mirror of
https://github.com/XProger/OpenLara.git
synced 2025-02-23 23:14:47 +01:00
add glTF exporter (debug config, models + first animation)
This commit is contained in:
parent
4c1c742d34
commit
bd17fb2e32
@ -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 : ;
|
||||
}
|
||||
}
|
||||
|
||||
|
320
src/extension.h
Normal file
320
src/extension.h
Normal file
@ -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
|
@ -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;\
|
||||
|
328
src/gltf.h
Normal file
328
src/gltf.h
Normal file
@ -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
|
135
src/json.h
Normal file
135
src/json.h
Normal file
@ -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
|
@ -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() {
|
||||
|
@ -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;
|
||||
|
@ -199,6 +199,7 @@
|
||||
<ClInclude Include="..\..\collision.h" />
|
||||
<ClInclude Include="..\..\controller.h" />
|
||||
<ClInclude Include="..\..\core.h" />
|
||||
<ClInclude Include="..\..\extension.h" />
|
||||
<ClInclude Include="..\..\format.h" />
|
||||
<ClInclude Include="..\..\debug.h" />
|
||||
<ClInclude Include="..\..\enemy.h" />
|
||||
@ -212,6 +213,8 @@
|
||||
<ClInclude Include="..\..\gapi\gu.h" />
|
||||
<ClInclude Include="..\..\gapi\gxm.h" />
|
||||
<ClInclude Include="..\..\gapi\vk.h" />
|
||||
<ClInclude Include="..\..\gltf.h" />
|
||||
<ClInclude Include="..\..\json.h" />
|
||||
<ClInclude Include="..\..\lang.h" />
|
||||
<ClInclude Include="..\..\lang\de.h" />
|
||||
<ClInclude Include="..\..\lang\en.h" />
|
||||
|
@ -116,6 +116,9 @@
|
||||
<ClInclude Include="..\..\lang\fi.h">
|
||||
<Filter>lang</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\extension.h" />
|
||||
<ClInclude Include="..\..\gltf.h" />
|
||||
<ClInclude Include="..\..\json.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\shaders\filter.glsl">
|
||||
|
60
src/utils.h
60
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 <int N>
|
||||
struct FixedStr {
|
||||
char data[N];
|
||||
|
Loading…
x
Reference in New Issue
Block a user