[graphics] shrub (#1261)

* update

* renderer appears to work
This commit is contained in:
water111 2022-03-29 21:38:48 -04:00 committed by GitHub
parent b688bdbfd1
commit 79683a44b1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 1269 additions and 161 deletions

View file

@ -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<size_t>(static_draws.size());
} else {
static_draws.resize(ser.load<size_t>());
}
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<size_t>(shrub_trees.size());
} else {
shrub_trees.resize(ser.load<size_t>());
}
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<int, MemoryUsageCategory::NUM_CATEGORIES> 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;
}

View file

@ -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<std::array<math::Vector4f, 4>> matrices;
std::vector<MatrixGroup> matrix_groups; // todo pack
std::vector<Vertex> 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<uint16_t>()(x.xoff) ^ std::hash<uint16_t>()(x.yoff) ^
std::hash<uint16_t>()(x.zoff) ^ std::hash<uint16_t>()(x.cluster_idx) ^
std::hash<uint16_t>()(x.s) ^ std::hash<uint16_t>()(x.t) ^
std::hash<uint16_t>()(x.color_index);
}
};
*/
};
std::vector<Vertex> vertices;
std::vector<math::Vector<u16, 3>> 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<std::array<math::Vector4f, 4>> matrices;
std::vector<InstanceGroup> instance_groups; // todo pack
std::vector<Vertex> 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<u32> 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<TimeOfDayColor> time_of_day_colors; // multiplier colors
PackedShrubVertices packed_vertices;
std::vector<ShrubDraw> static_draws; // the actual topology and settings
struct {
std::vector<ShrubGpuVertex> 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<Texture> textures;
std::array<std::vector<TfragTree>, TFRAG_GEOS> tfrag_trees;
std::array<std::vector<TieTree>, TIE_GEOS> tie_trees;
std::vector<ShrubTree> shrub_trees;
u16 version2 = TFRAG3_VERSION;
void serialize(Serializer& ser);

View file

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

View file

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

View file

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

View file

@ -33,13 +33,17 @@ std::array<math::Vector4f, 4> extract_shrub_matrix(const u16* data) {
struct ShrubVertex {
math::Vector<float, 3> xyz;
math::Vector<float, 2> st;
math::Vector<u8, 4> rgba_generic;
math::Vector<u8, 3> 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<ShrubVertex> 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<level_tools::TextureRemap>& 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<level_tools::TextureRemap>& 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<level_tools::TextureRemap>& 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<AdGifData> 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<ShrubProtoInfo>& protos) {
return result;
}
u32 clean_up_vertex_indices(std::vector<u32>& idx) {
std::vector<u32> 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<ShrubProtoInfo>& protos,
const TextureDB& tdb) {
std::unordered_map<u32, std::vector<u32>> static_draws_by_tex;
size_t global_vert_counter = 0;
for (auto& proto : protos) {
// packed_vert_indices[frag][draw] = {start, end}
std::vector<std::vector<std::pair<int, int>>> 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<level_tools::TextureRemap>& /*map*/,
const TextureDB& /*tex_db*/,
const std::vector<level_tools::TextureRemap>& map,
const TextureDB& tex_db,
const std::vector<std::pair<int, int>>& /*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<ShrubProtoInfo> 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);

View file

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

View file

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

View file

@ -30,6 +30,7 @@ class Loader {
};
std::array<std::vector<TieOpenGL>, tfrag3::TIE_GEOS> tie_data;
std::array<std::vector<GLuint>, tfrag3::TIE_GEOS> tfrag_vertex_data;
std::vector<GLuint> 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<std::string, Level> m_initializing_tfrag3_levels;

View file

@ -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<SkyRenderer>("sky", BucketCategory::OTHER, BucketId::SKY_DRAW); // 3
// 0 : ??
// 1 : ??
// 2 : ??
// 3 : SKY_DRAW
init_bucket_renderer<SkyRenderer>("sky", BucketCategory::OTHER, BucketId::SKY_DRAW);
// 4 : OCEAN_MID_AND_FAR
init_bucket_renderer<OceanMidAndFar>("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<TextureUploadHandler>("l0-tfrag-tex", BucketCategory::TEX,
BucketId::TFRAG_TEX_LEVEL0); // 5
BucketId::TFRAG_TEX_LEVEL0);
// 6 : TFRAG_LEVEL0
init_bucket_renderer<TFragment>("l0-tfrag-tfrag", BucketCategory::TFRAG, BucketId::TFRAG_LEVEL0,
normal_tfrags, false,
0); // 6
// 7
// 8
init_bucket_renderer<Tie3>("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<Tie3>("l0-tfrag-tie", BucketCategory::TIE, BucketId::TIE_LEVEL0, 0);
// 10 : MERC_TFRAG_TEX_LEVEL0
init_bucket_renderer<MercRenderer>("l0-tfrag-merc", BucketCategory::MERC,
BucketId::MERC_TFRAG_TEX_LEVEL0); // 10
BucketId::MERC_TFRAG_TEX_LEVEL0);
// 11 : GMERC_TFRAG_TEX_LEVEL0
init_bucket_renderer<GenericRenderer>("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<TextureUploadHandler>("l1-tfrag-tex", BucketCategory::TEX,
BucketId::TFRAG_TEX_LEVEL1); // 12
BucketId::TFRAG_TEX_LEVEL1);
// 13 : TFRAG_LEVEL1
init_bucket_renderer<TFragment>("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<Tie3>("l1-tfrag-tie", BucketCategory::TIE, BucketId::TIE_LEVEL1, 1);
// 17 : MERC_TFRAG_TEX_LEVEL1
init_bucket_renderer<MercRenderer>("l1-tfrag-merc", BucketCategory::MERC,
BucketId::MERC_TFRAG_TEX_LEVEL1); // 17
BucketId::MERC_TFRAG_TEX_LEVEL1);
// 18 : GMERC_TFRAG_TEX_LEVEL1
init_bucket_renderer<GenericRenderer>("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<TextureUploadHandler>("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<Shrub>("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<TextureUploadHandler>("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<GenericRenderer>("common-shrub-generic", BucketCategory::GENERIC_MERC,
BucketId::GENERIC_SHRUB); // 30
BucketId::SHRUB_TEX_LEVEL1);
// 26 : SHRUB_NORMAL_LEVEL1
init_bucket_renderer<Shrub>("l1-shrub", BucketCategory::SHRUB, BucketId::SHRUB_NORMAL_LEVEL1);
// 27 : ???
// 28 : SHRUB_BILLBOARD_LEVEL1
// 29 : SHRUB_TRANS_LEVEL1
// 30 : SHRUB_GENERIC_LEVEL1
init_bucket_renderer<GenericRenderer>("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<RenderMux>("sprite", BucketCategory::SPRITE, BucketId::SPRITE,
std::move(sprite_renderers)); // 66
init_bucket_renderer<DirectRenderer>("debug-draw-0", BucketCategory::DEBUG_DRAW,
init_bucket_renderer<DirectRenderer>("debug-draw-0", BucketCategory::OTHER,
BucketId::DEBUG_DRAW_0, 0x20000);
init_bucket_renderer<DirectRenderer>("debug-draw-1", BucketCategory::DEBUG_DRAW,
init_bucket_renderer<DirectRenderer>("debug-draw-1", BucketCategory::OTHER,
BucketId::DEBUG_DRAW_1, 0x8000);
// for now, for any unset renderers, just set them to an EmptyBucketRenderer.

View file

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

View file

@ -39,6 +39,7 @@ enum class ShaderId {
OCEAN_TEXTURE_MIPMAP = 14,
OCEAN_COMMON = 15,
SHADOW = 16,
SHRUB = 17,
MAX_SHADERS
};

View file

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

View file

@ -0,0 +1,77 @@
#pragma once
#include <optional>
#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<u32> index_list;
GLuint vao;
u32 vert_count;
const std::vector<tfrag3::ShrubDraw>* draws = nullptr;
const std::vector<tfrag3::TieWindInstance>* instance_info = nullptr;
const std::vector<tfrag3::TimeOfDayColor>* colors = nullptr;
SwizzledTimeOfDay tod_cache;
std::vector<std::array<math::Vector4f, 4>> wind_matrix_cache;
bool has_wind = false;
GLuint wind_vertex_index_buffer;
std::vector<u32> 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<float> cull_time;
Filtered<float> index_time;
Filtered<float> tod_time;
Filtered<float> setup_time;
Filtered<float> draw_time;
Filtered<float> tree_time;
} perf;
};
std::vector<Tree> m_trees;
std::string m_level_name;
const std::vector<GLuint>* m_textures;
u64 m_load_id = -1;
std::vector<math::Vector<u8, 4>> m_color_result;
static constexpr int TIME_OF_DAY_COLOR_COUNT = 8192;
bool m_has_level = false;
struct Cache {
std::vector<std::pair<int, int>> draw_idx_temp;
} m_cache;
TfragPcPortData m_pc_port_data;
};

View file

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

View file

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

View file

@ -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<int, int>* group_out,
return idx_buffer_ptr;
}
u32 make_all_visible_index_list(std::pair<int, int>* group_out,
u32* idx_out,
const std::vector<tfrag3::ShrubDraw>& draws) {
int idx_buffer_ptr = 0;
for (size_t i = 0; i < draws.size(); i++) {
const auto& draw = draws[i];
std::pair<int, int> 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<int, int>* group_out,
u32* idx_out,
const std::vector<tfrag3::StripDraw>& draws,

View file

@ -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<tfrag3::TimeOfDayColor>& in,
math::Vector<u8, 4>* out);
@ -64,4 +66,7 @@ u32 make_index_list_from_vis_string(std::pair<int, int>* group_out,
const std::vector<u8>& vis_data);
u32 make_all_visible_index_list(std::pair<int, int>* group_out,
u32* idx_out,
const std::vector<tfrag3::StripDraw>& draws);
const std::vector<tfrag3::StripDraw>& draws);
u32 make_all_visible_index_list(std::pair<int, int>* group_out,
u32* idx_out,
const std::vector<tfrag3::ShrubDraw>& draws);

View file

@ -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"};
"tfrag", "tie", "shrub", "tex", "merc", "mercneric", "sprite", "ocean", "other"};

View file

@ -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.));
}

View file

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

View file

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

View file

@ -37,3 +37,6 @@
:size-assert #x248
:flag-assert #x900000248
)
(define-extern add-pc-tfrag3-data (function dma-buffer level pointer))

View file

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

View file

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

View file

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

View file

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

View file

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