diff --git a/common/custom_data/TFrag3Data.cpp b/common/custom_data/TFrag3Data.cpp index 8a6fd4ba2..730f6196b 100644 --- a/common/custom_data/TFrag3Data.cpp +++ b/common/custom_data/TFrag3Data.cpp @@ -10,6 +10,13 @@ void PackedTieVertices::serialize(Serializer& ser) { ser.from_pod_vector(&vertices); } +void PackedShrubVertices::serialize(Serializer& ser) { + ser.from_pod_vector(&matrices); + ser.from_pod_vector(&instance_groups); + ser.from_pod_vector(&vertices); + ser.from_ptr(&total_vertex_count); +} + void StripDraw::serialize(Serializer& ser) { ser.from_ptr(&mode); ser.from_ptr(&tree_tex_id); @@ -28,6 +35,13 @@ void StripDraw::unpack() { } } +void ShrubDraw::serialize(Serializer& ser) { + ser.from_ptr(&mode); + ser.from_ptr(&tree_tex_id); + ser.from_pod_vector(&vertex_index_stream); + ser.from_ptr(&num_triangles); +} + void InstancedStripDraw::serialize(Serializer& ser) { ser.from_ptr(&mode); ser.from_ptr(&tree_tex_id); @@ -97,6 +111,29 @@ void TieTree::unpack() { } } +void ShrubTree::unpack() { + unpacked.vertices.resize(packed_vertices.total_vertex_count); + size_t i = 0; + + for (const auto& grp : packed_vertices.instance_groups) { + const auto& mat = packed_vertices.matrices[grp.matrix_idx]; + for (u32 src_idx = grp.start_vert; src_idx < grp.end_vert; src_idx++) { + auto& vtx = unpacked.vertices[i]; + vtx.color_index = grp.color_index; + const auto& proto_vtx = packed_vertices.vertices[src_idx]; + auto temp = mat[0] * proto_vtx.x + mat[1] * proto_vtx.y + mat[2] * proto_vtx.z + mat[3]; + vtx.x = temp.x(); + vtx.y = temp.y(); + vtx.z = temp.z(); + vtx.s = proto_vtx.s; + vtx.t = proto_vtx.t; + memcpy(vtx.rgba_base, proto_vtx.rgba, 3); + i++; + } + } + ASSERT(i == unpacked.vertices.size()); +} + void TfragTree::unpack() { unpacked.vertices.resize(packed_vertices.vertices.size()); for (size_t i = 0; i < unpacked.vertices.size(); i++) { @@ -152,6 +189,19 @@ void TieTree::serialize(Serializer& ser) { bvh.serialize(ser); } +void ShrubTree::serialize(Serializer& ser) { + ser.from_pod_vector(&time_of_day_colors); + packed_vertices.serialize(ser); + if (ser.is_saving()) { + ser.save(static_draws.size()); + } else { + static_draws.resize(ser.load()); + } + for (auto& draw : static_draws) { + draw.serialize(ser); + } +} + void BVH::serialize(Serializer& ser) { ser.from_ptr(&first_leaf_node); ser.from_ptr(&last_leaf_node); @@ -212,6 +262,15 @@ void Level::serialize(Serializer& ser) { } } + if (ser.is_saving()) { + ser.save(shrub_trees.size()); + } else { + shrub_trees.resize(ser.load()); + } + for (auto& tree : shrub_trees) { + tree.serialize(ser); + } + ser.from_ptr(&version2); if (ser.is_loading() && version2 != TFRAG3_VERSION) { fmt::print("version mismatch when loading tfrag3 data (at end). Got {}, expected {}\n", @@ -271,6 +330,20 @@ std::array Level::get_memory_usage() c } } + // shrub + for (const auto& shrub_tree : shrub_trees) { + result[SHRUB_TIME_OF_DAY] += shrub_tree.time_of_day_colors.size() * sizeof(TimeOfDayColor); + result[SHRUB_VERT] += shrub_tree.packed_vertices.matrices.size() * 4 * 4 * 4; + result[SHRUB_VERT] += + shrub_tree.packed_vertices.vertices.size() * sizeof(PackedShrubVertices::Vertex); + result[SHRUB_VERT] += shrub_tree.packed_vertices.instance_groups.size() * + sizeof(PackedShrubVertices::InstanceGroup); + + for (const auto& draw : shrub_tree.static_draws) { + result[SHRUB_IND] += sizeof(u32) * draw.vertex_index_stream.size(); + } + } + return result; } diff --git a/common/custom_data/Tfrag3Data.h b/common/custom_data/Tfrag3Data.h index 791fa5858..566c8e83d 100644 --- a/common/custom_data/Tfrag3Data.h +++ b/common/custom_data/Tfrag3Data.h @@ -40,10 +40,14 @@ enum MemoryUsageCategory { TFRAG_CLUSTER, TFRAG_TIME_OF_DAY, TFRAG_BVH, + + SHRUB_TIME_OF_DAY, + SHRUB_VERT, + SHRUB_IND, NUM_CATEGORIES }; -constexpr int TFRAG3_VERSION = 12; +constexpr int TFRAG3_VERSION = 13; // These vertices should be uploaded to the GPU at load time and don't change struct PreloadedVertex { @@ -73,7 +77,6 @@ struct PackedTieVertices { std::vector> matrices; std::vector matrix_groups; // todo pack std::vector vertices; - float cluster_size = 0; void serialize(Serializer& ser); }; @@ -83,29 +86,44 @@ struct PackedTfragVertices { u16 cluster_idx; u16 s, t; u16 color_index; - - /* - bool operator==(const Vertex& other) const { - return xoff == other.xoff && yoff == other.yoff && zoff == other.zoff && - cluster_idx == other.cluster_idx && s == other.s && t == other.t && - color_index == other.color_index; - } - - struct hash { - auto operator()(const Vertex& x) const { - return std::hash()(x.xoff) ^ std::hash()(x.yoff) ^ - std::hash()(x.zoff) ^ std::hash()(x.cluster_idx) ^ - std::hash()(x.s) ^ std::hash()(x.t) ^ - std::hash()(x.color_index); - } - }; - */ }; std::vector vertices; std::vector> cluster_origins; }; +struct ShrubGpuVertex { + float x, y, z; + float s, t; + u32 pad0; + u16 color_index; + u16 pad1; + u8 rgba_base[3]; + u8 pad2; +}; +static_assert(sizeof(ShrubGpuVertex) == 32, "ShrubGpuVertex size"); + +struct PackedShrubVertices { + struct Vertex { + float x, y, z; + float s, t; + u8 rgba[3]; + }; + + struct InstanceGroup { + s32 matrix_idx; + u32 start_vert; + u32 end_vert; + u16 color_index; + }; + std::vector> matrices; + std::vector instance_groups; // todo pack + std::vector vertices; + u32 total_vertex_count; + + void serialize(Serializer& ser); +}; + // Settings for drawing a group of triangle strips. // This refers to a group of PreloadedVertices that are already uploaded. // All triangles here are drawn in the same "mode" (blending, texture, etc) @@ -144,6 +162,19 @@ struct StripDraw { void serialize(Serializer& ser); }; +struct ShrubDraw { + DrawMode mode; // the OpenGL draw settings. + u32 tree_tex_id = 0; // the texture that should be bound for the draw + + // the list of vertices in the draw. This includes the restart code of UINT32_MAX that OpenGL + // will use to start a new strip. + std::vector vertex_index_stream; + + // for debug counting. + u32 num_triangles = 0; + void serialize(Serializer& ser); +}; + struct InstancedStripDraw { DrawMode mode; // the OpenGL draw settings. u32 tree_tex_id = 0; // the texture that should be bound for the draw @@ -261,6 +292,21 @@ struct TieTree { void unpack(); }; +struct ShrubTree { + // todo some visibility structure + std::vector time_of_day_colors; // multiplier colors + + PackedShrubVertices packed_vertices; + std::vector static_draws; // the actual topology and settings + + struct { + std::vector vertices; // mesh vertices + } unpacked; + + void serialize(Serializer& ser); + void unpack(); +}; + constexpr int TFRAG_GEOS = 3; constexpr int TIE_GEOS = 4; @@ -270,6 +316,7 @@ struct Level { std::vector textures; std::array, TFRAG_GEOS> tfrag_trees; std::array, TIE_GEOS> tie_trees; + std::vector shrub_trees; u16 version2 = TFRAG3_VERSION; void serialize(Serializer& ser); diff --git a/decompiler/config/all-types.gc b/decompiler/config/all-types.gc index aefe2fb95..0ccf42685 100644 --- a/decompiler/config/all-types.gc +++ b/decompiler/config/all-types.gc @@ -478,33 +478,32 @@ :type int32 :bitfield #f - (bucket-1 1) - (bucket-2 2) - (sky-draw 3) - (bucket-4 4) ;; ocean - (tfrag-tex0 5) - (tfrag-0 6) - (tfrag-near-0 7) - (tie-near-0 8) - (tie-0 9) - ;; merc0 10 - ;; generic0 11 - (bucket-10 10) - (bucket-11 11) + (bucket-0 0) ;; ? + (bucket-1 1) ;; ? + (bucket-2 2) ;; ? + (sky-draw 3) ;; actual sky and cloud framebuffer draws + (ocean-mid-and-far 4) ;; actual ocean framebuffer draws for mid/transition/far - (tfrag-tex1 12) - (tfrag-1 13) - (tfrag-near-1 14) - (tie-near-1 15) - (tie-1 16) - ;; merc1 17 - ;; generic1 18 - (bucket-17 17) - (bucket-18 18) + (tfrag-tex0 5) ;; tfrag texture upload, level 0 + (tfrag-0 6) ;; tfrag draw, level 0 + (tfrag-near-0 7) ;; tfrag near draw, level 0 + (tie-near-0 8) ;; tie near draw, level 0 + (tie-0 9) ;; tie draw, level 0 + (merc-tfrag-tex0 10) ;; merc, with tfrag textures, level 0 + (gmerc-tfrag-tex 11) ;; generic merc, with tfrag textures, level 0 + (tfrag-tex1 12) ;; tfrag texture upload, level 1 + (tfrag-1 13) ;; tfrag draw, level 1 + (tfrag-near-1 14) ;; tfrag near draw, level 1 + (tie-near-1 15) ;; tie near draw, level 1 + (tie-1 16) ;; tie draw, level 1 + (merc-tfrag-tex1 17) ;; merc, with tfrag textures, level 1 + (gmerc-tfrag-tex1 18) ;; generic merc, with tfrag textures, level 1 (shrub-tex0 19) - + (shrub0 20) + (shrub-tex1 25) + (shrub1 26) (generic-foreground 30) ;; ? (alpha-tex0 31) diff --git a/decompiler/level_extractor/BspHeader.cpp b/decompiler/level_extractor/BspHeader.cpp index ea1b42ac0..9b9c75b4a 100644 --- a/decompiler/level_extractor/BspHeader.cpp +++ b/decompiler/level_extractor/BspHeader.cpp @@ -1515,8 +1515,7 @@ std::string Shrubbery::print(const level_tools::PrintSettings& /*settings*/, int std::string GenericShrubFragment::print(const level_tools::PrintSettings& /*settings*/, int indent) const { std::string is(indent, ' '); - return fmt::format("{} qwcs: {} {} {} {}: 0x{:x}\n", is, cnt_qwc, vtx_qwc, col_qwc, stq_qwc, - vtx_cnt); + return fmt::format("{} qwcs: {} {} {} {}: {}\n", is, cnt_qwc, vtx_qwc, col_qwc, stq_qwc, vtx_cnt); } void GenericShrubFragment::read_from_file(TypedRef ref, diff --git a/decompiler/level_extractor/extract_level.cpp b/decompiler/level_extractor/extract_level.cpp index 9a7c985b1..39c6fb7e2 100644 --- a/decompiler/level_extractor/extract_level.cpp +++ b/decompiler/level_extractor/extract_level.cpp @@ -72,7 +72,10 @@ void print_memory_usage(const tfrag3::Level& lev, int uncompressed_data_size) { {"tfrag-vert", memory_use_by_category[tfrag3::MemoryUsageCategory::TFRAG_VERTS]}, {"tfrag-colors", memory_use_by_category[tfrag3::MemoryUsageCategory::TFRAG_TIME_OF_DAY]}, {"tfrag-cluster", memory_use_by_category[tfrag3::MemoryUsageCategory::TFRAG_CLUSTER]}, - {"tfrag-bvh", memory_use_by_category[tfrag3::MemoryUsageCategory::TFRAG_BVH]}}; + {"tfrag-bvh", memory_use_by_category[tfrag3::MemoryUsageCategory::TFRAG_BVH]}, + {"shrub-colors", memory_use_by_category[tfrag3::MemoryUsageCategory::SHRUB_TIME_OF_DAY]}, + {"shrub-vert", memory_use_by_category[tfrag3::MemoryUsageCategory::SHRUB_VERT]}, + {"shrub-ind", memory_use_by_category[tfrag3::MemoryUsageCategory::SHRUB_IND]}}; for (auto& known : known_categories) { total_accounted += known.second; } diff --git a/decompiler/level_extractor/extract_shrub.cpp b/decompiler/level_extractor/extract_shrub.cpp index 06a4c9491..fa6ecf642 100644 --- a/decompiler/level_extractor/extract_shrub.cpp +++ b/decompiler/level_extractor/extract_shrub.cpp @@ -33,13 +33,17 @@ std::array extract_shrub_matrix(const u16* data) { struct ShrubVertex { math::Vector xyz; math::Vector st; - math::Vector rgba_generic; + math::Vector rgba_generic; bool adc = false; }; - +struct DrawSettings { + DrawMode mode; + u32 combo_tex; +}; struct ShrubDraw { u32 start_vtx_idx = -1; AdGifData adgif; + DrawSettings settings; std::vector vertices; }; @@ -116,7 +120,127 @@ std::string debug_dump_proto_to_obj(const ShrubProtoInfo& proto) { return result; } -ShrubProtoInfo extract_proto(const shrub_types::PrototypeBucketShrub& proto) { +namespace { +/*! + * adgif shader texture id's can be "remapped". I think it allows textures to be shared. + * So far we haven't seen this feature used, but we do have the texture map and we check it here. + */ +u32 remap_texture(u32 original, const std::vector& map) { + auto masked = original & 0xffffff00; + for (auto& t : map) { + if (t.original_texid == masked) { + fmt::print("OKAY! remapped!\n"); + ASSERT(false); + return t.new_texid | 20; + } + } + return original; +} +} // namespace + +DrawSettings adgif_to_draw_mode(const AdGifData& ad, + const TextureDB& tdb, + const std::vector& map, + int count) { + // initialize draw mode + DrawMode current_mode; + current_mode.set_at(true); + current_mode.set_alpha_test(DrawMode::AlphaTest::GEQUAL); + current_mode.set_aref(0x26); + current_mode.set_alpha_fail(GsTest::AlphaFail::KEEP); + current_mode.set_zt(true); + current_mode.set_depth_test(GsTest::ZTest::GEQUAL); + current_mode.set_depth_write_enable(true); // todo, is this actual true + current_mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_SRC_SRC_SRC); + current_mode.enable_fog(); + + // ADGIF 0 + bool weird = (u8)ad.tex0_addr != (u32)GsRegisterAddress::TEX0_1; + if (weird) { + fmt::print("---------------- WEIRD: 0x{:x}\n", ad.tex0_addr); + fmt::print("i have {} verts\n", count); + } else { + ASSERT(ad.tex0_data == 0 || ad.tex0_data == 0x800000000); // note: decal?? todo + } + + // tw/th + + // ADGIF 1 + ASSERT((u8)ad.tex1_addr == (u32)GsRegisterAddress::TEX1_1); + u32 original_tex = ad.tex1_addr; + u32 new_tex = remap_texture(original_tex, map); + // try remapping it + if (original_tex != new_tex) { + fmt::print("map from 0x{:x} to 0x{:x}\n", original_tex, new_tex); + } + // texture the texture page/texture index, and convert to a PC port texture ID + u32 tpage = new_tex >> 20; + u32 tidx = (new_tex >> 8) & 0b1111'1111'1111; + u32 tex_combo = (((u32)tpage) << 16) | tidx; + // look up the texture to make sure it's valid + auto tex = tdb.textures.find(tex_combo); + ASSERT(tex != tdb.textures.end()); + if (weird) { + fmt::print("tex: {}\n", tex->second.name); + } + + // ADGIF 2 + ASSERT((u8)ad.mip_addr == (u32)GsRegisterAddress::MIPTBP1_1); + + // ADGIF 3 + ASSERT((u8)ad.clamp_addr == (u32)GsRegisterAddress::CLAMP_1); + { + bool clamp_s = ad.clamp_data & 0b001; + bool clamp_t = ad.clamp_data & 0b100; + current_mode.set_clamp_s_enable(clamp_s); + current_mode.set_clamp_t_enable(clamp_t); + } + + u64 final_alpha; + + // ADGIF 4 + if ((u8)ad.alpha_addr == (u32)GsRegisterAddress::ALPHA_1) { + final_alpha = ad.alpha_data; + } else { + ASSERT(false); + // ASSERT((u8)ad.alpha_addr == (u32)GsRegisterAddress::MIPTBP2_1); + } + + GsAlpha reg(final_alpha); + auto a = reg.a_mode(); + auto b = reg.b_mode(); + auto c = reg.c_mode(); + auto d = reg.d_mode(); + if (a == GsAlpha::BlendMode::SOURCE && b == GsAlpha::BlendMode::DEST && + c == GsAlpha::BlendMode::SOURCE && d == GsAlpha::BlendMode::DEST) { + current_mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_DST_SRC_DST); + } else if (a == GsAlpha::BlendMode::SOURCE && b == GsAlpha::BlendMode::ZERO_OR_FIXED && + c == GsAlpha::BlendMode::SOURCE && d == GsAlpha::BlendMode::DEST) { + current_mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_0_SRC_DST); + } else if (a == GsAlpha::BlendMode::ZERO_OR_FIXED && b == GsAlpha::BlendMode::SOURCE && + c == GsAlpha::BlendMode::SOURCE && d == GsAlpha::BlendMode::DEST) { + current_mode.set_alpha_blend(DrawMode::AlphaBlend::ZERO_SRC_SRC_DST); + } else if (a == GsAlpha::BlendMode::SOURCE && b == GsAlpha::BlendMode::DEST && + c == GsAlpha::BlendMode::ZERO_OR_FIXED && d == GsAlpha::BlendMode::DEST) { + current_mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_DST_FIX_DST); + } else if (a == GsAlpha::BlendMode::SOURCE && b == GsAlpha::BlendMode::SOURCE && + c == GsAlpha::BlendMode::SOURCE && d == GsAlpha::BlendMode::SOURCE) { + current_mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_SRC_SRC_SRC); + } else if (a == GsAlpha::BlendMode::SOURCE && b == GsAlpha::BlendMode::ZERO_OR_FIXED && + c == GsAlpha::BlendMode::DEST && d == GsAlpha::BlendMode::DEST) { + current_mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_0_DST_DST); + } else { + // unsupported blend: a 0 b 2 c 2 d 1 + // lg::error("unsupported blend: a {} b {} c {} d {}", (int)a, (int)b, (int)c, (int)d); + // ASSERT(false); + } + + return {current_mode, tex_combo}; +} + +ShrubProtoInfo extract_proto(const shrub_types::PrototypeBucketShrub& proto, + const TextureDB& tdb, + const std::vector& map) { ShrubProtoInfo result; for (int frag_idx = 0; frag_idx < proto.generic_geom.length; frag_idx++) { auto& frag_out = result.frags.emplace_back(); @@ -125,8 +249,15 @@ ShrubProtoInfo extract_proto(const shrub_types::PrototypeBucketShrub& proto) { std::vector adgif_data; adgif_data.resize(frag.textures.size() / sizeof(AdGifData)); memcpy(adgif_data.data(), frag.textures.data(), frag.textures.size()); + + if (frag_idx == 0 && proto.name == "vil2-cattail.mb") { + fmt::print("Skipping broken village2 thing\n"); + continue; + } + for (size_t i = 0; i < adgif_data.size(); i++) { auto& draw = frag_out.draws.emplace_back(); + draw.adgif = adgif_data[i]; const auto& ag = adgif_data[i]; int count = (ag.tex1_addr >> 32) & 0xfff; // drop the eop flag @@ -144,15 +275,18 @@ ShrubProtoInfo extract_proto(const shrub_types::PrototypeBucketShrub& proto) { 3 * sizeof(u16)); vert_out.xyz = math::Vector3f(vert_data[0], vert_data[1], vert_data[2]); - u16 st_data[2]; + s16 st_data[2]; memcpy(st_data, frag.stq.data() + sizeof(u16) * 2 * (vert_idx + draw.start_vtx_idx), 2 * sizeof(u16)); vert_out.st = math::Vector2f(st_data[0], st_data[1]); vert_out.adc = (st_data[0] & 1) == 0; // adc in the low bit of texture coordinate - memcpy(vert_out.rgba_generic.data(), frag.col.data() + 4 * (vert_idx + draw.start_vtx_idx), - 4); + memcpy(vert_out.rgba_generic.data(), frag.col.data() + 3 * (vert_idx + draw.start_vtx_idx), + 3); + ASSERT(3 * (vert_idx + draw.start_vtx_idx) + 3 <= frag.col.size()); } + + draw.settings = adgif_to_draw_mode(ag, tdb, map, count); } ASSERT(frag.vtx_cnt * 3 * sizeof(u16) <= frag.vtx.size()); @@ -182,6 +316,7 @@ void extract_instance(const shrub_types::InstanceShrubbery& inst, // result.wind_index = instance.wind_index; result.mat[0][3] = 0.f; + result.color_idx = inst.color_indices / 4; protos.at(result.proto_idx).instances.push_back(result); } @@ -261,17 +396,200 @@ std::string dump_full_to_obj(const std::vector& protos) { return result; } +u32 clean_up_vertex_indices(std::vector& idx) { + std::vector fixed; + u32 num_tris = 0; + + bool looking_for_start = true; + size_t i_of_start; + for (size_t i = 0; i < idx.size(); i++) { + if (looking_for_start) { + if (idx[i] != UINT32_MAX) { + looking_for_start = false; + i_of_start = i; + } + } else { + if (idx[i] == UINT32_MAX) { + looking_for_start = true; + size_t num_verts = i - i_of_start; + if (num_verts >= 3) { + if (!fixed.empty()) { + fixed.push_back(UINT32_MAX); + } + fixed.insert(fixed.end(), idx.begin() + i_of_start, idx.begin() + i); + num_tris += (num_verts - 2); + } + } + } + } + + if (!looking_for_start) { + size_t num_verts = idx.size() - i_of_start; + if (num_verts >= 3) { + if (!fixed.empty()) { + fixed.push_back(UINT32_MAX); + } + fixed.insert(fixed.end(), idx.begin() + i_of_start, idx.begin() + idx.size()); + num_tris += (num_verts - 2); + } + } + + idx = std::move(fixed); + + return num_tris; +} + +void make_draws(tfrag3::Level& lev, + tfrag3::ShrubTree& tree_out, + const std::vector& protos, + const TextureDB& tdb) { + std::unordered_map> static_draws_by_tex; + size_t global_vert_counter = 0; + for (auto& proto : protos) { + // packed_vert_indices[frag][draw] = {start, end} + std::vector>> packed_vert_indices; + + for (size_t frag_idx = 0; frag_idx < proto.frags.size(); frag_idx++) { + auto& frag_inds = packed_vert_indices.emplace_back(); + auto& frag = proto.frags[frag_idx]; + + for (auto& draw : frag.draws) { + int start = tree_out.packed_vertices.vertices.size(); + for (auto& vert : draw.vertices) { + tree_out.packed_vertices.vertices.push_back( + {vert.xyz.x(), + vert.xyz.y(), + vert.xyz.z(), + vert.st.x(), + vert.st.y(), + {vert.rgba_generic[0], vert.rgba_generic[1], vert.rgba_generic[2]}}); + } + int end = tree_out.packed_vertices.vertices.size(); + frag_inds.emplace_back(start, end); + } + } + + for (auto& inst : proto.instances) { + u32 matrix_idx = tree_out.packed_vertices.matrices.size(); + tree_out.packed_vertices.matrices.push_back(inst.mat); + + for (size_t frag_idx = 0; frag_idx < proto.frags.size(); frag_idx++) { + auto& frag = proto.frags[frag_idx]; // shared info for all instances of this frag + + for (size_t draw_idx = 0; draw_idx < frag.draws.size(); draw_idx++) { + auto& draw = frag.draws[draw_idx]; + + // what texture are we using? + u32 combo_tex = draw.settings.combo_tex; + + // try looking it up in the existing textures that we have in the C++ renderer data. + // (this is shared with tfrag) + u32 idx_in_lev_data = UINT32_MAX; + for (u32 i = 0; i < lev.textures.size(); i++) { + if (lev.textures[i].combo_id == combo_tex) { + idx_in_lev_data = i; + break; + } + } + + if (idx_in_lev_data == UINT32_MAX) { + // didn't find it, have to add a new one texture. + auto tex_it = tdb.textures.find(combo_tex); + if (tex_it == tdb.textures.end()) { + bool ok_to_miss = false; // for TIE, there's no missing textures. + if (ok_to_miss) { + // we're missing a texture, just use the first one. + tex_it = tdb.textures.begin(); + } else { + fmt::print( + "texture {} wasn't found. make sure it is loaded somehow. You may need to " + "include " + "ART.DGO or GAME.DGO in addition to the level DGOs for shared textures.\n", + combo_tex); + fmt::print("tpage is {}\n", combo_tex >> 16); + fmt::print("id is {} (0x{:x})\n", combo_tex & 0xffff, combo_tex & 0xffff); + ASSERT(false); + } + } + // add a new texture to the level data + idx_in_lev_data = lev.textures.size(); + lev.textures.emplace_back(); + auto& new_tex = lev.textures.back(); + new_tex.combo_id = combo_tex; + new_tex.w = tex_it->second.w; + new_tex.h = tex_it->second.h; + new_tex.debug_name = tex_it->second.name; + new_tex.debug_tpage_name = tdb.tpage_names.at(tex_it->second.page); + new_tex.data = tex_it->second.rgba_bytes; + } + + DrawMode mode = draw.settings.mode; + + // okay, we now have a texture and draw mode, let's see if we can add to an existing... + auto existing_draws_in_tex = static_draws_by_tex.find(idx_in_lev_data); + tfrag3::ShrubDraw* draw_to_add_to = nullptr; + if (existing_draws_in_tex != static_draws_by_tex.end()) { + for (auto idx : existing_draws_in_tex->second) { + if (tree_out.static_draws.at(idx).mode == mode) { + draw_to_add_to = &tree_out.static_draws[idx]; + } + } + } + + if (!draw_to_add_to) { + // nope, need to create a new draw + tree_out.static_draws.emplace_back(); + static_draws_by_tex[idx_in_lev_data].push_back(tree_out.static_draws.size() - 1); + draw_to_add_to = &tree_out.static_draws.back(); + draw_to_add_to->mode = mode; + draw_to_add_to->tree_tex_id = idx_in_lev_data; + } + + // now we have a draw, time to add vertices + // draw_to_add_to->num_triangles += draw.vertices.size() - 2; + tfrag3::PackedShrubVertices::InstanceGroup grp; + grp.matrix_idx = matrix_idx; + grp.color_index = inst.color_idx; + grp.start_vert = packed_vert_indices.at(frag_idx).at(draw_idx).first; + grp.end_vert = packed_vert_indices.at(frag_idx).at(draw_idx).second; + tree_out.packed_vertices.instance_groups.push_back(grp); + + for (size_t vidx = 0; vidx < draw.vertices.size(); vidx++) { + if (draw.vertices[vidx].adc) { + draw_to_add_to->vertex_index_stream.push_back(vidx + global_vert_counter); + draw_to_add_to->num_triangles++; + } else { + draw_to_add_to->vertex_index_stream.push_back(UINT32_MAX); + draw_to_add_to->vertex_index_stream.push_back(vidx + global_vert_counter - 1); + draw_to_add_to->vertex_index_stream.push_back(vidx + global_vert_counter); + } + } + draw_to_add_to->vertex_index_stream.push_back(UINT32_MAX); + + global_vert_counter += draw.vertices.size(); + } + } + } + } + + for (auto& draw : tree_out.static_draws) { + draw.num_triangles = clean_up_vertex_indices(draw.vertex_index_stream); + } + tree_out.packed_vertices.total_vertex_count = global_vert_counter; +} + void extract_shrub(const shrub_types::DrawableTreeInstanceShrub* tree, const std::string& debug_name, - const std::vector& /*map*/, - const TextureDB& /*tex_db*/, + const std::vector& map, + const TextureDB& tex_db, const std::vector>& /*expected_missing_textures*/, - tfrag3::Level& /*out*/, + tfrag3::Level& out, bool dump_level) { + auto& tree_out = out.shrub_trees.emplace_back(); auto& protos = tree->info.prototype_inline_array_shrub; std::vector proto_info; for (auto& proto : protos.data) { - proto_info.push_back(extract_proto(proto)); + proto_info.push_back(extract_proto(proto, tex_db, map)); } for (auto& arr : tree->discovered_arrays) { @@ -283,6 +601,17 @@ void extract_shrub(const shrub_types::DrawableTreeInstanceShrub* tree, } } + // time of day colors + tree_out.time_of_day_colors.resize(tree->time_of_day.height); + for (int k = 0; k < (int)tree->time_of_day.height; k++) { + for (int j = 0; j < 8; j++) { + memcpy(tree_out.time_of_day_colors[k].rgba[j].data(), &tree->time_of_day.colors[k * 8 + j], + 4); + } + } + + make_draws(out, tree_out, proto_info, tex_db); + if (dump_level) { auto path = file_util::get_file_path({fmt::format("debug_out/shrub_all/{}.obj", debug_name)}); file_util::create_dir_if_needed_for_file(path); diff --git a/game/CMakeLists.txt b/game/CMakeLists.txt index 1967b4bd5..3e5d0d047 100644 --- a/game/CMakeLists.txt +++ b/game/CMakeLists.txt @@ -79,6 +79,7 @@ set(RUNTIME_SOURCE graphics/display.cpp graphics/sceGraphicsInterface.cpp graphics/opengl_renderer/background/background_common.cpp + graphics/opengl_renderer/background/Shrub.cpp graphics/opengl_renderer/background/Tfrag3.cpp graphics/opengl_renderer/background/TFragment.cpp graphics/opengl_renderer/background/Tie3.cpp diff --git a/game/graphics/opengl_renderer/Loader.cpp b/game/graphics/opengl_renderer/Loader.cpp index 542760288..ae353a047 100644 --- a/game/graphics/opengl_renderer/Loader.cpp +++ b/game/graphics/opengl_renderer/Loader.cpp @@ -131,6 +131,10 @@ void Loader::loader_thread() { } } } + + for (auto& shrub_tree : result->shrub_trees) { + shrub_tree.unpack(); + } fmt::print( "------------> Load from file: {:.3f}s, import {:.3f}s, decomp {:.3f}s unpack {:.3f}s\n", disk_load_time, import_time, decomp_time, unpack_timer.getSeconds()); @@ -276,6 +280,78 @@ bool Loader::init_tfrag(Timer& timer, LevelData& data) { } } +bool Loader::init_shrub(Timer& timer, LevelData& data) { + if (data.shrub_load_done) { + return true; + } + + if (data.level->shrub_trees.empty()) { + data.shrub_load_done = true; + return true; + } + + if (!data.shrub_opengl_created) { + for (auto& in_tree : data.level->shrub_trees) { + GLuint& tree_out = data.shrub_vertex_data.emplace_back(); + glGenBuffers(1, &tree_out); + glBindBuffer(GL_ARRAY_BUFFER, tree_out); + glBufferData(GL_ARRAY_BUFFER, + in_tree.unpacked.vertices.size() * sizeof(tfrag3::ShrubGpuVertex), nullptr, + GL_STATIC_DRAW); + } + data.shrub_opengl_created = true; + return false; + } + + constexpr u32 CHUNK_SIZE = 32768; + u32 uploaded_bytes = 0; + + while (true) { + const auto& tree = data.level->shrub_trees[data.shrub_next_tree]; + u32 end_vert_in_tree = tree.unpacked.vertices.size(); + // the number of vertices we'd need to finish the tree right now + size_t num_verts_left_in_tree = end_vert_in_tree - data.shrub_next_vert; + size_t start_vert_for_chunk; + size_t end_vert_for_chunk; + + bool complete_tree; + + if (num_verts_left_in_tree > CHUNK_SIZE) { + complete_tree = false; + // should only do partial + start_vert_for_chunk = data.shrub_next_vert; + end_vert_for_chunk = start_vert_for_chunk + CHUNK_SIZE; + data.shrub_next_vert += CHUNK_SIZE; + } else { + // should do all! + start_vert_for_chunk = data.shrub_next_vert; + end_vert_for_chunk = end_vert_in_tree; + complete_tree = true; + } + + // glBindVertexArray(m_trees[m_load_state.vert_geo][m_load_state.vert_tree].vao); + glBindBuffer(GL_ARRAY_BUFFER, data.shrub_vertex_data[data.shrub_next_tree]); + u32 upload_size = (end_vert_for_chunk - start_vert_for_chunk) * sizeof(tfrag3::ShrubGpuVertex); + glBufferSubData(GL_ARRAY_BUFFER, start_vert_for_chunk * sizeof(tfrag3::ShrubGpuVertex), + upload_size, tree.unpacked.vertices.data() + start_vert_for_chunk); + uploaded_bytes += upload_size; + + if (complete_tree) { + // and move on to next tree + data.shrub_next_vert = 0; + data.shrub_next_tree++; + if (data.shrub_next_tree >= data.level->shrub_trees.size()) { + data.shrub_load_done = true; + return true; + } + } + + if (timer.getMs() > Loader::TIE_LOAD_BUDGET || (uploaded_bytes / 1024) > 2048) { + return false; + } + } +} + bool Loader::init_tie(Timer& timer, LevelData& data) { if (data.tie_load_done) { return true; @@ -511,12 +587,14 @@ void Loader::update(TexturePool& texture_pool) { if (upload_textures(loader_timer, lev.data, texture_pool)) { if (init_tie(loader_timer, lev.data)) { if (init_tfrag(loader_timer, lev.data)) { - // we're done! lock before removing from loaded. - lk.lock(); - it->second.data.load_id = m_id++; + if (init_shrub(loader_timer, lev.data)) { + // we're done! lock before removing from loaded. + lk.lock(); + it->second.data.load_id = m_id++; - m_loaded_tfrag3_levels[name] = std::move(lev); - m_initializing_tfrag3_levels.erase(it); + m_loaded_tfrag3_levels[name] = std::move(lev); + m_initializing_tfrag3_levels.erase(it); + } } } } diff --git a/game/graphics/opengl_renderer/Loader.h b/game/graphics/opengl_renderer/Loader.h index adee94f30..b03f31c30 100644 --- a/game/graphics/opengl_renderer/Loader.h +++ b/game/graphics/opengl_renderer/Loader.h @@ -30,6 +30,7 @@ class Loader { }; std::array, tfrag3::TIE_GEOS> tie_data; std::array, tfrag3::TIE_GEOS> tfrag_vertex_data; + std::vector shrub_vertex_data; // internal load state bool tie_opengl_created = false; @@ -45,6 +46,11 @@ class Loader { u32 tfrag_next_geo = 0; u32 tfrag_next_tree = 0; u32 tfrag_next_vert = 0; + + bool shrub_opengl_created = false; + bool shrub_load_done = false; + u32 shrub_next_tree = 0; + u32 shrub_next_vert = 0; }; const LevelData* get_tfrag3_level(const std::string& level_name); @@ -63,6 +69,7 @@ class Loader { bool upload_textures(Timer& timer, LevelData& data, TexturePool& texture_pool); bool init_tie(Timer& timer, LevelData& data); bool init_tfrag(Timer& timer, LevelData& data); + bool init_shrub(Timer& timer, LevelData& data); // used by game and loader thread std::unordered_map m_initializing_tfrag3_levels; diff --git a/game/graphics/opengl_renderer/OpenGLRenderer.cpp b/game/graphics/opengl_renderer/OpenGLRenderer.cpp index 730018e22..340201051 100644 --- a/game/graphics/opengl_renderer/OpenGLRenderer.cpp +++ b/game/graphics/opengl_renderer/OpenGLRenderer.cpp @@ -11,6 +11,7 @@ #include "game/graphics/opengl_renderer/Sprite3.h" #include "game/graphics/opengl_renderer/background/TFragment.h" #include "game/graphics/opengl_renderer/background/Tie3.h" +#include "game/graphics/opengl_renderer/background/Shrub.h" #include "game/graphics/opengl_renderer/MercRenderer.h" #include "game/graphics/opengl_renderer/EyeRenderer.h" #include "game/graphics/opengl_renderer/ShadowRenderer.h" @@ -83,68 +84,82 @@ void OpenGLRenderer::init_bucket_renderers() { //------------- // PRE TEXTURE //------------- - // 0 - // 1 - // 2 - init_bucket_renderer("sky", BucketCategory::OTHER, BucketId::SKY_DRAW); // 3 + // 0 : ?? + // 1 : ?? + // 2 : ?? + // 3 : SKY_DRAW + init_bucket_renderer("sky", BucketCategory::OTHER, BucketId::SKY_DRAW); + // 4 : OCEAN_MID_AND_FAR init_bucket_renderer("ocean-mid-far", BucketCategory::OCEAN, - BucketId::OCEAN_MID_AND_FAR); // 4 + BucketId::OCEAN_MID_AND_FAR); //----------------------- // LEVEL 0 tfrag texture //----------------------- + // 5 : TFRAG_TEX_LEVEL0 init_bucket_renderer("l0-tfrag-tex", BucketCategory::TEX, - BucketId::TFRAG_TEX_LEVEL0); // 5 + BucketId::TFRAG_TEX_LEVEL0); + // 6 : TFRAG_LEVEL0 init_bucket_renderer("l0-tfrag-tfrag", BucketCategory::TFRAG, BucketId::TFRAG_LEVEL0, - normal_tfrags, false, - 0); // 6 - // 7 - // 8 - init_bucket_renderer("l0-tfrag-tie", BucketCategory::TIE, BucketId::TIE_LEVEL0, 0); // 9 + normal_tfrags, false, 0); + // 7 : TFRAG_NEAR_LEVEL0 + // 8 : TIE_NEAR_LEVEL0 + // 9 : TIE_LEVEL0 + init_bucket_renderer("l0-tfrag-tie", BucketCategory::TIE, BucketId::TIE_LEVEL0, 0); + // 10 : MERC_TFRAG_TEX_LEVEL0 init_bucket_renderer("l0-tfrag-merc", BucketCategory::MERC, - BucketId::MERC_TFRAG_TEX_LEVEL0); // 10 + BucketId::MERC_TFRAG_TEX_LEVEL0); + // 11 : GMERC_TFRAG_TEX_LEVEL0 init_bucket_renderer("l0-tfrag-gmerc", BucketCategory::GENERIC_MERC, - BucketId::GMERC_TFRAG_TEX_LEVEL0); // 11 + BucketId::GMERC_TFRAG_TEX_LEVEL0); //----------------------- // LEVEL 1 tfrag texture //----------------------- + // 12 : TFRAG_TEX_LEVEL1 init_bucket_renderer("l1-tfrag-tex", BucketCategory::TEX, - BucketId::TFRAG_TEX_LEVEL1); // 12 + BucketId::TFRAG_TEX_LEVEL1); + // 13 : TFRAG_LEVEL1 init_bucket_renderer("l1-tfrag-tfrag", BucketCategory::TFRAG, BucketId::TFRAG_LEVEL1, normal_tfrags, false, 1); - // 14 - // 15 + // 14 : TFRAG_NEAR_LEVEL1 + // 15 : TIE_NEAR_LEVEL1 + // 16 : TIE_LEVEL1 init_bucket_renderer("l1-tfrag-tie", BucketCategory::TIE, BucketId::TIE_LEVEL1, 1); + // 17 : MERC_TFRAG_TEX_LEVEL1 init_bucket_renderer("l1-tfrag-merc", BucketCategory::MERC, - BucketId::MERC_TFRAG_TEX_LEVEL1); // 17 + BucketId::MERC_TFRAG_TEX_LEVEL1); + // 18 : GMERC_TFRAG_TEX_LEVEL1 init_bucket_renderer("l1-tfrag-gmerc", BucketCategory::GENERIC_MERC, - BucketId::GMERC_TFRAG_TEX_LEVEL1); // 18 + BucketId::GMERC_TFRAG_TEX_LEVEL1); //----------------------- // LEVEL 0 shrub texture //----------------------- + // 19 : SHRUB_TEX_LEVEL0 init_bucket_renderer("l0-shrub-tex", BucketCategory::TEX, - BucketId::SHRUB_TEX_LEVEL0); // 19 - // 20 - // 21 - // 22 - // 23 - // 24 + BucketId::SHRUB_TEX_LEVEL0); + // 20 : SHRUB_NORMAL_LEVEL0 + init_bucket_renderer("l0-shrub", BucketCategory::SHRUB, BucketId::SHRUB_NORMAL_LEVEL0); + // 21 : ??? + // 22 : SHRUB_BILLBOARD_LEVEL0 + // 23 : SHRUB_TRANS_LEVEL0 + // 24 : SHRUB_GENERIC_LEVEL0 //----------------------- // LEVEL 1 shrub texture //----------------------- + // 25 : SHRUB_TEX_LEVEL1 init_bucket_renderer("l1-shrub-tex", BucketCategory::TEX, - BucketId::SHRUB_TEX_LEVEL1); // 25 - // 26 - // 27 - // 28 - // 29 - - // I don't think this is actually used? or it might be wrong. - init_bucket_renderer("common-shrub-generic", BucketCategory::GENERIC_MERC, - BucketId::GENERIC_SHRUB); // 30 + BucketId::SHRUB_TEX_LEVEL1); + // 26 : SHRUB_NORMAL_LEVEL1 + init_bucket_renderer("l1-shrub", BucketCategory::SHRUB, BucketId::SHRUB_NORMAL_LEVEL1); + // 27 : ??? + // 28 : SHRUB_BILLBOARD_LEVEL1 + // 29 : SHRUB_TRANS_LEVEL1 + // 30 : SHRUB_GENERIC_LEVEL1 + init_bucket_renderer("mystery-generic", BucketCategory::GENERIC_MERC, + BucketId::SHRUB_GENERIC_LEVEL1); //----------------------- // LEVEL 0 alpha texture @@ -251,9 +266,9 @@ void OpenGLRenderer::init_bucket_renderers() { init_bucket_renderer("sprite", BucketCategory::SPRITE, BucketId::SPRITE, std::move(sprite_renderers)); // 66 - init_bucket_renderer("debug-draw-0", BucketCategory::DEBUG_DRAW, + init_bucket_renderer("debug-draw-0", BucketCategory::OTHER, BucketId::DEBUG_DRAW_0, 0x20000); - init_bucket_renderer("debug-draw-1", BucketCategory::DEBUG_DRAW, + init_bucket_renderer("debug-draw-1", BucketCategory::OTHER, BucketId::DEBUG_DRAW_1, 0x8000); // for now, for any unset renderers, just set them to an EmptyBucketRenderer. diff --git a/game/graphics/opengl_renderer/Shader.cpp b/game/graphics/opengl_renderer/Shader.cpp index e30ef6558..022c23718 100644 --- a/game/graphics/opengl_renderer/Shader.cpp +++ b/game/graphics/opengl_renderer/Shader.cpp @@ -81,5 +81,6 @@ ShaderLibrary::ShaderLibrary() { at(ShaderId::OCEAN_TEXTURE) = {"ocean_texture"}; at(ShaderId::OCEAN_TEXTURE_MIPMAP) = {"ocean_texture_mipmap"}; at(ShaderId::OCEAN_COMMON) = {"ocean_common"}; + at(ShaderId::SHRUB) = {"shrub"}; at(ShaderId::SHADOW) = {"shadow"}; } diff --git a/game/graphics/opengl_renderer/Shader.h b/game/graphics/opengl_renderer/Shader.h index ee63cef1f..52d013928 100644 --- a/game/graphics/opengl_renderer/Shader.h +++ b/game/graphics/opengl_renderer/Shader.h @@ -39,6 +39,7 @@ enum class ShaderId { OCEAN_TEXTURE_MIPMAP = 14, OCEAN_COMMON = 15, SHADOW = 16, + SHRUB = 17, MAX_SHADERS }; diff --git a/game/graphics/opengl_renderer/background/Shrub.cpp b/game/graphics/opengl_renderer/background/Shrub.cpp new file mode 100644 index 000000000..8b2c0e494 --- /dev/null +++ b/game/graphics/opengl_renderer/background/Shrub.cpp @@ -0,0 +1,303 @@ +#include "Shrub.h" + +Shrub::Shrub(const std::string& name, BucketId my_id) : BucketRenderer(name, my_id) { + m_color_result.resize(TIME_OF_DAY_COLOR_COUNT); +} + +Shrub::~Shrub() { + discard_tree_cache(); +} + +void Shrub::render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) { + if (!m_enabled) { + while (dma.current_tag_offset() != render_state->next_bucket) { + dma.read_and_advance(); + } + return; + } + + auto data0 = dma.read_and_advance(); + ASSERT(data0.vif1() == 0); + ASSERT(data0.vif0() == 0); + ASSERT(data0.size_bytes == 0); + + if (dma.current_tag().kind == DmaTag::Kind::CALL) { + // renderer didn't run, let's just get out of here. + for (int i = 0; i < 4; i++) { + dma.read_and_advance(); + } + ASSERT(dma.current_tag_offset() == render_state->next_bucket); + return; + } + + auto pc_port_data = dma.read_and_advance(); + ASSERT(pc_port_data.size_bytes == sizeof(TfragPcPortData)); + memcpy(&m_pc_port_data, pc_port_data.data, sizeof(TfragPcPortData)); + m_pc_port_data.level_name[11] = '\0'; + + while (dma.current_tag_offset() != render_state->next_bucket) { + dma.read_and_advance(); + } + + TfragRenderSettings settings; + settings.hvdf_offset = m_pc_port_data.hvdf_off; + settings.fog = m_pc_port_data.fog; + + memcpy(settings.math_camera.data(), m_pc_port_data.camera[0].data(), 64); + settings.tree_idx = 0; + + for (int i = 0; i < 8; i++) { + settings.time_of_day_weights[i] = + 2 * (0xff & m_pc_port_data.itimes[i / 2].data()[2 * (i % 2)]) / 127.f; + } + + for (int i = 0; i < 4; i++) { + settings.planes[i] = m_pc_port_data.planes[i]; + render_state->camera_planes[i] = m_pc_port_data.planes[i]; + } + render_state->has_camera_planes = true; + + m_has_level = setup_for_level(m_pc_port_data.level_name, render_state); + render_all_trees(settings, render_state, prof); +} + +void Shrub::update_load(const Loader::LevelData* loader_data) { + const tfrag3::Level* lev_data = loader_data->level.get(); + // We changed level! + discard_tree_cache(); + m_trees.resize(lev_data->shrub_trees.size()); + + size_t max_draws = 0; + size_t time_of_day_count = 0; + for (u32 l_tree = 0; l_tree < lev_data->shrub_trees.size(); l_tree++) { + size_t idx_buffer_len = 0; + const auto& tree = lev_data->shrub_trees[l_tree]; + max_draws = std::max(tree.static_draws.size(), max_draws); + for (auto& draw : tree.static_draws) { + idx_buffer_len += draw.vertex_index_stream.size(); + } + time_of_day_count = std::max(tree.time_of_day_colors.size(), time_of_day_count); + u32 verts = tree.unpacked.vertices.size(); + glGenVertexArrays(1, &m_trees[l_tree].vao); + glBindVertexArray(m_trees[l_tree].vao); + m_trees[l_tree].vertex_buffer = loader_data->shrub_vertex_data[l_tree]; + m_trees[l_tree].vert_count = verts; + m_trees[l_tree].draws = &tree.static_draws; + m_trees[l_tree].colors = &tree.time_of_day_colors; + m_trees[l_tree].tod_cache = swizzle_time_of_day(tree.time_of_day_colors); + glBindBuffer(GL_ARRAY_BUFFER, m_trees[l_tree].vertex_buffer); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + glEnableVertexAttribArray(3); + + glVertexAttribPointer(0, // location 0 in the shader + 3, // 3 values per vert + GL_FLOAT, // floats + GL_FALSE, // normalized + sizeof(tfrag3::ShrubGpuVertex), // stride + (void*)offsetof(tfrag3::ShrubGpuVertex, x) // offset (0) + ); + + glVertexAttribPointer(1, // location 1 in the shader + 2, // 3 values per vert + GL_FLOAT, // floats + GL_FALSE, // normalized + sizeof(tfrag3::ShrubGpuVertex), // stride + (void*)offsetof(tfrag3::ShrubGpuVertex, s) // offset (0) + ); + + glVertexAttribPointer(2, // location 1 in the shader + 3, // 4 color components + GL_UNSIGNED_BYTE, // u8 + GL_TRUE, // normalized (255 becomes 1) + sizeof(tfrag3::ShrubGpuVertex), // + (void*)offsetof(tfrag3::ShrubGpuVertex, rgba_base) // + ); + + glVertexAttribIPointer(3, // location 2 in the shader + 1, // 1 values per vert + GL_UNSIGNED_SHORT, // u16 + sizeof(tfrag3::ShrubGpuVertex), // stride + (void*)offsetof(tfrag3::ShrubGpuVertex, color_index) // offset (0) + ); + + glGenBuffers(1, &m_trees[l_tree].index_buffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_trees[l_tree].index_buffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx_buffer_len * sizeof(u32), nullptr, GL_STREAM_DRAW); + m_trees[l_tree].index_list.resize(idx_buffer_len); + + glActiveTexture(GL_TEXTURE10); + glGenTextures(1, &m_trees[l_tree].time_of_day_texture); + glBindTexture(GL_TEXTURE_1D, m_trees[l_tree].time_of_day_texture); + glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, TIME_OF_DAY_COLOR_COUNT, 0, GL_RGBA, + GL_UNSIGNED_INT_8_8_8_8, nullptr); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glBindVertexArray(0); + } + + m_cache.draw_idx_temp.resize(max_draws); + ASSERT(time_of_day_count <= TIME_OF_DAY_COLOR_COUNT); +} + +bool Shrub::setup_for_level(const std::string& level, SharedRenderState* render_state) { + // make sure we have the level data. + Timer tfrag3_setup_timer; + auto lev_data = render_state->loader->get_tfrag3_level(level); + if (!lev_data || (m_has_level && lev_data->load_id != m_load_id)) { + m_has_level = false; + m_textures = nullptr; + m_level_name = ""; + discard_tree_cache(); + return false; + } + m_textures = &lev_data->textures; + m_load_id = lev_data->load_id; + + if (m_level_name != level) { + update_load(lev_data); + m_has_level = true; + m_level_name = level; + } else { + m_has_level = true; + } + + if (tfrag3_setup_timer.getMs() > 5) { + fmt::print("Shrub setup: {:.1f}ms\n", tfrag3_setup_timer.getMs()); + } + + return m_has_level; +} + +void Shrub::discard_tree_cache() { + for (auto& tree : m_trees) { + glBindTexture(GL_TEXTURE_1D, tree.time_of_day_texture); + glDeleteTextures(1, &tree.time_of_day_texture); + glDeleteBuffers(1, &tree.index_buffer); + glDeleteVertexArrays(1, &tree.vao); + } + + m_trees.clear(); +} + +void Shrub::render_all_trees(const TfragRenderSettings& settings, + SharedRenderState* render_state, + ScopedProfilerNode& prof) { + for (u32 i = 0; i < m_trees.size(); i++) { + render_tree(i, settings, render_state, prof); + } +} + +void Shrub::render_tree(int idx, + const TfragRenderSettings& settings, + SharedRenderState* render_state, + ScopedProfilerNode& prof) { + Timer tree_timer; + auto& tree = m_trees.at(idx); + tree.perf.draws = 0; + tree.perf.verts = 0; + tree.perf.full_draws = 0; + tree.perf.wind_draws = 0; + if (!m_has_level) { + return; + } + + if (m_color_result.size() < tree.colors->size()) { + m_color_result.resize(tree.colors->size()); + } + + Timer interp_timer; + interp_time_of_day_fast(settings.time_of_day_weights, tree.tod_cache, m_color_result.data()); + tree.perf.tod_time.add(interp_timer.getSeconds()); + + Timer setup_timer; + glActiveTexture(GL_TEXTURE10); + glBindTexture(GL_TEXTURE_1D, tree.time_of_day_texture); + glTexSubImage1D(GL_TEXTURE_1D, 0, 0, tree.colors->size(), GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, + m_color_result.data()); + + first_tfrag_draw_setup(settings, render_state, ShaderId::SHRUB); + + glBindVertexArray(tree.vao); + glBindBuffer(GL_ARRAY_BUFFER, tree.vertex_buffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tree.index_buffer); + glActiveTexture(GL_TEXTURE0); + glEnable(GL_PRIMITIVE_RESTART); + glPrimitiveRestartIndex(UINT32_MAX); + tree.perf.tod_time.add(setup_timer.getSeconds()); + + int last_texture = -1; + u32 idx_buffer_ptr = 0; + + tree.perf.cull_time.add(0); + Timer index_timer; + idx_buffer_ptr = make_all_visible_index_list(m_cache.draw_idx_temp.data(), tree.index_list.data(), + *tree.draws); + tree.perf.index_time.add(index_timer.getSeconds()); + tree.perf.index_upload = sizeof(u32) * idx_buffer_ptr; + + Timer draw_timer; + glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx_buffer_ptr * sizeof(u32), tree.index_list.data(), + GL_STREAM_DRAW); + + for (size_t draw_idx = 0; draw_idx < tree.draws->size(); draw_idx++) { + const auto& draw = tree.draws->operator[](draw_idx); + const auto& indices = m_cache.draw_idx_temp[draw_idx]; + + if (indices.second <= indices.first) { + continue; + } + + if ((int)draw.tree_tex_id != last_texture) { + glBindTexture(GL_TEXTURE_2D, m_textures->at(draw.tree_tex_id)); + last_texture = draw.tree_tex_id; + } + + auto double_draw = setup_tfrag_shader(render_state, draw.mode, ShaderId::SHRUB); + int draw_size = indices.second - indices.first; + void* offset = (void*)(indices.first * sizeof(u32)); + + prof.add_draw_call(); + prof.add_tri(draw.num_triangles * (float)draw_size / draw.vertex_index_stream.size()); + + bool is_full = draw_size == (int)draw.vertex_index_stream.size(); + + tree.perf.draws++; + if (is_full) { + tree.perf.full_draws++; + } + tree.perf.verts += draw_size; + + glDrawElements(GL_TRIANGLE_STRIP, draw_size, GL_UNSIGNED_INT, (void*)offset); + + switch (double_draw.kind) { + case DoubleDrawKind::NONE: + break; + case DoubleDrawKind::AFAIL_NO_DEPTH_WRITE: + tree.perf.draws++; + tree.perf.verts += draw_size; + if (is_full) { + tree.perf.full_draws++; + } + prof.add_draw_call(); + prof.add_tri(draw_size); + glUniform1f(glGetUniformLocation(render_state->shaders[ShaderId::SHRUB].id(), "alpha_min"), + -10.f); + glUniform1f(glGetUniformLocation(render_state->shaders[ShaderId::SHRUB].id(), "alpha_max"), + double_draw.aref_second); + glDepthMask(GL_FALSE); + glDrawElements(GL_TRIANGLE_STRIP, draw_size, GL_UNSIGNED_INT, (void*)offset); + break; + default: + ASSERT(false); + } + } + + glBindVertexArray(0); + tree.perf.draw_time.add(draw_timer.getSeconds()); + tree.perf.tree_time.add(tree_timer.getSeconds()); +} + +void Shrub::draw_debug_window() {} \ No newline at end of file diff --git a/game/graphics/opengl_renderer/background/Shrub.h b/game/graphics/opengl_renderer/background/Shrub.h new file mode 100644 index 000000000..88ef8086e --- /dev/null +++ b/game/graphics/opengl_renderer/background/Shrub.h @@ -0,0 +1,77 @@ +#pragma once + +#include + +#include "game/graphics/gfx.h" +#include "game/graphics/opengl_renderer/background/background_common.h" +#include "game/graphics/opengl_renderer/BucketRenderer.h" +#include "game/graphics/pipelines/opengl.h" +#include "common/util/FilteredValue.h" + +class Shrub : public BucketRenderer { + public: + Shrub(const std::string& name, BucketId my_id); + ~Shrub(); + bool setup_for_level(const std::string& level, SharedRenderState* render_state); + void render_all_trees(const TfragRenderSettings& settings, + SharedRenderState* render_state, + ScopedProfilerNode& prof); + void render_tree(int idx, + const TfragRenderSettings& settings, + SharedRenderState* render_state, + ScopedProfilerNode& prof); + void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override; + void draw_debug_window() override; + + private: + void update_load(const Loader::LevelData* loader_data); + void discard_tree_cache(); + + struct Tree { + GLuint vertex_buffer; + GLuint index_buffer; + GLuint time_of_day_texture; + std::vector index_list; + GLuint vao; + u32 vert_count; + const std::vector* draws = nullptr; + const std::vector* instance_info = nullptr; + const std::vector* colors = nullptr; + SwizzledTimeOfDay tod_cache; + + std::vector> wind_matrix_cache; + + bool has_wind = false; + GLuint wind_vertex_index_buffer; + std::vector wind_vertex_index_offsets; + + struct { + u32 index_upload = 0; + u32 verts = 0; + u32 draws = 0; + u32 full_draws = 0; // ones that have all visible + u32 wind_draws = 0; + Filtered cull_time; + Filtered index_time; + Filtered tod_time; + Filtered setup_time; + Filtered draw_time; + Filtered tree_time; + } perf; + }; + + std::vector m_trees; + std::string m_level_name; + const std::vector* m_textures; + u64 m_load_id = -1; + + std::vector> m_color_result; + + static constexpr int TIME_OF_DAY_COLOR_COUNT = 8192; + bool m_has_level = false; + + struct Cache { + std::vector> draw_idx_temp; + } m_cache; + TfragPcPortData m_pc_port_data; +}; diff --git a/game/graphics/opengl_renderer/background/Tfrag3.cpp b/game/graphics/opengl_renderer/background/Tfrag3.cpp index 07e254dcc..2e949dc86 100644 --- a/game/graphics/opengl_renderer/background/Tfrag3.cpp +++ b/game/graphics/opengl_renderer/background/Tfrag3.cpp @@ -184,7 +184,7 @@ void Tfrag3::render_tree(int geom, glTexSubImage1D(GL_TEXTURE_1D, 0, 0, tree.colors->size(), GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, m_color_result.data()); - first_tfrag_draw_setup(settings, render_state); + first_tfrag_draw_setup(settings, render_state, ShaderId::TFRAG3); glBindVertexArray(tree.vao); glBindBuffer(GL_ARRAY_BUFFER, tree.vertex_buffer); @@ -212,7 +212,7 @@ void Tfrag3::render_tree(int geom, ASSERT(m_textures); glBindTexture(GL_TEXTURE_2D, m_textures->at(draw.tree_tex_id)); - auto double_draw = setup_tfrag_shader(render_state, draw.mode); + auto double_draw = setup_tfrag_shader(render_state, draw.mode, ShaderId::TFRAG3); tree.tris_this_frame += draw.num_triangles; tree.draws_this_frame++; int draw_size = indices.second - indices.first; diff --git a/game/graphics/opengl_renderer/background/Tie3.cpp b/game/graphics/opengl_renderer/background/Tie3.cpp index 7cc8555d3..95b820494 100644 --- a/game/graphics/opengl_renderer/background/Tie3.cpp +++ b/game/graphics/opengl_renderer/background/Tie3.cpp @@ -360,6 +360,7 @@ void Tie3::render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfi if (!m_override_level) { m_has_level = setup_for_level(m_pc_port_data.level_name, render_state); } + render_all_trees(lod(), settings, render_state, prof); } @@ -441,7 +442,7 @@ void Tie3::render_tree_wind(int idx, glBindTexture(GL_TEXTURE_2D, m_textures->at(draw.tree_tex_id)); last_texture = draw.tree_tex_id; } - auto double_draw = setup_tfrag_shader(render_state, draw.mode); + auto double_draw = setup_tfrag_shader(render_state, draw.mode, ShaderId::TFRAG3); int off = 0; for (auto& grp : draw.instance_groups) { @@ -524,7 +525,7 @@ void Tie3::render_tree(int idx, glTexSubImage1D(GL_TEXTURE_1D, 0, 0, tree.colors->size(), GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, m_color_result.data()); - first_tfrag_draw_setup(settings, render_state); + first_tfrag_draw_setup(settings, render_state, ShaderId::TFRAG3); glBindVertexArray(tree.vao); glBindBuffer(GL_ARRAY_BUFFER, tree.vertex_buffer); @@ -574,7 +575,7 @@ void Tie3::render_tree(int idx, last_texture = draw.tree_tex_id; } - auto double_draw = setup_tfrag_shader(render_state, draw.mode); + auto double_draw = setup_tfrag_shader(render_state, draw.mode, ShaderId::TFRAG3); int draw_size = indices.second - indices.first; void* offset = (void*)(indices.first * sizeof(u32)); diff --git a/game/graphics/opengl_renderer/background/background_common.cpp b/game/graphics/opengl_renderer/background/background_common.cpp index aefd747f0..4fc14ec00 100644 --- a/game/graphics/opengl_renderer/background/background_common.cpp +++ b/game/graphics/opengl_renderer/background/background_common.cpp @@ -132,30 +132,31 @@ DoubleDraw setup_opengl_from_draw_mode(DrawMode mode, u32 tex_unit, bool mipmap) return double_draw; } -DoubleDraw setup_tfrag_shader(SharedRenderState* render_state, DrawMode mode) { +DoubleDraw setup_tfrag_shader(SharedRenderState* render_state, DrawMode mode, ShaderId shader) { auto draw_settings = setup_opengl_from_draw_mode(mode, GL_TEXTURE0, true); - glUniform1f(glGetUniformLocation(render_state->shaders[ShaderId::TFRAG3].id(), "alpha_min"), + glUniform1f(glGetUniformLocation(render_state->shaders[shader].id(), "alpha_min"), draw_settings.aref_first); - glUniform1f(glGetUniformLocation(render_state->shaders[ShaderId::TFRAG3].id(), "alpha_max"), - 10.f); + glUniform1f(glGetUniformLocation(render_state->shaders[shader].id(), "alpha_max"), 10.f); return draw_settings; } -void first_tfrag_draw_setup(const TfragRenderSettings& settings, SharedRenderState* render_state) { - render_state->shaders[ShaderId::TFRAG3].activate(); - glUniform1i(glGetUniformLocation(render_state->shaders[ShaderId::TFRAG3].id(), "tex_T0"), 0); - glUniformMatrix4fv(glGetUniformLocation(render_state->shaders[ShaderId::TFRAG3].id(), "camera"), - 1, GL_FALSE, settings.math_camera.data()); - glUniform4f(glGetUniformLocation(render_state->shaders[ShaderId::TFRAG3].id(), "hvdf_offset"), +void first_tfrag_draw_setup(const TfragRenderSettings& settings, + SharedRenderState* render_state, + ShaderId shader) { + render_state->shaders[shader].activate(); + glUniform1i(glGetUniformLocation(render_state->shaders[shader].id(), "tex_T0"), 0); + glUniformMatrix4fv(glGetUniformLocation(render_state->shaders[shader].id(), "camera"), 1, + GL_FALSE, settings.math_camera.data()); + glUniform4f(glGetUniformLocation(render_state->shaders[shader].id(), "hvdf_offset"), settings.hvdf_offset[0], settings.hvdf_offset[1], settings.hvdf_offset[2], settings.hvdf_offset[3]); - glUniform1f(glGetUniformLocation(render_state->shaders[ShaderId::TFRAG3].id(), "fog_constant"), + glUniform1f(glGetUniformLocation(render_state->shaders[shader].id(), "fog_constant"), settings.fog.x()); - glUniform1f(glGetUniformLocation(render_state->shaders[ShaderId::TFRAG3].id(), "fog_min"), + glUniform1f(glGetUniformLocation(render_state->shaders[shader].id(), "fog_min"), settings.fog.y()); - glUniform1f(glGetUniformLocation(render_state->shaders[ShaderId::TFRAG3].id(), "fog_max"), + glUniform1f(glGetUniformLocation(render_state->shaders[shader].id(), "fog_max"), settings.fog.z()); - glUniform4f(glGetUniformLocation(render_state->shaders[ShaderId::TFRAG3].id(), "fog_color"), + glUniform4f(glGetUniformLocation(render_state->shaders[shader].id(), "fog_color"), render_state->fog_color[0], render_state->fog_color[1], render_state->fog_color[2], render_state->fog_intensity); } @@ -499,6 +500,23 @@ u32 make_all_visible_index_list(std::pair* group_out, return idx_buffer_ptr; } +u32 make_all_visible_index_list(std::pair* group_out, + u32* idx_out, + const std::vector& draws) { + int idx_buffer_ptr = 0; + for (size_t i = 0; i < draws.size(); i++) { + const auto& draw = draws[i]; + std::pair ds; + ds.first = idx_buffer_ptr; + memcpy(&idx_out[idx_buffer_ptr], draw.vertex_index_stream.data(), + draw.vertex_index_stream.size() * sizeof(u32)); + idx_buffer_ptr += draw.vertex_index_stream.size(); + ds.second = idx_buffer_ptr; + group_out[i] = ds; + } + return idx_buffer_ptr; +} + u32 make_index_list_from_vis_string(std::pair* group_out, u32* idx_out, const std::vector& draws, diff --git a/game/graphics/opengl_renderer/background/background_common.h b/game/graphics/opengl_renderer/background/background_common.h index bc3d44233..57266be72 100644 --- a/game/graphics/opengl_renderer/background/background_common.h +++ b/game/graphics/opengl_renderer/background/background_common.h @@ -23,10 +23,12 @@ struct DoubleDraw { float aref_second = 0.; }; -DoubleDraw setup_tfrag_shader(SharedRenderState* render_state, DrawMode mode); +DoubleDraw setup_tfrag_shader(SharedRenderState* render_state, DrawMode mode, ShaderId shader); DoubleDraw setup_opengl_from_draw_mode(DrawMode mode, u32 tex_unit, bool mipmap); -void first_tfrag_draw_setup(const TfragRenderSettings& settings, SharedRenderState* render_state); +void first_tfrag_draw_setup(const TfragRenderSettings& settings, + SharedRenderState* render_state, + ShaderId shader); void interp_time_of_day_slow(const float weights[8], const std::vector& in, math::Vector* out); @@ -64,4 +66,7 @@ u32 make_index_list_from_vis_string(std::pair* group_out, const std::vector& vis_data); u32 make_all_visible_index_list(std::pair* group_out, u32* idx_out, - const std::vector& draws); \ No newline at end of file + const std::vector& draws); +u32 make_all_visible_index_list(std::pair* group_out, + u32* idx_out, + const std::vector& draws); \ No newline at end of file diff --git a/game/graphics/opengl_renderer/buckets.h b/game/graphics/opengl_renderer/buckets.h index b12df1f65..de32c772c 100644 --- a/game/graphics/opengl_renderer/buckets.h +++ b/game/graphics/opengl_renderer/buckets.h @@ -10,30 +10,30 @@ enum class BucketId { OCEAN_MID_AND_FAR = 4, TFRAG_TEX_LEVEL0 = 5, TFRAG_LEVEL0 = 6, - // 7 - // 8 + TFRAG_NEAR_LEVEL0 = 7, + TIE_NEAR_LEVEL0 = 8, TIE_LEVEL0 = 9, MERC_TFRAG_TEX_LEVEL0 = 10, GMERC_TFRAG_TEX_LEVEL0 = 11, TFRAG_TEX_LEVEL1 = 12, TFRAG_LEVEL1 = 13, - // 14 - // 15 + TFRAG_NEAR_LEVEL1 = 14, + TIE_NEAR_LEVEL1 = 15, TIE_LEVEL1 = 16, MERC_TFRAG_TEX_LEVEL1 = 17, GMERC_TFRAG_TEX_LEVEL1 = 18, SHRUB_TEX_LEVEL0 = 19, - // 20 + SHRUB_NORMAL_LEVEL0 = 20, // 21 - // 22 - // 23 - // 24 + SHRUB_BILLBOARD_LEVEL0 = 22, + SHRUB_TRANS_LEVEL0 = 23, + SHRUB_GENERIC_LEVEL0 = 24, SHRUB_TEX_LEVEL1 = 25, - // 26 + SHRUB_NORMAL_LEVEL1 = 26, // 27 - // 28 - // 29 - GENERIC_SHRUB = 30, + SHRUB_BILLBOARD_LEVEL1 = 28, + SHRUB_TRANS_LEVEL1 = 29, + SHRUB_GENERIC_LEVEL1 = 30, ALPHA_TEX_LEVEL0 = 31, TFRAG_TRANS0_AND_SKY_BLEND_LEVEL0 = 32, // 33 @@ -78,15 +78,15 @@ enum class BucketId { enum class BucketCategory { TFRAG, TIE, + SHRUB, TEX, MERC, GENERIC_MERC, SPRITE, OCEAN, - DEBUG_DRAW, OTHER, MAX_CATEGORIES }; constexpr const char* BUCKET_CATEGORY_NAMES[(int)BucketCategory::MAX_CATEGORIES] = { - "tfrag", "tie", "tex", "merc", "mercneric", "sprite", "ocean", "debug", "other"}; \ No newline at end of file + "tfrag", "tie", "shrub", "tex", "merc", "mercneric", "sprite", "ocean", "other"}; \ No newline at end of file diff --git a/game/graphics/opengl_renderer/shaders/shrub.frag b/game/graphics/opengl_renderer/shaders/shrub.frag new file mode 100644 index 000000000..481940e1c --- /dev/null +++ b/game/graphics/opengl_renderer/shaders/shrub.frag @@ -0,0 +1,28 @@ +#version 430 core + +out vec4 color; + +in vec4 fragment_color; +in vec3 tex_coord; +in float fogginess; +uniform sampler2D tex_T0; + +uniform float alpha_min; +uniform float alpha_max; +uniform vec4 fog_color; + +void main() { + vec4 T0 = texture(tex_T0, tex_coord.xy / 4096.f); + color = fragment_color * T0 * 2.0; + color.w *= 2; + + if (color.a < alpha_min * 2) { + discard; + } + + if (color.a > alpha_max) { + discard; + } + + color.xyz = mix(color.xyz, fog_color.xyz / 255., clamp(fogginess/255 * fog_color.w, 0., 1.)); +} diff --git a/game/graphics/opengl_renderer/shaders/shrub.vert b/game/graphics/opengl_renderer/shaders/shrub.vert new file mode 100644 index 000000000..7a7a4b28a --- /dev/null +++ b/game/graphics/opengl_renderer/shaders/shrub.vert @@ -0,0 +1,86 @@ +#version 430 core + +layout (location = 0) in vec3 position_in; +layout (location = 1) in vec3 tex_coord_in; +layout (location = 2) in vec3 rgba_base; +layout (location = 3) in int time_of_day_index; + +uniform vec4 hvdf_offset; +uniform mat4 camera; +uniform float fog_constant; +uniform float fog_min; +uniform float fog_max; +layout (binding = 10) uniform sampler1D tex_T1; // note, sampled in the vertex shader on purpose. + +out vec4 fragment_color; +out vec3 tex_coord; +out float fogginess; + +void main() { + + + // old system: + // - load vf12 + // - itof0 vf12 + // - multiply with camera matrix (add trans) + // - let Q = fogx / vf12.w + // - xyz *= Q + // - xyzw += hvdf_offset + // - clip w. + // - ftoi4 vf12 + // use in gs. + // gs is 12.4 fixed point, set up with 2048.0 as the center. + + // the itof0 is done in the preprocessing step. now we have floats. + + // Step 3, the camera transform + vec4 transformed = -camera[3].xyzw; + transformed += -camera[0] * position_in.x; + transformed += -camera[1] * position_in.y; + transformed += -camera[2] * position_in.z; + + // compute Q + float Q = fog_constant / transformed[3]; + + float fog1 = -transformed.w + hvdf_offset.w; + float fog2 = min(fog1, fog_max); + float fog3 = max(fog2, fog_min); + fogginess = 255 - fog3; + + // perspective divide! + transformed.xyz *= Q; + + // offset + transformed.xyz += hvdf_offset.xyz; + + // ftoi4 + //transformed.xyzw *= 16; + + // correct xy offset + transformed.xy -= (2048.); + + // correct z scale + transformed.z /= (8388608); + transformed.z -= 1; + + // correct xy scale + transformed.x /= (256); + transformed.y /= -(128); + + // hack + transformed.xyz *= transformed.w; + + gl_Position = transformed; + // scissoring area adjust + gl_Position.y *= 512.0/448.0; + + // time of day lookup + // start with the vertex color (only rgb, VIF filled in the 255.) + fragment_color = vec4(rgba_base, 1); + // get the time of day multiplier + vec4 tod_color = texelFetch(tex_T1, time_of_day_index, 0); + // combine + fragment_color *= tod_color * 2; + + tex_coord = tex_coord_in; +} diff --git a/goal_src/engine/dma/dma-h.gc b/goal_src/engine/dma/dma-h.gc index f8af1ab69..1fea2597f 100644 --- a/goal_src/engine/dma/dma-h.gc +++ b/goal_src/engine/dma/dma-h.gc @@ -213,31 +213,34 @@ :type int32 :bitfield #f - (bucket-1 1) - (bucket-2 2) - (sky-draw 3) - (bucket-4 4) ;; ocean - (tfrag-tex0 5) - (tfrag-0 6) - (tfrag-near-0 7) - (tie-near-0 8) - (tie-0 9) - (bucket-10 10) ;; merc - (bucket-11 11) + (bucket-0 0) ;; ? + (bucket-1 1) ;; ? + (bucket-2 2) ;; ? + (sky-draw 3) ;; actual sky and cloud framebuffer draws + (ocean-mid-and-far 4) ;; actual ocean framebuffer draws for mid/transition/far - (tfrag-tex1 12) - (tfrag-1 13) - (tfrag-near-1 14) - (tie-near-1 15) - (tie-1 16) - ;; merc1 17 - ;; generic1 18 - (bucket-17 17) ;; merc - (bucket-18 18) + (tfrag-tex0 5) ;; tfrag texture upload, level 0 + (tfrag-0 6) ;; tfrag draw, level 0 + (tfrag-near-0 7) ;; tfrag near draw, level 0 + (tie-near-0 8) ;; tie near draw, level 0 + (tie-0 9) ;; tie draw, level 0 + (merc-tfrag-tex0 10) ;; merc, with tfrag textures, level 0 + (gmerc-tfrag-tex 11) ;; generic merc, with tfrag textures, level 0 + (tfrag-tex1 12) ;; tfrag texture upload, level 1 + (tfrag-1 13) ;; tfrag draw, level 1 + (tfrag-near-1 14) ;; tfrag near draw, level 1 + (tie-near-1 15) ;; tie near draw, level 1 + (tie-1 16) ;; tie draw, level 1 + (merc-tfrag-tex1 17) ;; merc, with tfrag textures, level 1 + (gmerc-tfrag-tex1 18) ;; generic merc, with tfrag textures, level 1 (shrub-tex0 19) - + (shrub0 20) + + (shrub-tex1 25) + (shrub1 26) + (generic-foreground 30) ;; ? (alpha-tex0 31) diff --git a/goal_src/engine/gfx/background-h.gc b/goal_src/engine/gfx/background-h.gc index 37dd1ad61..f06c071cf 100644 --- a/goal_src/engine/gfx/background-h.gc +++ b/goal_src/engine/gfx/background-h.gc @@ -37,3 +37,6 @@ :size-assert #x248 :flag-assert #x900000248 ) + + +(define-extern add-pc-tfrag3-data (function dma-buffer level pointer)) \ No newline at end of file diff --git a/goal_src/engine/gfx/merc/merc.gc b/goal_src/engine/gfx/merc/merc.gc index 7098a9223..746f37ae6 100644 --- a/goal_src/engine/gfx/merc/merc.gc +++ b/goal_src/engine/gfx/merc/merc.gc @@ -593,7 +593,7 @@ "Setup merc DMA buffers. Call this _after_ drawing." (when (logtest? *vu1-enable-user* (vu1-renderer-mask merc)) (merc-vu1-init-buffer - (bucket-id bucket-10) + (bucket-id merc-tfrag-tex0) (new 'static 'gs-test :ate #x1 :atst (gs-atest greater-equal) @@ -615,7 +615,7 @@ 0 ) (merc-vu1-init-buffer - (bucket-id bucket-17) + (bucket-id merc-tfrag-tex1) (new 'static 'gs-test :ate #x1 :atst (gs-atest greater-equal) diff --git a/goal_src/engine/gfx/ocean/ocean.gc b/goal_src/engine/gfx/ocean/ocean.gc index 8d27c5bea..6fab96f5d 100644 --- a/goal_src/engine/gfx/ocean/ocean.gc +++ b/goal_src/engine/gfx/ocean/ocean.gc @@ -661,7 +661,7 @@ ) (dma-bucket-insert-tag (-> *display* frames (-> *display* on-screen) frame bucket-group) - (bucket-id bucket-4) + (bucket-id ocean-mid-and-far) gp-1 (the-as (pointer dma-tag) a3-0) ) diff --git a/goal_src/engine/gfx/shrub/shrubbery.gc b/goal_src/engine/gfx/shrub/shrubbery.gc index 5aee8ab28..ea40a1f79 100644 --- a/goal_src/engine/gfx/shrub/shrubbery.gc +++ b/goal_src/engine/gfx/shrub/shrubbery.gc @@ -475,6 +475,37 @@ ; (-> (the-as drawable-group (-> arg0 data 0)) length) ; s3-0 ; ) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; NOTE: this part is completely rewritten for PC + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + (when (logtest? *vu1-enable-user* (vu1-renderer-mask shrubbery)) + (let* ((s2-1 (-> *display* frames (-> *display* on-screen) frame global-buf)) + (s3-1 (-> s2-1 base)) + ) + (add-pc-tfrag3-data s2-1 (-> *level* data (-> (scratchpad-object terrain-context) bsp lev-index))) + (let ((a3-22 (-> s2-1 base))) + (let ((v1-57 (the-as object (-> s2-1 base)))) + (set! (-> (the-as dma-packet v1-57) dma) (new 'static 'dma-tag :id (dma-tag-id next))) + (set! (-> (the-as dma-packet v1-57) vif0) (new 'static 'vif-tag)) + (set! (-> (the-as dma-packet v1-57) vif1) (new 'static 'vif-tag)) + (set! (-> s2-1 base) (&+ (the-as pointer v1-57) 16)) + ) + (dma-bucket-insert-tag + (-> *display* frames (-> *display* on-screen) frame bucket-group) + (the-as bucket-id (if (zero? (-> (scratchpad-object terrain-context) bsp lev-index)) + (bucket-id shrub0) + (bucket-id shrub1) + ) + ) + s3-1 + (the-as (pointer dma-tag) a3-22) + ) + ) + ) + ) + (read! (-> *perf-stats* data (perf-stat-bucket inst-shrub))) (reset! (-> *perf-stats* data (perf-stat-bucket proto-shrub))) ; (draw-prototype-inline-array-shrub s4-0 s3-0) diff --git a/test/decompiler/reference/engine/gfx/merc/merc_REF.gc b/test/decompiler/reference/engine/gfx/merc/merc_REF.gc index a4b794d53..f30138fb9 100644 --- a/test/decompiler/reference/engine/gfx/merc/merc_REF.gc +++ b/test/decompiler/reference/engine/gfx/merc/merc_REF.gc @@ -493,7 +493,7 @@ (defun merc-vu1-init-buffers () (when (logtest? *vu1-enable-user* (vu1-renderer-mask merc)) (merc-vu1-init-buffer - (bucket-id bucket-10) + (bucket-id merc-tfrag-tex0) (new 'static 'gs-test :ate #x1 :atst (gs-atest greater-equal) @@ -515,7 +515,7 @@ 0 ) (merc-vu1-init-buffer - (bucket-id bucket-17) + (bucket-id merc-tfrag-tex1) (new 'static 'gs-test :ate #x1 :atst (gs-atest greater-equal) diff --git a/test/decompiler/reference/engine/level/level-h_REF.gc b/test/decompiler/reference/engine/level/level-h_REF.gc index e2e509064..0ebb88c06 100644 --- a/test/decompiler/reference/engine/level/level-h_REF.gc +++ b/test/decompiler/reference/engine/level/level-h_REF.gc @@ -339,8 +339,8 @@ (new 'static 'dma-foreground-sink-group :sink (new 'static 'array dma-foreground-sink 3 - (new 'static 'dma-foreground-sink :bucket (bucket-id bucket-10)) - (new 'static 'generic-dma-foreground-sink :bucket (bucket-id bucket-11) :foreground-output-bucket 1) + (new 'static 'dma-foreground-sink :bucket (bucket-id merc-tfrag-tex0)) + (new 'static 'generic-dma-foreground-sink :bucket (bucket-id gmerc-tfrag-tex) :foreground-output-bucket 1) ) ) (new 'static 'dma-foreground-sink-group @@ -380,9 +380,9 @@ (new 'static 'dma-foreground-sink-group :sink (new 'static 'array dma-foreground-sink 3 - (new 'static 'dma-foreground-sink :bucket (bucket-id bucket-17) :foreground-texture-level 1) + (new 'static 'dma-foreground-sink :bucket (bucket-id merc-tfrag-tex1) :foreground-texture-level 1) (new 'static 'generic-dma-foreground-sink - :bucket (bucket-id bucket-18) + :bucket (bucket-id gmerc-tfrag-tex1) :foreground-texture-level 1 :foreground-output-bucket 1 )