/* MODELS.C symbols in this file: 00112DB0 0430: _render_model_parts (0000) 001131E0 0110: _model_interpolate_node_orientations (0000) 001132F0 0090: _model_get_node_orientations (0000) 00113380 0140: _model_get_node_matrices (0000) 001134C0 0110: _model_node_matrices_from_orientations (0000) 001135D0 00a0: _model_find_marker (0000) 00113670 0030: _model_get_default_inverse_matrix (0000) 001136A0 0070: _model_find_node (0000) 00113710 0010: _model_geometry_part_build_tangent_matrices (0000) 00113720 0860: _render_model (0000) 00113F80 01d0: _model_get_marker_by_name (0000) 00114150 0070: _model_build_tangent_matrices (0000) 0027F98C 000d: ??_C@_0N@PBHIAIOK@render_model?$AA@ (0000) 0027F9A0 0061: ??_C@_0GB@CAHPCM@part?9?$DOcentroid_secondary_node_in@ (0000) 0027FA08 005d: ??_C@_0FN@MLJHGCND@part?9?$DOcentroid_primary_node_inde@ (0000) 0027FA68 002c: ??_C@_0CM@MBIKHGBC@?$CBTEST_FLAG?$CIflags?0?5_render_model_@ (0000) 0027FA94 001f: ??_C@_0BP@PJEILNMK@c?3?2halo?2SOURCE?2models?2models?4c?$AA@ (0000) 0027FAB4 001e: ??_C@_0BO@FFDGBNA@node?9?$DOparent_node_index?$CB?$DNNONE?$AA@ (0000) 0027FAD8 0064: ??_C@_0GE@EAOMEKOC@?$CIactual_detail_level_index?5?$DO?$DN?50?$CJ@ (0000) 0027FB3C 001e: ??_C@_0BO@INPHBEBB@actual_detail_level_index?5?$DO?50?$AA@ (0000) 0027FB60 0060: ??_C@_0GA@CEBIGMGH@geometry_detail_level_index?$DO?$DN0?5?$CG@ (0000) 0027FBC0 0009: ??_C@_08OEDKDIOB@lighting?$AA@ (0000) 0027FBD0 0073: ??_C@_0HD@NKPBNIND@object_marker?9?$DOnode_index?$DO?$DN0?5?$CG?$CG?5@ (0000) 0027FC44 0008: ??_C@_07LGFKJAB@markers?$AA@ (0000) 0027FC4C 000e: ??_C@_0O@BKFGBDDJ@node_matrices?$AA@ (0000) 0030A390 05f8: _render_model_section (0000) 00456650 0088: _default_function_values (0000) _default_render_model_change_colors (0000) _default_render_model_effect (0000) _default_render_model_region_permutation_indices (0000) */ /* ---------- headers */ #include "cseries.h" #include "cseries/errors.h" #include "cseries/profile.h" #include "models.h" #include "model_definitions.h" #include "game/game.h" #include "math/real_math.h" #include "objects/objects.h" #include "rasterizer/rasterizer.h" #include "rasterizer/rasterizer_geometry.h" #include "render/render.h" #include "render/render_debug.h" #include "scenario/scenario.h" #include "scenario/scenario_definitions.h" #include "shaders/shader_definitions.h" #include "shaders/shaders.h" #include "rasterizer/rasterizer_console_vars.h" #include "rasterizer/rasterizer_model_types.h" /* port: model tags and static enclosure recognition at map initialization. */ #include "cache/cache_files.h" #include "rasterizer/rasterizer_transparent_geometry.h" /* ---------- constants */ enum { NUMBER_OF_DETAIL_LEVELS_PER_MODEL = 5, CORTANA_MODEL_NODE_LIST_CHECKSUM = 124371095, }; enum { _scenario_cortana_hack_bit = 0, _scenario_demo_ui_bit, NUMBER_OF_SCENARIO_FLAGS }; enum { _rasterizer_geometry_no_sort_bit = 0, _rasterizer_geometry_no_queue_bit, _rasterizer_geometry_no_fog_bit, _rasterizer_geometry_no_zbuffer_bit, _rasterizer_geometry_sky_bit, _rasterizer_geometry_viewspace_bit, _rasterizer_geometry_atmospheric_fog_but_no_planar_fog_bit, _rasterizer_geometry_first_person_bit, _rasterizer_geometry_parts_define_local_nodes_bit, NUMBER_OF_RASTERIZER_GEOMETRY_FLAGS }; enum { _render_model_immediate_bit = 0, _render_model_shadow_bit, _render_model_no_planar_fog_bit, _render_model_first_person_bit, NUMBER_OF_RENDER_MODEL_FLAGS }; enum { _render_model_pass_solid = 0, _render_model_pass_decal, _render_model_pass_transparent, NUMBER_OF_RENDER_MODEL_PASSES }; enum { _model_geometry_part_stripped_bit = 0, _model_geometry_part_local_nodes_bit, NUMBER_OF_MODEL_GEOMETRY_PART_FLAGS }; enum { _shader_type_screen = 0, _shader_type_effect, _shader_type_decal, _shader_type_environment, _shader_type_model, _shader_type_transparent_generic, _shader_type_transparent_chicago, _shader_type_transparent_water, _shader_type_transparent_glass, _shader_type_transparent_meter, _shader_type_transparent_plasma, NUMBER_OF_SHADER_TYPES }; enum { _shader_model_detail_after_reflection_bit = 0, _shader_model_two_sided_bit, _shader_model_not_alpha_tested_bit, _shader_model_alpha_blended_decal_bit, _shader_model_true_atmospheric_fog_bit, _shader_model_nocull_two_sided_bit, NUMBER_OF_SHADER_MODEL_FLAGS }; /* ---------- macros */ /* ---------- structures */ struct model_shader_reference { struct tag_reference shader; short permutation_index; word pad; long unused[3]; }; struct model_geometry { byte reserved[0x24]; struct tag_block parts; }; struct model_geometry_part { unsigned long flags; short shader_index; char previous_part_index; char next_part_index; short centroid_primary_node_index; short centroid_secondary_node_index; real centroid_primary_node_weight; real centroid_secondary_node_weight; real_point3d centroid; struct tag_block uncompressed_vertices; struct tag_block compressed_vertices; struct tag_block triangles; struct triangle_buffer triangle_buffer; struct vertex_buffer vertex_buffer; }; typedef char verify_model_shader_reference_size[sizeof(struct model_shader_reference) == 0x20 ? 1 : -1]; typedef char verify_model_geometry_part_size[sizeof(struct model_geometry_part) == 0x68 ? 1 : -1]; struct shader_model_definition { struct shader shader; word flags; short type; byte reserved_before_translucency[0xC]; real translucency; }; struct render_sort_filth { short *previous_group_presorted_index_reference; short *next_group_presorted_index_reference; short group_index; short next_part_index; short part_index; word pad; }; typedef char verify_render_model_effect_size[sizeof(struct render_model_effect) == 0x28 ? 1 : -1]; typedef char verify_rasterizer_model_begin_parameters_size[sizeof(struct rasterizer_model_begin_parameters) == 0xCC ? 1 : -1]; /* ---------- prototypes */ #include "rasterizer/rasterizer_models.h" static void render_model_parts( struct model const *model, char const *region_permutation_indices, short region_permutation_count, struct render_skinning const *skinning, long object_index, short geometry_detail_level_index, short forced_shader_permutation_index, long flags); static void model_geometry_part_build_tangent_matrices( struct model_geometry_part *part); static void model_data_error( struct model const *model, char const *problem); /* ---------- globals */ extern boolean rasterizer_model_cortana_hack; extern boolean render_model_nodes; extern boolean render_model_markers; extern boolean render_model_vertex_counts; extern boolean render_model_index_counts; extern boolean render_model_no_geometry; static real default_function_values[MAXIMUM_FUNCTION_VALUES_PER_MODEL] = { 0 }; static real_rgb_color default_render_model_change_colors[MAXIMUM_CHANGE_COLORS_PER_MODEL] = { 0 }; static struct render_model_effect default_render_model_effect = { 0 }; static char default_render_model_region_permutation_indices[MAXIMUM_REGIONS_PER_MODEL] = { 0 }; static struct profile_section render_model_section = { "render_model", NONE, TRUE }; /* ---------- private code */ #ifdef HALO_ANDROID /* ---------- VR: the first-person hands' skin The VR arms bend the first-person hands model's arms far from where its animations have them; a triangle whose corners hang from bones that move apart (an arm's, the skeleton's root's) stretches across. Read from the part's own buffers, as drawn. */ void platform_log(char const *format, ...); char *tag_get_name(long tag_index); boolean rasterizer_model_buffer_data(struct vertex_buffer const *vertices, struct triangle_buffer const *triangles, void const **vertex_data, void const **index_data); /* the part's vertices and indices, if they are of the kinds read here */ static boolean model_vr_part_buffers( struct model_geometry_part const *part, struct model_vertex_compressed const **vertices, word const **indices) { if (!rasterizer_model_buffer_data(&part->vertex_buffer, &part->triangle_buffer, (void const **)vertices, (void const **)indices)) { return FALSE; } return *vertices && *indices && part->vertex_buffer.count > 0 && part->vertex_buffer.count <= 65535 && part->triangle_buffer.count > 0 && part->triangle_buffer.count <= 65535 && part->vertex_buffer.type == _rasterizer_vertex_type_model_compressed && (part->triangle_buffer.type == _triangle_buffer_type_precompiled_strip || part->triangle_buffer.type == _triangle_buffer_type_triangles); } /* a triangle's corners (from the strip, or the list) and the nodes they hang from (bits, a node more than a trace of weight); FALSE if malformed */ static boolean model_vr_triangle( struct model_geometry_part const *part, struct model_vertex_compressed const *vertices, word const *indices, long triangle_index, short node_count, word corners[3], unsigned long long *nodes) { short corner; *nodes = 0; for (corner = 0; corner < 3; corner++) { long at = part->triangle_buffer.type == _triangle_buffer_type_triangles ? triangle_index * 3 + corner : triangle_index + corner; struct model_vertex_compressed const *vertex; real weight; short influence; corners[corner] = indices[at]; if (corners[corner] >= part->vertex_buffer.count) return FALSE; vertex = &vertices[corners[corner]]; /* (the weight is a signed short's fraction, as rasterizer_debug_model_vertices reads it) */ weight = (real)vertex->node_weight / 32767.0f; if (weight < 0.0f || weight > 1.0f) return FALSE; for (influence = 0; influence < 2; influence++) { short node = (short)(vertex->nodes[influence] / 3); real amount = influence ? 1.0f - weight : weight; if (amount <= 0.001f) continue; if (vertex->nodes[influence] % 3 || node >= node_count || node >= 64) return FALSE; *nodes |= 1ULL << node; } } return TRUE; } /* once a model: how many triangles hang from each node, and how many of those from its root too (debug's logcat; first_person_weapons.c, for the hands) */ void model_vr_log_skin( long model_index) { static long logged[8] = { NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE }; struct model *model; long triangles[64]; long with_root[64]; long total = 0; short slot; short node_index; short region_index; short geometry_seen[MAXIMUM_REGIONS_PER_MODEL]; short geometry_seen_count = 0; boolean readable = TRUE; for (slot = 0; slot < NUMBEROF(logged); slot++) { if (logged[slot] == model_index) return; if (logged[slot] == NONE) break; } if (slot == NUMBEROF(logged) || model_index == NONE) return; logged[slot] = model_index; model = model_definition_get(model_index); if (model->nodes.count <= 0 || model->nodes.count > 64) return; csmemset(triangles, 0, sizeof(triangles)); csmemset(with_root, 0, sizeof(with_root)); /* (each region's first permutation, in the finest detail, as drawn) */ for (region_index = 0; region_index < model->regions.count && region_index < MAXIMUM_REGIONS_PER_MODEL; region_index++) { struct model_region *region = TAG_BLOCK_GET_ELEMENT(&model->regions, region_index, struct model_region); struct model_region_permutation *permutation; short geometry_index; short seen; struct model_geometry *geometry; short part_index; if (region->permutations.count <= 0) continue; permutation = TAG_BLOCK_GET_ELEMENT(®ion->permutations, 0, struct model_region_permutation); geometry_index = permutation->geometry_indices[NUMBER_OF_DETAIL_LEVELS_PER_MODEL - 1]; for (seen = 0; seen < geometry_seen_count && geometry_seen[seen] != geometry_index; seen++); if (geometry_index == NONE || seen < geometry_seen_count) continue; geometry_seen[geometry_seen_count++] = geometry_index; geometry = TAG_BLOCK_GET_ELEMENT(&model->geometries, geometry_index, struct model_geometry); for (part_index = 0; part_index < geometry->parts.count; part_index++) { struct model_geometry_part *part = TAG_BLOCK_GET_ELEMENT(&geometry->parts, part_index, struct model_geometry_part); struct model_vertex_compressed const *vertices; word const *indices; long triangle_index; if (!model_vr_part_buffers(part, &vertices, &indices)) { readable = FALSE; continue; } for (triangle_index = 0; triangle_index < part->triangle_buffer.count; triangle_index++) { word corners[3]; unsigned long long nodes; if (!model_vr_triangle(part, vertices, indices, triangle_index, model->nodes.count, corners, &nodes)) { readable = FALSE; break; } if (corners[0] == corners[1] || corners[1] == corners[2] || corners[0] == corners[2]) continue; total++; for (node_index = 0; node_index < model->nodes.count; node_index++) { if (!(nodes & (1ULL << node_index))) continue; triangles[node_index]++; if (nodes & 1) with_root[node_index]++; } } } } platform_log("VR skin of %s: %ld triangles%s; %ld hang from its root '%s'", tag_get_name(model_index), total, readable ? "" : " (some parts unreadable)", triangles[0], TAG_BLOCK_GET_ELEMENT(&model->nodes, 0, struct model_node)->name); for (node_index = 1; node_index < model->nodes.count; node_index++) { if (triangles[node_index]) { platform_log("VR skin: node %d '%s': %ld triangles, %ld with the root", node_index, TAG_BLOCK_GET_ELEMENT(&model->nodes, node_index, struct model_node)->name, triangles[node_index], with_root[node_index]); } } } #endif static void render_model_parts( struct model const *model, char const *region_permutation_indices, short region_permutation_count, struct render_skinning const *skinning, long object_index, short geometry_detail_level_index, short forced_shader_permutation_index, long flags) { boolean immediate = TEST_FLAG(flags, _render_model_immediate_bit); short last_pass = TEST_FLAG(flags, _render_model_shadow_bit) ? _render_model_pass_solid : _render_model_pass_transparent; struct render_sort_filth sort_filth[MAXIMUM_PARTS_PER_MODEL_GEOMETRY]; real_point3d centroid; short pass; for (pass = _render_model_pass_solid; pass<=last_pass; pass++) { short sort_filth_count = 0; short region_index; short i, j; /* port: no more regions than the permutations passed in hold: an object's MAXIMUM_REGIONS_PER_OBJECT, or the default's MAXIMUM_REGIONS_PER_MODEL (a map's count; retail models have 8 at most) */ short region_count = (short)MIN(model->regions.count, region_permutation_count); for (region_index = 0; region_indexregions, region_index, struct model_region); char permutation_index = region_permutation_indices[region_index]; if (permutation_index!=NONE) { struct model_region_permutation *permutation; short geometry_index; /* port: a permutation the region has, and a geometry the model has (a map's indices); a bad one draws nothing */ if (!VALID_INDEX(permutation_index, region->permutations.count)) { model_data_error(model, "region permutation"); continue; } permutation = TAG_BLOCK_GET_ELEMENT( ®ion->permutations, permutation_index, struct model_region_permutation); geometry_index = permutation->geometry_indices[geometry_detail_level_index]; if (geometry_index!=NONE && !VALID_INDEX(geometry_index, model->geometries.count)) { model_data_error(model, "geometry"); continue; } if (!render_model_no_geometry && geometry_index!=NONE) { struct model_geometry *geometry = TAG_BLOCK_GET_ELEMENT(&model->geometries, geometry_index, struct model_geometry); short part_index; for (part_index = 0; part_indexparts.count; part_index++) { struct model_geometry_part *part = TAG_BLOCK_GET_ELEMENT(&geometry->parts, part_index, struct model_geometry_part); struct model_shader_reference *shader_reference; struct shader *shader; /* port: a shader the model has (a map's index); a part without one isn't drawn */ if (!VALID_INDEX(part->shader_index, model->shaders.count)) { model_data_error(model, "shader"); continue; } shader_reference = TAG_BLOCK_GET_ELEMENT( &model->shaders, part->shader_index, struct model_shader_reference); shader = shader_definition_get(shader_reference->shader.index); if (shader_type_is_valid_for_model(shader->base.type) && !TEST_FLAG(part->flags, _model_geometry_part_stripped_bit)) { if (shader_type_is_transparent(shader->base.type)) { if (pass==_render_model_pass_transparent) { /* port: the root's matrix for a node the model doesn't have (a map's index) */ short centroid_node_index = VALID_INDEX(part->centroid_primary_node_index, skinning->node_matrix_count) ? part->centroid_primary_node_index : 0; match_assert("c:\\halo\\SOURCE\\models\\models.c", 442, !TEST_FLAG(flags, _render_model_shadow_bit)); match_assert( "c:\\halo\\SOURCE\\models\\models.c", 445, part->centroid_primary_node_index>=0 && part->centroid_primary_node_indexnodes.count); match_assert( "c:\\halo\\SOURCE\\models\\models.c", 446, part->centroid_secondary_node_index>=0 && part->centroid_secondary_node_indexnodes.count); matrix4x3_transform_point( &skinning->node_matrices[centroid_node_index], &part->centroid, ¢roid); rasterizer_model_transparent_geometry_submit( shader, forced_shader_permutation_index ? forced_shader_permutation_index : shader_reference->permutation_index, &part->triangle_buffer, NONE, part->triangle_buffer.count, &part->vertex_buffer, NONE, ¢roid, /* port: no sort record once all of sort_filth is used (it wrote one past it) */ sort_filth_countnext_part_index>0 || part->previous_part_index>=0)) { sort_filth[sort_filth_count].part_index = part_index; sort_filth[sort_filth_count].next_part_index = part->next_part_index; sort_filth_count++; } } } else if (shader->base.type==_shader_type_model && TEST_FLAG(((struct shader_model_definition *)shader_get_and_verify_type(shader, _shader_type_model))->flags, _shader_model_alpha_blended_decal_bit)) { if (pass==_render_model_pass_decal) { match_assert("c:\\halo\\SOURCE\\models\\models.c", 491, !TEST_FLAG(flags, _render_model_shadow_bit)); rasterizer_model_draw( shader, forced_shader_permutation_index ? forced_shader_permutation_index : shader_reference->permutation_index, &part->triangle_buffer, NONE, part->triangle_buffer.count, &part->vertex_buffer, NONE); } } else if (pass==_render_model_pass_solid) { if (TEST_FLAG(flags, _render_model_shadow_bit)) { rasterizer_environment_shadow_model_draw( shader, forced_shader_permutation_index ? forced_shader_permutation_index : shader_reference->permutation_index, &part->triangle_buffer, &part->vertex_buffer); } else { rasterizer_model_draw( shader, forced_shader_permutation_index ? forced_shader_permutation_index : shader_reference->permutation_index, &part->triangle_buffer, NONE, part->triangle_buffer.count, &part->vertex_buffer, NONE); rasterizer_debug_model_vertices(object_index, skinning, part); } } } } } } } for (i = 0; i0) { *sort_filth[i].next_group_presorted_index_reference = sort_filth[j].group_index; *sort_filth[j].previous_group_presorted_index_reference = sort_filth[i].group_index; break; } } } } return; } /* ---------- public code */ /* port: a pickup's energy inside its two-sided glass shell is drawn before the shell with the engine's own part links (render_model_parts), as Halo CE Restored's tags link them, so that the centroid sort cannot put it after the glass; models with links of their own, skinned ones and those with other transparent parts are left as they are */ static void model_geometry_fix_transparent_part_links( struct model const *model, struct model_geometry *geometry) { short i, glass = NONE, energy = NONE, transparent_count = 0; struct model_geometry_part *parts = geometry->parts.address; if (model->nodes.count != 1 || geometry->parts.count < 2 || geometry->parts.count > MAXIMUM_PARTS_PER_MODEL_GEOMETRY || !parts) { return; } for (i = 0; i < geometry->parts.count; ++i) { struct shader *shader; struct model_shader_reference const *reference; if (parts[i].flags || parts[i].previous_part_index != NONE || parts[i].next_part_index != NONE || !VALID_INDEX(parts[i].shader_index, model->shaders.count)) { return; } reference = TAG_BLOCK_GET_ELEMENT(&model->shaders, parts[i].shader_index, struct model_shader_reference); shader = shader_definition_get(reference->shader.index); if (shader_type_is_transparent(shader->base.type)) { ++transparent_count; if (shader->base.type == _shader_type_transparent_glass) { glass = i; } else if (shader->base.type == _shader_type_transparent_generic) { energy = i; } } } if (transparent_count == 2 && glass != NONE && energy != NONE && rasterizer_transparent_geometry_is_enclosure( shader_definition_get(TAG_BLOCK_GET_ELEMENT(&model->shaders, parts[glass].shader_index, struct model_shader_reference)->shader.index), &parts[glass].vertex_buffer, &parts[glass].triangle_buffer, shader_definition_get(TAG_BLOCK_GET_ELEMENT(&model->shaders, parts[energy].shader_index, struct model_shader_reference)->shader.index), &parts[energy].vertex_buffer)) { /* (a link to part zero is none: with the glass part zero, the two parts, which have no links of their own, change places) */ if (glass == 0) { struct model_geometry_part swap = parts[glass]; parts[glass] = parts[energy]; parts[energy] = swap; glass = energy; energy = 0; } parts[energy].next_part_index = (char)glass; parts[glass].previous_part_index = (char)energy; } } /* port: the links of every model the map loaded (at its start: the meshes do not change, and the next map's tags are loaded afresh) */ void models_fix_transparent_part_links(void) { struct tag_iterator iterator; long index; tag_iterator_new(&iterator, MODELS_GROUP_TAG); while ((index = tag_iterator_next(&iterator)) != NONE) { struct model *model = model_definition_get(index); long geometry; for (geometry = 0; geometry < model->geometries.count; ++geometry) { model_geometry_fix_transparent_part_links(model, TAG_BLOCK_GET_ELEMENT(&model->geometries, geometry, struct model_geometry)); } } } void model_interpolate_node_orientations( struct model const *model, struct real_orientation *original_node_orientations, struct real_orientation *target_node_orientations, short frame_index, short frame_count) { real fraction = (real)(frame_index + 1) / (real)frame_count; real inverse_fraction = 1.f - fraction; short node_index; /* port: no more nodes than the engine's arrays hold (a map's count) */ short node_count = (short)MIN(model->nodes.count, MAXIMUM_NODES_PER_MODEL); match_assert( "c:\\halo\\SOURCE\\models\\models.c", 579, frame_count>0); match_assert( "c:\\halo\\SOURCE\\models\\models.c", 580, frame_indexscale = original->scale * inverse_fraction + target->scale * fraction; quaternions_interpolate_and_normalize( &original->rotation, &target->rotation, fraction, &target->rotation); target->translation.x = original->translation.x * inverse_fraction + target->translation.x * fraction; target->translation.y = original->translation.y * inverse_fraction + target->translation.y * fraction; target->translation.z = original->translation.z * inverse_fraction + target->translation.z * fraction; } return; } void model_get_node_orientations( struct model const *model, real_orientation *node_orientations) { short node_index; /* port: no more nodes than the engine's arrays hold (a map's count) */ short node_count = (short)MIN(model->nodes.count, MAXIMUM_NODES_PER_MODEL); for (node_index = 0; node_indexnodes, node_index, struct model_node); node_orientations[node_index].rotation = node->default_rotation; node_orientations[node_index].translation = node->default_translation; node_orientations[node_index].scale = 1.f; } return; } void model_get_node_matrices( struct model const *model, real_matrix4x3 *node_matrices, real_point3d const *origin, real_vector3d const *forward, real_vector3d const *up) { short node_queue[MAXIMUM_NODES_PER_MODEL]; short read_index, write_index; /* port: the nodes the queue and the matrices hold (a map's count) */ short node_count = (short)MIN(model->nodes.count, MAXIMUM_NODES_PER_MODEL); /* port: a model without nodes has no matrices */ if (node_count<=0) { return; } node_queue[0] = 0; read_index = 0; write_index = 1; while (read_index!=write_index) { short node_index = node_queue[read_index++]; struct model_node *node = TAG_BLOCK_GET_ELEMENT(&model->nodes, node_index, struct model_node); real_matrix4x3 node_matrix; matrix4x3_from_point_and_quaternion(&node_matrix, &node->default_translation, &node->default_rotation); if (node_index == 0) { matrix4x3_from_point_and_vectors( &node_matrices[node_index], origin ? origin : global_origin3d, forward ? forward : global_forward3d, up ? up : global_up3d); matrix4x3_multiply(&node_matrices[node_index], &node_matrix, &node_matrices[node_index]); } else { short parent_node_index = node->parent_node_index; match_assert("c:\\halo\\SOURCE\\models\\models.c", 650, node->parent_node_index!=NONE); /* port: a parent the model doesn't have is the root (a map's index) */ if (!VALID_INDEX(parent_node_index, node_count)) { model_data_error(model, "node's parent"); parent_node_index = 0; } matrix4x3_multiply(&node_matrices[parent_node_index], &node_matrix, &node_matrices[node_index]); } /* port: only nodes the model has, and no more than the queue holds (a map's links, which could go in a loop) */ if (node->next_sibling_node_index!=NONE) { if (VALID_INDEX(node->next_sibling_node_index, node_count) && write_indexnext_sibling_node_index; } else { model_data_error(model, "node's sibling"); } } if (node->first_child_node_index!=NONE) { if (VALID_INDEX(node->first_child_node_index, node_count) && write_indexfirst_child_node_index; } else { model_data_error(model, "node's child"); } } } return; } void model_node_matrices_from_orientations( struct model const *model, real_matrix4x3 *node_matrices, real_orientation const *node_orientations, real_point3d const *origin, real_vector3d const *forward, real_vector3d const *up) { real_matrix4x3 root_matrix; /* port: the nodes the queue and the matrices hold (a map's count) */ short node_count = (short)MIN(model->nodes.count, MAXIMUM_NODES_PER_MODEL); matrix4x3_from_point_and_vectors(&root_matrix, origin, forward, up); if (node_count>0) { short node_queue[MAXIMUM_NODES_PER_MODEL]; short read_index = 0; short write_index = 1; node_queue[0] = 0; do { short node_index = node_queue[read_index++]; struct model_node *node = TAG_BLOCK_GET_ELEMENT(&model->nodes, node_index, struct model_node); real_matrix4x3 const *parent_matrix = node_index==0 ? &root_matrix : &node_matrices[node->parent_node_index]; real_matrix4x3 node_matrix; /* port: a parent the model doesn't have is the root (a map's index) */ if (node_index!=0 && !VALID_INDEX(node->parent_node_index, node_count)) { model_data_error(model, "node's parent"); parent_matrix = &node_matrices[0]; } matrix4x3_from_orientation(&node_matrix, &node_orientations[node_index]); matrix4x3_multiply(parent_matrix, &node_matrix, &node_matrices[node_index]); /* port: only nodes the model has, and no more than the queue holds (a map's links, which could go in a loop) */ if (node->next_sibling_node_index!=NONE) { if (VALID_INDEX(node->next_sibling_node_index, node_count) && write_indexnext_sibling_node_index; } else { model_data_error(model, "node's sibling"); } } if (node->first_child_node_index!=NONE) { if (VALID_INDEX(node->first_child_node_index, node_count) && write_indexfirst_child_node_index; } else { model_data_error(model, "node's child"); } } } while (read_index!=write_index); } return; } short model_find_marker( long model_index, char const *name) { if (model_index != NONE && name && *name) { struct model *model = model_definition_get(model_index); short lower_bound = 0; short upper_bound = (short)model->markers.count - 1; while (lower_bound <= upper_bound) { short marker_index = (short)((lower_bound + upper_bound) / 2); struct model_marker *marker = TAG_BLOCK_GET_ELEMENT( &model->markers, marker_index, struct model_marker); long comparison = _stricmp(name, marker->name); if (comparison == 0) { return marker_index; } if (comparison < 0) { upper_bound = marker_index - 1; } else { lower_bound = marker_index + 1; } } } return NONE; } real_matrix4x3 *model_get_default_inverse_matrix( struct model *model, short node_index) { struct model_node *node = TAG_BLOCK_GET_ELEMENT(&model->nodes, node_index, struct model_node); return &node->runtime_default_inverse_matrix; } short model_find_node( long model_index, char const *name) { if (model_index!=NONE) { short node_index; struct model *model = model_definition_get(model_index); for (node_index = 0; node_indexnodes.count; node_index++) { struct model_node *node = TAG_BLOCK_GET_ELEMENT(&model->nodes, node_index, struct model_node); if (!csstrcmp(node->name, name)) { return node_index; } } } return NONE; } short model_get_marker_by_name( long model_index, char const *name, byte const *region_permutations, short const *node_remapping_table, short node_count, real_matrix4x3 const *node_matrices, boolean mirrored_flag, struct object_marker *markers, short maximum_marker_count) { short result = 0; short marker_index = model_find_marker(model_index, name); match_assert("c:\\halo\\SOURCE\\models\\models.c", 760, node_matrices); match_assert("c:\\halo\\SOURCE\\models\\models.c", 761, markers); if (marker_index!=NONE) { short i; struct model *model = model_definition_get(model_index); struct model_marker* marker = TAG_BLOCK_GET_ELEMENT(&model->markers, marker_index, struct model_marker); /* port: the nodes the matrices passed in hold (a map's counts) */ short matrix_count = node_remapping_table ? (short)MIN(node_count, MAXIMUM_NODES_PER_MODEL) : (short)MIN(model->nodes.count, MAXIMUM_NODES_PER_MODEL); for (i =0; iinstances.count; i++) { struct model_marker_instance* instance = TAG_BLOCK_GET_ELEMENT(&marker->instances, i, struct model_marker_instance); /* port: a marker on a region or node the model doesn't have is skipped (a map's indices); the permutations passed in are an object's (retail's markers are on regions 5 at most) */ if ((region_permutations && instance->region_index>=MAXIMUM_REGIONS_PER_OBJECT) || instance->node_index>=MIN(model->nodes.count, MAXIMUM_NODES_PER_MODEL)) { model_data_error(model, "marker"); continue; } if (!region_permutations || region_permutations[instance->region_index]==instance->permutation_index) { struct object_marker *object_marker; short marker_node_index; if (result>=maximum_marker_count) { break; } marker_node_index = node_remapping_table ? node_remapping_table[instance->node_index] : instance->node_index; if (!VALID_INDEX(marker_node_index, matrix_count)) { model_data_error(model, "marker"); continue; } object_marker = &markers[result++]; object_marker->node_index = marker_node_index; matrix4x3_from_point_and_quaternion(&object_marker->node_matrix, &instance->translation, &instance->rotation); match_assert( "c:\\halo\\SOURCE\\models\\models.c", 785, object_marker->node_index>=0 && object_marker->node_index<(node_remapping_table ? node_count : model->nodes.count)); matrix4x3_multiply(&node_matrices[object_marker->node_index], &object_marker->node_matrix, &object_marker->matrix); if (mirrored_flag) { negate_vector3d(&object_marker->matrix.left, &object_marker->matrix.left); } } } } return result; } void model_build_tangent_matrices( struct model *model) { short geometry_index; for (geometry_index = 0; geometry_index < model->geometries.count; geometry_index++) { struct model_geometry *geometry = TAG_BLOCK_GET_ELEMENT( &model->geometries, geometry_index, struct model_geometry); short part_index; for (part_index = 0; part_index < geometry->parts.count; part_index++) { model_geometry_part_build_tangent_matrices(TAG_BLOCK_GET_ELEMENT( &geometry->parts, part_index, struct model_geometry_part)); } } return; } static void model_geometry_part_build_tangent_matrices( struct model_geometry_part *part) { return; } /* port: a map's model with an index past what it has */ static void model_data_error( struct model const *model, char const *problem) { if (model_data_report_once(model)) { error( _error_silent, "### ERROR a model (%ld nodes) has a bad %s index; it is skipped", model->nodes.count, problem); } return; } void render_model( long model_index, real level_of_detail_pixels, real_matrix4x3 const *node_matrices, char const *region_permutation_indices, real_rgb_color const *change_colors, real const *function_values, struct render_lighting const *lighting, real_point3d const *centroid, real radius, struct render_model_effect const *model_effect, long unique_identifier, short forced_shader_permutation_index, unsigned long flags) { struct model *model = model_definition_get(model_index); profile_enter(render_model_section); match_assert("c:\\halo\\SOURCE\\models\\models.c", 82, lighting); if (model->node_list_checksum==CORTANA_MODEL_NODE_LIST_CHECKSUM && TEST_FLAG(global_scenario_get()->flags, _scenario_cortana_hack_bit)) { rasterizer_model_cortana_hack = TRUE; } else { rasterizer_model_cortana_hack = FALSE; } if (level_of_detail_pixels>=model->detail_cutoff_pixels[0] || TEST_FLAG(flags, _render_model_shadow_bit)) { real_matrix4x3 relative_node_matrices[MAXIMUM_NODES_PER_MODEL]; struct rasterizer_model_begin_parameters model_parameters; short geometry_detail_level_index; short node_index; /* port: how many regions the permutations passed in hold (an object's; the default's otherwise) */ short region_permutation_count = region_permutation_indices ? MAXIMUM_REGIONS_PER_OBJECT : MAXIMUM_REGIONS_PER_MODEL; /* port: no more nodes than relative_node_matrices holds (a map's count) */ short node_count = (short)MIN(model->nodes.count, MAXIMUM_NODES_PER_MODEL); if (!region_permutation_indices) { region_permutation_indices = default_render_model_region_permutation_indices; } if (!model_effect) { model_effect = &default_render_model_effect; } if (!change_colors) { change_colors = default_render_model_change_colors; } if (!function_values) { function_values = default_function_values; } if (!centroid) { centroid = &node_matrices->position; } if (node_matrices) { for (node_index = 0; node_indexnodes, node_index, struct model_node); matrix4x3_multiply( &node_matrices[node_index], &node->runtime_default_inverse_matrix, &relative_node_matrices[node_index]); } } else { for (node_index = 0; node_index0 && level_of_detail_pixelsdetail_cutoff_pixels[geometry_detail_level_index]) { geometry_detail_level_index--; } if (rasterizer_debug_options.debug_model_lod!=NONE) { geometry_detail_level_index = PIN(rasterizer_debug_options.debug_model_lod, 0, NUMBER_OF_DETAIL_LEVELS_PER_MODEL-1); } match_assert( "c:\\halo\\SOURCE\\models\\models.c", 169, geometry_detail_level_index>=0 && geometry_detail_level_indexnodes, node_index, struct model_node); /* port: and the parent is one the model has (a map's index) */ if (VALID_INDEX(node->parent_node_index, node_count)) { render_debug_line( TRUE, &node_matrices[node_index].position, &node_matrices[node->parent_node_index].position, global_real_argb_white); } render_debug_matrix(TRUE, &node_matrices[node_index], 0.05f); } } if (render_model_markers) { short marker_index; for (marker_index = 0; marker_indexmarkers.count; marker_index++) { struct model_marker *marker = TAG_BLOCK_GET_ELEMENT(&model->markers, marker_index, struct model_marker); short instance_index; for (instance_index = 0; instance_indexinstances.count; instance_index++) { struct model_marker_instance *instance = TAG_BLOCK_GET_ELEMENT( &marker->instances, instance_index, struct model_marker_instance); /* port: and the region and node are ones the model has room for (a map's indices) */ if (instance->region_indexnode_indexregion_index]==instance->permutation_index) { real_matrix4x3 marker_matrix; matrix4x3_from_point_and_quaternion(&marker_matrix, &instance->translation, &instance->rotation); matrix4x3_multiply(&node_matrices[instance->node_index], &marker_matrix, &marker_matrix); render_debug_matrix(FALSE, &marker_matrix, 0.05f); render_debug_string_at_point(FALSE, &marker_matrix.position, marker->name, global_real_argb_white); } } } } if (render_model_vertex_counts || render_model_index_counts) { short maximum_actual_detail_level_index = geometry_detail_level_index; boolean has_unstripped_parts = FALSE; short vertex_count = 0; short index_count = 0; short region_index; real distance; /* port: the regions and permutations render_model_parts draws (a map's counts and indices) */ for (region_index = 0; region_indexregions.count, region_permutation_count); region_index++) { struct model_region *region = TAG_BLOCK_GET_ELEMENT(&model->regions, region_index, struct model_region); char permutation_index = region_permutation_indices[region_index]; if (VALID_INDEX(permutation_index, region->permutations.count)) { struct model_region_permutation *permutation = TAG_BLOCK_GET_ELEMENT( ®ion->permutations, permutation_index, struct model_region_permutation); short actual_detail_level_index; short geometry_index; for (actual_detail_level_index = geometry_detail_level_index+1; actual_detail_level_indexgeometry_indices[actual_detail_level_index]!= permutation->geometry_indices[geometry_detail_level_index]) { break; } } match_assert("c:\\halo\\SOURCE\\models\\models.c", 247, actual_detail_level_index > 0); actual_detail_level_index--; match_assert( "c:\\halo\\SOURCE\\models\\models.c", 249, (actual_detail_level_index >= 0) && (actual_detail_level_index < NUMBER_OF_DETAIL_LEVELS_PER_MODEL)); maximum_actual_detail_level_index = MAX(maximum_actual_detail_level_index, actual_detail_level_index); geometry_index = permutation->geometry_indices[geometry_detail_level_index]; if (VALID_INDEX(geometry_index, model->geometries.count)) { struct model_geometry *geometry = TAG_BLOCK_GET_ELEMENT(&model->geometries, geometry_index, struct model_geometry); short part_index; for (part_index = 0; part_indexparts.count; part_index++) { struct model_geometry_part *part = TAG_BLOCK_GET_ELEMENT(&geometry->parts, part_index, struct model_geometry_part); vertex_count+= part->vertex_buffer.count; switch (part->triangle_buffer.type) { case _triangle_buffer_type_triangles: index_count+= 3*part->triangle_buffer.count; has_unstripped_parts = TRUE; break; case _triangle_buffer_type_precompiled_strip: index_count+= part->triangle_buffer.count+2; break; default: match_assert("c:\\halo\\SOURCE\\models\\models.c", 276, !"unreachable"); } } } } } distance = fabs( render.frustum.world_to_view.forward.k*centroid->x + render.frustum.world_to_view.left.k*centroid->y + render.frustum.world_to_view.up.k*centroid->z + render.frustum.world_to_view.position.z); level_of_detail_pixels = distance/render.frustum.projection_world_to_screen.j*level_of_detail_pixels*0.5f; if (level_of_detail_pixels>0.0001f) { real_argb_color const *detail_level_colors[NUMBER_OF_DETAIL_LEVELS_PER_MODEL]; real_argb_color const *color; char string[256]; real_point3d point; detail_level_colors[0] = global_real_argb_blue; detail_level_colors[1] = global_real_argb_green; detail_level_colors[2] = global_real_argb_yellow; detail_level_colors[3] = global_real_argb_orange; detail_level_colors[4] = global_real_argb_red; color = detail_level_colors[maximum_actual_detail_level_index]; if (has_unstripped_parts && (game_time_get()+model_index)%30<15) { color = global_real_argb_white; } csstrcpy(string, ""); if (render_model_vertex_counts) { _snprintf(string+csstrlen(string), sizeof(string)-csstrlen(string), "%d", vertex_count); } if (render_model_vertex_counts && render_model_index_counts) { _snprintf(string+csstrlen(string), sizeof(string)-csstrlen(string), "/"); } if (render_model_index_counts) { _snprintf(string+csstrlen(string), sizeof(string)-csstrlen(string), "%d", index_count); } set_real_point3d(&point, centroid->x, centroid->y, centroid->z+level_of_detail_pixels); render_debug_string_at_point(FALSE, &point, string, color); } } } model_parameters.unique_identifier = unique_identifier; model_parameters.lighting = *lighting; model_parameters.centroid = *centroid; model_parameters.radius = radius; model_parameters.effect = *model_effect; model_parameters.animation.colors = change_colors; model_parameters.animation.values = function_values; model_parameters.skinning.node_matrices = relative_node_matrices; model_parameters.skinning.node_matrix_count = node_count; model_parameters.geometry_flags = 0; model_parameters.base_map_scale = model->base_map_scale; if (TEST_FLAG(flags, _render_model_immediate_bit)) { model_parameters.geometry_flags = FLAG(_rasterizer_geometry_no_sort_bit) | FLAG(_rasterizer_geometry_no_queue_bit) | FLAG(_rasterizer_geometry_no_fog_bit) | FLAG(_rasterizer_geometry_no_zbuffer_bit) | FLAG(_rasterizer_geometry_sky_bit); } SET_FLAG(model_parameters.geometry_flags, _rasterizer_geometry_atmospheric_fog_but_no_planar_fog_bit, TEST_FLAG(flags, _render_model_no_planar_fog_bit)); SET_FLAG(model_parameters.geometry_flags, _rasterizer_geometry_first_person_bit, TEST_FLAG(flags, _render_model_first_person_bit)); if (TEST_FLAG(flags, _render_model_shadow_bit)) { rasterizer_environment_shadow_model_begin(&model_parameters); } else { rasterizer_model_begin(&model_parameters, FALSE); } render_model_parts( model, region_permutation_indices, region_permutation_count, &model_parameters.skinning, unique_identifier, geometry_detail_level_index, forced_shader_permutation_index, flags); if (TEST_FLAG(flags, _render_model_shadow_bit)) { rasterizer_environment_shadow_model_end(); } else { rasterizer_model_end(); } } rasterizer_model_cortana_hack = FALSE; profile_exit(render_model_section); return; } /* port: TRUE the first time a map's model, animation or other tag data is found bad. The checks run every frame; the report goes out once. */ boolean model_data_report_once( void const *data) { static void const *reported_data[32]; static long next_reported_index = 0; /* (and no more than this many reports in all: with more bad tags than the list holds, each would push another out and be reported again every frame) */ static long report_count = 0; long reported_index; for (reported_index = 0; reported_index<(long)NUMBEROF(reported_data); reported_index++) { if (reported_data[reported_index]==data) { return FALSE; } } if (report_count >= 4 * (long)NUMBEROF(reported_data)) { return FALSE; } report_count++; reported_data[next_reported_index] = data; next_reported_index = (next_reported_index+1)%(long)NUMBEROF(reported_data); return TRUE; }