#include "collide_drawable.h" #include #include #include "common/util/Assert.h" #include "goalc/data_compiler/DataObjectGenerator.h" /* (deftype drawable (basic) ((id int16 :offset-assert 4) (bsphere vector :inline :offset-assert 16) ) (deftype drawable-group (drawable) ((length int16 :offset 6) (data drawable 1 :offset-assert 32) ) (deftype drawable-tree (drawable-group) () (deftype drawable-inline-array (drawable) ((length int16 :offset 6) ;; this is kinda weird. ) (deftype drawable-tree-collide-fragment (drawable-tree) ((data-override drawable-inline-array 1 :offset 32)) ;; should be 1 there (deftype drawable-inline-array-collide-fragment (drawable-inline-array) ((data collide-fragment 1 :inline :offset-assert 32) (deftype collide-fragment (drawable) ((mesh collide-frag-mesh :offset 8) ) :method-count-assert 18 :size-assert #x20 (deftype collide-frag-mesh (basic) ((packed-data uint32 :offset-assert 4) (pat-array uint32 :offset-assert 8) (strip-data-len uint16 :offset-assert 12) (poly-count uint16 :offset-assert 14) (base-trans vector :inline :offset-assert 16) ;; these go in the w of the vector above. (vertex-count uint8 :offset 28) (vertex-data-qwc uint8 :offset 29) (total-qwc uint8 :offset 30) (unused uint8 :offset 31) ) :method-count-assert 9 :size-assert #x20 (deftype draw-node (drawable) ((child-count uint8 :offset 6) (flags uint8 :offset 7) (child drawable :offset 8) (distance float :offset 12) ) :size-assert #x20 (deftype drawable-inline-array-node (drawable-inline-array) ((data draw-node 1 :inline) (pad uint32) ) :method-count-assert 18 :size-assert #x44 */ size_t generate_pat_array(DataObjectGenerator& gen, const std::vector& pats) { gen.align_to_basic(); size_t result = gen.current_offset_bytes(); for (auto& pat : pats) { gen.add_word(pat.val); } return result; } size_t generate_packed_collide_data(DataObjectGenerator& gen, const std::vector& data) { gen.align_to_basic(); size_t result = gen.current_offset_bytes(); ASSERT((data.size() % 4) == 0); for (size_t i = 0; i < data.size(); i += 4) { u32 word; memcpy(&word, data.data() + i, 4); gen.add_word(word); } return result; } size_t generate_collide_frag_mesh(DataObjectGenerator& gen, const CollideFragMeshData& mesh, size_t packed_data_loc, size_t pat_array_loc) { gen.align_to_basic(); gen.add_type_tag("collide-frag-mesh"); // 0 size_t result = gen.current_offset_bytes(); gen.link_word_to_byte(gen.add_word(0), packed_data_loc); // 4 gen.link_word_to_byte(gen.add_word(0), pat_array_loc); // 8 gen.add_word(mesh.strip_data_len | (mesh.poly_count << 16)); // 12 gen.add_word(mesh.base_trans_xyz_s32.x()); // 16 gen.add_word(mesh.base_trans_xyz_s32.y()); // 20 gen.add_word(mesh.base_trans_xyz_s32.z()); // 24 u32 packed = 0; packed |= mesh.vertex_count; packed |= ((u32)mesh.vertex_data_qwc) << 8; packed |= ((u32)mesh.total_qwc) << 16; gen.add_word(packed); // 28 return result; } size_t generate_collide_fragment(DataObjectGenerator& gen, const CollideFragMeshData& mesh, size_t frag_mesh_loc) { /* .type collide-fragment .word 0x10000 .word L705 .word 0x0 .word 0x46bf480a .word 0x43dc730b .word 0xb71ed4fe .word 0x46c42e44 */ gen.align_to_basic(); gen.add_type_tag("collide-fragment"); size_t result = gen.current_offset_bytes(); gen.add_word(0x10000); // ??? gen.link_word_to_byte(gen.add_word(0), frag_mesh_loc); gen.add_word(0); for (int i = 0; i < 4; i++) { gen.add_word_float(mesh.bsphere[i]); } return result; } size_t generate_collide_fragment_array(DataObjectGenerator& gen, const std::vector& meshes, const std::vector& frag_mesh_locs, std::vector& loc_out) { gen.align_to_basic(); gen.add_type_tag("drawable-inline-array-collide-fragment"); // 0 size_t result = gen.current_offset_bytes(); ASSERT(meshes.size() < UINT16_MAX); gen.add_word(meshes.size() << 16); // 4, 6 gen.add_word(0); // 8 gen.add_word(0); // 12 gen.add_word(0); // 16 gen.add_word(0); // 20 gen.add_word(0); // 24 gen.add_word(0); // 28 ASSERT(meshes.size() == frag_mesh_locs.size()); for (size_t i = 0; i < meshes.size(); i++) { auto& mesh = meshes[i]; // should be 8 words here: gen.add_type_tag("collide-fragment"); // 1 size_t me = gen.current_offset_bytes(); gen.add_word(0x10000); // ??? gen.link_word_to_byte(gen.add_word(0), frag_mesh_locs[i]); gen.add_word(0); for (int j = 0; j < 4; j++) { gen.add_word_float(mesh.bsphere[j]); } loc_out.push_back(me); } return result; } int child_count(const collide::DrawNode* node) { if (node->frag_children.empty()) { return node->draw_node_children.size(); } else { return node->frag_children.size(); } } std::unordered_map add_draw_nodes( DataObjectGenerator& gen, const std::vector& nodes, const std::vector& frag_locs, size_t& array_out) { std::unordered_map result; std::unordered_map back_map; gen.align_to_basic(); gen.add_type_tag("drawable-inline-array-node"); // 0 array_out = gen.current_offset_bytes(); ASSERT(nodes.size() < UINT16_MAX); gen.add_word(nodes.size() << 16); // 4, 6 gen.add_word(0); // 8 gen.add_word(0); // 12 gen.add_word(0); // 16 gen.add_word(0); // 20 gen.add_word(0); // 24 gen.add_word(0); // 28 for (auto& node : nodes) { bool is_draw_node = node->frag_children.empty(); // should be 8 words here: gen.add_type_tag("draw-node"); // 1 size_t me = gen.current_offset_bytes(); u32 packed_flags = 0; packed_flags |= ((is_draw_node ? node->draw_node_children.size() : node->frag_children.size()) << 16); packed_flags |= ((is_draw_node ? 1 : 0) << 24); gen.add_word(packed_flags); // 2 if (is_draw_node) { // gen.link_word_to_byte(gen.add_word(0), result.at(&node->draw_node_children.at(0))); // 3 back_map[gen.add_word(0)] = &node->draw_node_children.at(0); } else { gen.link_word_to_byte(gen.add_word(0), frag_locs.at(node->frag_children.at(0))); } result[node] = me; gen.add_word(0); // 4 gen.add_word_float(node->bsphere.x()); // 5 gen.add_word_float(node->bsphere.y()); // 6 gen.add_word_float(node->bsphere.z()); // 7 gen.add_word_float(node->bsphere.w()); // 8 } for (const auto& [loc, node] : back_map) { gen.link_word_to_byte(loc, result.at(node)); } return result; } std::vector bfs_nodes(const collide::DrawNode& fake_root) { std::vector out; std::unordered_set added; std::vector frontier; for (auto& dnc : fake_root.draw_node_children) { frontier.push_back(&dnc); } while (!frontier.empty()) { std::vector next_frontier; for (auto x : frontier) { if (added.find(x) != added.end()) { continue; } added.insert(x); out.push_back(x); for (auto& child : x->draw_node_children) { if (added.find(&child) == added.end()) { next_frontier.push_back(&child); } } } frontier = std::move(next_frontier); } return out; } size_t DrawableTreeCollideFragment::add_to_object_file(DataObjectGenerator& gen) const { // generate pat array size_t pat_array_loc = generate_pat_array(gen, packed_frags.pats); // generated packed data std::vector packed_data_locs; for (auto& mesh : packed_frags.packed_frag_data) { packed_data_locs.push_back(generate_packed_collide_data(gen, mesh.packed_data)); } // generate collide frag meshes std::vector collide_frag_meshes; for (size_t i = 0; i < packed_data_locs.size(); i++) { collide_frag_meshes.push_back(generate_collide_frag_mesh(gen, packed_frags.packed_frag_data[i], packed_data_locs[i], pat_array_loc)); } std::vector children_refs; generate_collide_fragment_array(gen, packed_frags.packed_frag_data, collide_frag_meshes, children_refs); auto order = bfs_nodes(bvh.fake_root_node); size_t others_array; auto located_nodes = add_draw_nodes(gen, order, children_refs, others_array); size_t root_dian = -1; { gen.align_to_basic(); gen.add_type_tag("drawable-inline-array-node"); // 0 root_dian = gen.current_offset_bytes(); gen.add_word(bvh.fake_root_node.draw_node_children.size() << 16); // 4, 6 gen.add_word(0); // 8 gen.add_word(0); // 12 gen.add_word(0); // 16 gen.add_word(0); // 20 gen.add_word(0); // 24 gen.add_word(0); // 28 for (auto& node : bvh.fake_root_node.draw_node_children) { bool is_draw_node = node.frag_children.empty(); // should be 8 words here: gen.add_type_tag("draw-node"); // 1 u32 packed_flags = 0; packed_flags |= ((is_draw_node ? node.draw_node_children.size() : node.frag_children.size()) << 16); packed_flags |= ((is_draw_node ? 1 : 0) << 24); gen.add_word(packed_flags); // 2 if (is_draw_node) { gen.link_word_to_byte(gen.add_word(0), located_nodes.at(&node.draw_node_children.at(0))); // 3 } else { gen.link_word_to_byte(gen.add_word(0), children_refs.at(node.frag_children.at(0))); } gen.add_word(0); // 4 gen.add_word_float(node.bsphere.x()); // 5 gen.add_word_float(node.bsphere.y()); // 6 gen.add_word_float(node.bsphere.z()); // 7 gen.add_word_float(node.bsphere.w()); // 8 } } { gen.align_to_basic(); gen.add_type_tag("drawable-tree-collide-fragment"); size_t result = gen.current_offset_bytes(); gen.add_word(2 << 16); // todo the minus one here?? for (int i = 0; i < 6; i++) { gen.add_word(0); } gen.link_word_to_byte(gen.add_word(0), root_dian); gen.link_word_to_byte(gen.add_word(0), others_array); return result; } }