/* Copyright (C) 2022 kichikuou * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include "system4.h" #include "system4/buffer.h" #include "3d_internal.h" #define METERS_PER_INCH 0.0254 static char *read_cstring(struct buffer *r) { char *s = strdup(buffer_strdata(r)); buffer_skip(r, strlen(s) + 1); return s; } static void read_position(struct buffer *r, vec3 v) { // left->right handed system v[0] = buffer_read_float(r) * METERS_PER_INCH; v[1] = buffer_read_float(r) * METERS_PER_INCH; v[2] = -buffer_read_float(r) * METERS_PER_INCH; } static void read_direction(struct buffer *r, vec3 v) { // left->right handed system v[0] = buffer_read_float(r); v[1] = buffer_read_float(r); v[2] = -buffer_read_float(r); } static void read_quaternion(struct buffer *r, versor q) { // left->right handed system float w = buffer_read_float(r); float x = -buffer_read_float(r); float y = -buffer_read_float(r); float z = buffer_read_float(r); glm_quat_init(q, x, y, z, w); glm_quat_normalize(q); } static uint32_t parse_material_attributes(const char *name) { uint32_t flags = 0; if (strstr(name, "(sprite)")) flags |= MATERIAL_SPRITE; if (strstr(name, "(alpha)")) flags |= MATERIAL_ALPHA; return flags; } static void parse_textures(struct buffer *r, int pol_version, struct pol_material *m) { glm_vec2_one(m->uv_tiling); int nr_textures = buffer_read_int32(r); for (int i = 0; i < nr_textures; i++) { char *filename = read_cstring(r); int type = buffer_read_int32(r); vec2 uv_tiling = { 1.0f, 1.0f }; if (pol_version >= 3) { uv_tiling[0] = buffer_read_float(r); uv_tiling[1] = buffer_read_float(r); } if ((unsigned)type < MAX_TEXTURE_TYPE) { m->textures[type] = filename; if (type == COLOR_MAP) glm_vec2_copy(uv_tiling, m->uv_tiling); } else { WARNING("invalid texture type %d", type); free(filename); } } } static void destroy_material(struct pol_material *m) { free(m->name); for (int i = 0; i < MAX_TEXTURE_TYPE; i++) free(m->textures[i]); } static void destroy_material_group(struct pol_material_group *mg) { destroy_material(&mg->m); if (mg->children) { for (uint32_t i = 0; i < mg->nr_children; i++) destroy_material_group(&mg->children[i]); free(mg->children); } } // Material tree layout: // level 0: top-level material per pol->materials[]. Either a textured leaf // or a group containing sub-materials, never both. // level 1: sub-material. Either a textured leaf or, in v4 only, a "blend // group" (is_group=1) with exactly 2 leaf children. // level 2: leaf children of a v4 blend group; cannot themselves be a group. static void parse_material_group(struct buffer *r, int pol_version, struct pol_material_group *mg, int level) { mg->m.name = read_cstring(r); mg->m.flags = parse_material_attributes(mg->m.name); bool is_group = false; if (pol_version >= 4) is_group = buffer_read_int32(r); if (is_group) { if (level >= 2) ERROR("material group nesting too deep"); uint32_t uk = buffer_read_int32(r); if (uk != 0) ERROR("unexpected nonzero value in material group: %u", uk); mg->nr_children = buffer_read_int32(r); if (level == 1 && mg->nr_children != 2) ERROR("blend group must have 2 children, got %u", mg->nr_children); } else { parse_textures(r, pol_version, &mg->m); if (pol_version >= 4) { uint32_t uk = buffer_read_int32(r); if (uk != 0) ERROR("unexpected nonzero value in material leaf: %u", uk); } else if (level == 0) { mg->nr_children = buffer_read_int32(r); } } if (mg->nr_children > 0) { mg->children = xcalloc(mg->nr_children, sizeof(struct pol_material_group)); for (uint32_t i = 0; i < mg->nr_children; i++) parse_material_group(r, pol_version, &mg->children[i], level + 1); } } static void parse_vertex(struct buffer *r, int pol_version, struct pol_vertex *v) { read_position(r, v->pos); v->nr_weights = pol_version == 1 ? buffer_read_int32(r) : buffer_read_u16(r); if (v->nr_weights) { v->weights = xcalloc(v->nr_weights, sizeof(struct pol_bone_weight)); for (uint32_t i = 0; i < v->nr_weights; i++) { v->weights[i].bone = pol_version == 1 ? buffer_read_int32(r) : buffer_read_u16(r); v->weights[i].weight = buffer_read_float(r); } } } static void destroy_vertex(struct pol_vertex *v) { if (v->weights) free(v->weights); } static void parse_triangle(struct buffer *r, struct pol_mesh *mesh, int triangle_index, const struct pol_material_group *mg) { struct pol_triangle *t = &mesh->triangles[triangle_index]; t->vert_index[0] = buffer_read_int32(r); t->vert_index[1] = buffer_read_int32(r); t->vert_index[2] = buffer_read_int32(r); t->uv_index[0] = buffer_read_int32(r); t->uv_index[1] = buffer_read_int32(r); t->uv_index[2] = buffer_read_int32(r); if (mesh->light_uvs) { t->light_uv_index[0] = buffer_read_int32(r) - mesh->nr_uvs; t->light_uv_index[1] = buffer_read_int32(r) - mesh->nr_uvs; t->light_uv_index[2] = buffer_read_int32(r) - mesh->nr_uvs; } if (mesh->blend_uvs) { t->blend_uv_index[0] = buffer_read_int32(r) - mesh->nr_uvs - mesh->nr_light_uvs; t->blend_uv_index[1] = buffer_read_int32(r) - mesh->nr_uvs - mesh->nr_light_uvs; t->blend_uv_index[2] = buffer_read_int32(r) - mesh->nr_uvs - mesh->nr_light_uvs; } t->color_index[0] = buffer_read_int32(r); t->color_index[1] = buffer_read_int32(r); t->color_index[2] = buffer_read_int32(r); if (mesh->alphas) { t->alpha_index[0] = buffer_read_int32(r); t->alpha_index[1] = buffer_read_int32(r); t->alpha_index[2] = buffer_read_int32(r); } if (mesh->blend_weights) { t->blend_weight_index[0] = buffer_read_int32(r); t->blend_weight_index[1] = buffer_read_int32(r); t->blend_weight_index[2] = buffer_read_int32(r); } read_direction(r, t->normals[0]); read_direction(r, t->normals[1]); read_direction(r, t->normals[2]); t->material_group_index = buffer_read_int32(r); if (t->material_group_index >= mg->nr_children) t->material_group_index = 0; } static uint32_t parse_mesh_attributes(const char *name) { uint32_t flags = 0; if (strstr(name, "(nolighting)")) flags |= MESH_NOLIGHTING; if (strstr(name, "(nomakeshadow)")) flags |= MESH_NOMAKESHADOW; if (strstr(name, "(env)")) flags |= MESH_ENVMAP; if (strstr(name, "(both)")) flags |= MESH_BOTH; if (strstr(name, "(sprite)")) flags |= MESH_SPRITE; if (strstr(name, "(alpha)")) flags |= MESH_ALPHA; return flags; } static struct pol_mesh *parse_mesh(struct buffer *r, const struct pol *pol) { int type = buffer_read_int32(r); if (type != 0) { if (type != -1) WARNING("unknown mesh type: %d", type); return NULL; } struct pol_mesh *mesh = xcalloc(1, sizeof(struct pol_mesh)); mesh->name = read_cstring(r); mesh->flags = parse_mesh_attributes(mesh->name); mesh->material = buffer_read_int32(r); if (mesh->material >= pol->nr_materials) { WARNING("material index out of range (%d >= %d)", mesh->material, pol->nr_materials); mesh->material = 0; } mesh->nr_vertices = buffer_read_int32(r); mesh->vertices = xcalloc(mesh->nr_vertices, sizeof(struct pol_vertex)); for (uint32_t i = 0; i < mesh->nr_vertices; i++) { parse_vertex(r, pol->version, &mesh->vertices[i]); } mesh->nr_uvs = buffer_read_int32(r); mesh->uvs = xcalloc(mesh->nr_uvs, sizeof(vec2)); for (uint32_t i = 0; i < mesh->nr_uvs; i++) { mesh->uvs[i][0] = buffer_read_float(r); mesh->uvs[i][1] = buffer_read_float(r); } mesh->nr_light_uvs = buffer_read_int32(r); if (mesh->nr_light_uvs > 0) { mesh->flags |= MESH_HAS_LIGHT_UV; mesh->light_uvs = xcalloc(mesh->nr_light_uvs, sizeof(vec2)); for (uint32_t i = 0; i < mesh->nr_light_uvs; i++) { mesh->light_uvs[i][0] = buffer_read_float(r); mesh->light_uvs[i][1] = buffer_read_float(r); } } if (pol->version >= 4) { mesh->nr_blend_uvs = buffer_read_int32(r); if (mesh->nr_blend_uvs > 0) { mesh->blend_uvs = xcalloc(mesh->nr_blend_uvs, sizeof(vec2)); for (uint32_t i = 0; i < mesh->nr_blend_uvs; i++) { mesh->blend_uvs[i][0] = buffer_read_float(r); mesh->blend_uvs[i][1] = buffer_read_float(r); } } } mesh->nr_colors = buffer_read_int32(r); if (mesh->nr_colors > 0) { mesh->colors = xcalloc(mesh->nr_colors, sizeof(vec3)); for (uint32_t i = 0; i < mesh->nr_colors; i++) { if (pol->version == 1) { mesh->colors[i][0] = buffer_read_float(r); mesh->colors[i][1] = buffer_read_float(r); mesh->colors[i][2] = buffer_read_float(r); } else { mesh->colors[i][0] = buffer_read_u8(r) / 255.f; mesh->colors[i][1] = buffer_read_u8(r) / 255.f; mesh->colors[i][2] = buffer_read_u8(r) / 255.f; buffer_skip(r, 1); } } } if (pol->version >= 2) { mesh->nr_alphas = buffer_read_int32(r); if (mesh->nr_alphas > 0) { mesh->alphas = xcalloc(mesh->nr_alphas, sizeof(float)); for (uint32_t i = 0; i < mesh->nr_alphas; i++) { mesh->alphas[i] = buffer_read_u8(r) / 255.f; } } } if (pol->version >= 4) { mesh->nr_blend_weights = buffer_read_int32(r); if (mesh->nr_blend_weights > 0) { mesh->blend_weights = xcalloc(mesh->nr_blend_weights, sizeof(float)); for (uint32_t i = 0; i < mesh->nr_blend_weights; i++) { mesh->blend_weights[i] = buffer_read_u8(r) / 255.f; } } } mesh->nr_triangles = buffer_read_int32(r); mesh->triangles = xcalloc(mesh->nr_triangles, sizeof(struct pol_triangle)); for (uint32_t i = 0; i < mesh->nr_triangles; i++) { parse_triangle(r, mesh, i, &pol->materials[mesh->material]); } if (pol->version == 1) { if (buffer_read_int32(r) != 1) WARNING("unexpected mesh footer"); if (buffer_read_int32(r) != 0) WARNING("unexpected mesh footer"); } return mesh; } static void free_mesh(struct pol_mesh *mesh) { if (!mesh) return; free(mesh->name); for (uint32_t i = 0; i < mesh->nr_vertices; i++) destroy_vertex(&mesh->vertices[i]); free(mesh->vertices); free(mesh->uvs); free(mesh->light_uvs); free(mesh->blend_uvs); free(mesh->colors); free(mesh->alphas); free(mesh->blend_weights); free(mesh->triangles); free(mesh); } static void parse_bone(struct buffer *r, struct pol_bone *bone) { bone->name = read_cstring(r); bone->id = buffer_read_int32(r); bone->parent = buffer_read_int32(r); read_position(r, bone->pos); read_quaternion(r, bone->rotq); } static void destroy_bone(struct pol_bone *bone) { free(bone->name); } struct pol *pol_parse(uint8_t *data, size_t size) { struct buffer r; buffer_init(&r, data, size); if (memcmp(buffer_strdata(&r), "POL\0", 4)) return NULL; buffer_skip(&r, 4); struct pol *pol = xcalloc(1, sizeof(struct pol)); pol->version = buffer_read_int32(&r); if (pol->version != 1 && pol->version != 2 && pol->version != 4) { WARNING("unknown POL version: %d", pol->version); free(pol); return NULL; } pol->nr_materials = buffer_read_int32(&r); pol->materials = xcalloc(pol->nr_materials, sizeof(struct pol_material_group)); for (uint32_t i = 0; i < pol->nr_materials; i++) { parse_material_group(&r, pol->version, &pol->materials[i], 0); } pol->nr_meshes = buffer_read_int32(&r); pol->meshes = xcalloc(pol->nr_meshes, sizeof(struct pol_mesh *)); for (uint32_t i = 0; i < pol->nr_meshes; i++) { pol->meshes[i] = parse_mesh(&r, pol); } pol->nr_bones = buffer_read_int32(&r); if (pol->nr_bones) { pol->bones = xcalloc(pol->nr_bones, sizeof(struct pol_bone)); for (uint32_t i = 0; i < pol->nr_bones; i++) { parse_bone(&r, &pol->bones[i]); } } if (buffer_remaining(&r) != 0) { WARNING("extra data at end"); } return pol; } void pol_free(struct pol *pol) { for (uint32_t i = 0; i < pol->nr_materials; i++) destroy_material_group(&pol->materials[i]); free(pol->materials); for (uint32_t i = 0; i < pol->nr_meshes; i++) free_mesh(pol->meshes[i]); free(pol->meshes); for (uint32_t i = 0; i < pol->nr_bones; i++) destroy_bone(&pol->bones[i]); free(pol->bones); free(pol); } void pol_compute_aabb(struct pol *pol, vec3 dest[2]) { vec3 aabb[2]; glm_aabb_invalidate(aabb); for (uint32_t i = 0; i < pol->nr_meshes; i++) { struct pol_mesh *mesh = pol->meshes[i]; if (!mesh) continue; for (uint32_t j = 0; j < mesh->nr_vertices; j++) { glm_vec3_minv(mesh->vertices[j].pos, aabb[0], aabb[0]); glm_vec3_maxv(mesh->vertices[j].pos, aabb[1], aabb[1]); } } glm_vec3_copy(aabb[0], dest[0]); glm_vec3_copy(aabb[1], dest[1]); } struct pol_bone *pol_find_bone(struct pol *pol, uint32_t id) { for (uint32_t i = 0; i < pol->nr_bones; i++) { if (pol->bones[i].id == id) return &pol->bones[i]; } return NULL; } struct mot *mot_parse(uint8_t *data, size_t size, const char *name) { struct buffer r; buffer_init(&r, data, size); if (memcmp(buffer_strdata(&r), "MOT\0", 4)) return NULL; buffer_skip(&r, 4); uint32_t version = buffer_read_int32(&r); if (version != 0) { WARNING("unknown MOT version: %d", version); return NULL; } uint32_t nr_frames = buffer_read_int32(&r); uint32_t nr_bones = buffer_read_int32(&r); struct mot *mot = xcalloc(1, sizeof(struct mot) + nr_bones * sizeof(struct mot_bone *)); mot->name = strdup(name); mot->nr_frames = nr_frames; mot->nr_bones = nr_bones; for (uint32_t i = 0; i < nr_bones; i++) { struct mot_bone *m = xcalloc(1, sizeof(struct mot_bone) + sizeof(struct mot_frame) * nr_frames); m->name = read_cstring(&r); m->id = buffer_read_int32(&r); m->parent = buffer_read_int32(&r); for (uint32_t j = 0; j < nr_frames; j++) { read_position(&r, m->frames[j].pos); read_quaternion(&r, m->frames[j].rotq); buffer_skip(&r, 16); // another quaternion? } mot->motions[i] = m; } if (buffer_remaining(&r) != 0) { WARNING("extra data at end"); } return mot; } void mot_free(struct mot *mot) { free(mot->name); for (uint32_t i = 0; i < mot->nr_bones; i++) { free(mot->motions[i]->name); free(mot->motions[i]); } if (mot->texture_indices) { free(mot->texture_indices); } mpr_free(mot->mpr); free(mot); } struct amt *amt_parse(uint8_t *data, size_t size) { struct buffer r; buffer_init(&r, data, size); if (memcmp(buffer_strdata(&r), "AMT\0", 4)) return NULL; buffer_skip(&r, 4); uint32_t version = buffer_read_int32(&r); int nr_fields; switch (version) { case 1: nr_fields = 3; break; case 2: nr_fields = 5; break; case 3: nr_fields = 6; break; case 4: nr_fields = 7; break; case 5: nr_fields = 11; break; case 6: nr_fields = 15; break; default: WARNING("unknown MOT version: %d", version); return NULL; } int nr_materials = buffer_read_int32(&r); struct amt *amt = xmalloc(sizeof(struct amt) + sizeof(struct amt_material *) * nr_materials); amt->version = version; amt->nr_materials = nr_materials; for (int i = 0; i < nr_materials; i++) { struct amt_material *m = amt->materials[i] = xmalloc(sizeof(struct amt_material) + sizeof(float) * nr_fields); m->name = read_cstring(&r); for (int j = 0; j < nr_fields; j++) m->fields[j] = buffer_read_float(&r); } if (buffer_remaining(&r) != 0) { WARNING("extra data at end"); } return amt; } void amt_free(struct amt *amt) { for (int i = 0; i < amt->nr_materials; i++) { free(amt->materials[i]->name); free(amt->materials[i]); } free(amt); } struct amt_material *amt_find_material(struct amt *amt, const char *name) { for (int i = 0; i < amt->nr_materials; i++) { if (!strcmp(amt->materials[i]->name, name)) return amt->materials[i]; } return NULL; } void opr_load(uint8_t *data, size_t size, struct pol *pol) { bool *selected = xmalloc(pol->nr_meshes * sizeof(bool)); for (int i = 0; i < pol->nr_meshes; i++) { selected[i] = !!pol->meshes[i]; } while (size > 0) { char line[200]; uint8_t *nl = memchr(data, '\n', size); if (nl) nl++; else nl = data + size; int copylen = min(nl - data, sizeof(line) - 1); memcpy(line, (char *)data, copylen); line[copylen] = '\0'; size -= nl - data; data = nl; char s[200]; int i1, i2, i3; float f1, f2, f3; if (sscanf(line, "Mesh = \"%[^\"]\"", s) == 1) { for (int i = 0; i < pol->nr_meshes; i++) { selected[i] = pol->meshes[i] && !strcmp(pol->meshes[i]->name, s); } } else if (sscanf(line, "MeshPart = \"%[^\"]\"", s) == 1) { for (int i = 0; i < pol->nr_meshes; i++) { selected[i] = pol->meshes[i] && strstr(pol->meshes[i]->name, s); } } else if (sscanf(line, "BlendMode = %s", s) == 1) { if (!strcmp(s, "Add")) { for (int i = 0; i < pol->nr_meshes; i++) { if (!selected[i]) continue; pol->meshes[i]->flags |= MESH_BLEND_ADDITIVE; } } else { WARNING("unknown BlendMode: %s", s); } } else if (sscanf(line, "Edge = %d", &i1) == 1) { if (i1 == 0) { for (int i = 0; i < pol->nr_meshes; i++) { if (!selected[i]) continue; pol->meshes[i]->flags |= MESH_NO_EDGE; } } else { WARNING("invalid Edge value: %d", i1); } } else if (sscanf(line, "EdgeColor = ( %d , %d , %d )", &i1, &i2, &i3) == 3) { for (int i = 0; i < pol->nr_meshes; i++) { if (!selected[i]) continue; pol->meshes[i]->edge_color = COLOR(i1, i2, i3, 255); } } else if (sscanf(line, "EdgeSize = %f", &f1) == 1) { for (int i = 0; i < pol->nr_meshes; i++) { if (!selected[i]) continue; pol->meshes[i]->edge_size = f1; } } else if (sscanf(line, "HeightDetection = %s", s) == 1) { if (!strcmp(s, "true")) { for (int i = 0; i < pol->nr_meshes; i++) { if (!selected[i]) continue; pol->meshes[i]->flags |= MESH_HEIGHT_DETECTION; } } else if (!strcmp(s, "false")) { for (int i = 0; i < pol->nr_meshes; i++) { if (!selected[i]) continue; pol->meshes[i]->flags |= MESH_NO_HEIGHT_DETECTION; } } else { WARNING("invalid HeightDetection: %s", s); } } else if (sscanf(line, "DrawShadow = %s", s) == 1) { if (!strcmp(s, "false")) { for (int i = 0; i < pol->nr_meshes; i++) { if (!selected[i]) continue; pol->meshes[i]->flags |= MESH_NO_DRAWSHADOW; } } else if (strcmp(s, "true")) { WARNING("invalid DrawShadow: %s", s); } } else if (sscanf(line, "ZWrite = %s", s) == 1) { if (!strcmp(s, "false")) { for (int i = 0; i < pol->nr_meshes; i++) { if (!selected[i]) continue; pol->meshes[i]->flags |= MESH_NO_ZWRITE; } } else if (strcmp(s, "true")) { WARNING("invalid ZWrite: %s", s); } } else if (sscanf(line, "UVScroll = ( %f , %f )", &f1, &f2) == 2) { for (int i = 0; i < pol->nr_meshes; i++) { if (!selected[i]) continue; pol->meshes[i]->uv_scroll[0] = f1; pol->meshes[i]->uv_scroll[1] = f2; } } else if (sscanf(line, "SpecularColor = ( %f , %f , %f )", &f1, &f2, &f3) == 3) { for (int i = 0; i < pol->nr_meshes; i++) { if (!selected[i]) continue; pol->meshes[i]->flags |= MESH_HAS_SPECULAR_COLOR; pol->meshes[i]->specular_color[0] = f1; pol->meshes[i]->specular_color[1] = f2; pol->meshes[i]->specular_color[2] = f3; } } else if (sscanf(line, "SpecularPower = %f", &f1) == 1) { for (int i = 0; i < pol->nr_meshes; i++) { if (!selected[i]) continue; pol->meshes[i]->flags |= MESH_HAS_SPECULAR_POWER; pol->meshes[i]->specular_power = f1; } } else if (sscanf(line, "ParallaxScale = %f", &f1) == 1) { // parallax mapping is not implemented } else if (sscanf(line, "ReliefScale = %f", &f1) == 1) { // relief mapping is not implemented } else if (sscanf(line, "MeshCombinable = %s", s) == 1) { if (strcmp(s, "true") && strcmp(s, "false")) WARNING("invalid MeshCombinable: %s", s); } else if (strchr(line, '=')) { WARNING("unknown field: %s", line); } } free(selected); } void txa_load(uint8_t *data, size_t size, struct mot *mot) { uint32_t count = 0; uint32_t *indices = xmalloc(100 * sizeof(uint32_t)); const char *p = (const char *)data; while (p < (const char *)data + size) { if (isdigit(*p)) { uint32_t n = 0; while (p < (const char *)data + size && isdigit(*p)) { n = n * 10 + (*p - '0'); p++; } indices[count++] = n; if (count % 100 == 0) { indices = xrealloc(indices, (count + 100) * sizeof(uint32_t)); } } else if (isspace(*p)) { p++; } else { WARNING("invalid character in txa file: '%c'", *p); p++; } } mot->nr_texture_indices = count; mot->texture_indices = indices; } void lit_reset(float *out) { static const float lit_param_defaults[RE_NR_LIGHT_PARAMS] = { [SEAL_LP_TONEMAP_EXPOSURE_BIAS] = 1.0f, [SEAL_LP_TONEMAP_WHITE_POINT] = 1.0f, [SEAL_LP_TONEMAP_A] = 0.22f, [SEAL_LP_TONEMAP_B] = 0.30f, [SEAL_LP_TONEMAP_C] = 0.10f, [SEAL_LP_TONEMAP_D] = 0.20f, [SEAL_LP_TONEMAP_E] = 0.01f, [SEAL_LP_TONEMAP_F] = 0.30f, [SEAL_LP_HEMI_VEC + 0] = -1.0f, [SEAL_LP_HEMI_VEC + 1] = -1.0f, [SEAL_LP_HEMI_VEC + 2] = 1.0f, [SEAL_LP_HEMI_SKY_COLOR + 0] = 1.2f, [SEAL_LP_HEMI_SKY_COLOR + 1] = 1.2f, [SEAL_LP_HEMI_SKY_COLOR + 2] = 1.2f, [SEAL_LP_HEMI_MID_COLOR + 0] = 0.5f, [SEAL_LP_HEMI_MID_COLOR + 1] = 0.5f, [SEAL_LP_HEMI_MID_COLOR + 2] = 0.5f, [SEAL_LP_HEMI_GROUND_COLOR + 0] = 0.3f, [SEAL_LP_HEMI_GROUND_COLOR + 1] = 0.3f, [SEAL_LP_HEMI_GROUND_COLOR + 2] = 0.3f, [SEAL_LP_LS_BETA_R] = 0.25f, [SEAL_LP_LS_BETA_M] = 1.25f, [SEAL_LP_LS_G] = 0.05f, [SEAL_LP_LS_DISTANCE] = 140.0f, [SEAL_LP_LS_LIGHT_VEC + 0] = -1.0f, [SEAL_LP_LS_LIGHT_VEC + 1] = -1.0f, [SEAL_LP_LS_LIGHT_VEC + 2] = 1.0f, [SEAL_LP_LS_LIGHT_COLOR + 0] = 1.0f, [SEAL_LP_LS_LIGHT_COLOR + 1] = 1.0f, [SEAL_LP_LS_LIGHT_COLOR + 2] = 1.0f, [SEAL_LP_LS_SUN_COLOR + 0] = 0.5f, [SEAL_LP_LS_SUN_COLOR + 1] = 0.5f, [SEAL_LP_LS_SUN_COLOR + 2] = 0.5f, }; memcpy(out, lit_param_defaults, sizeof(lit_param_defaults)); } bool lit_parse(uint8_t *data, size_t size, float *out) { if (size != 8 + RE_NR_LIGHT_PARAMS * 4 || memcmp(data, "LITP", 4) != 0) { WARNING("invalid .lit file"); return false; } struct buffer r; buffer_init(&r, data, size); buffer_skip(&r, 4); // "LITP" int32_t version = buffer_read_int32(&r); if (version != 0) { WARNING("unsupported .lit version %d", version); return false; } lit_reset(out); for (int i = 0; i < RE_NR_LIGHT_PARAMS; i++) out[i] = buffer_read_float(&r); return true; }