1
0
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:
XProger 2019-04-22 00:56:14 +03:00
parent 4c1c742d34
commit bd17fb2e32
10 changed files with 836 additions and 28 deletions

View File

@ -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
View 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

View File

@ -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
View 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
View 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

View File

@ -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() {

View File

@ -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;

View File

@ -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" />

View File

@ -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">

View File

@ -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];