@@ -629,25 +629,35 @@ auto fetch_color_attr(const AttrConfig& mapping, std::string_view buf, std::stri
629629 }
630630}
631631
632+ struct AttrAddress {
633+ std::string offs;
634+ std::string_view buf;
635+ bool le;
636+ };
637+
638+ auto attr_address (const AttrConfig& mapping, GXAttr attr, std::string_view vidx, u32 vtxStride, u32 dlExtra, u32 within)
639+ -> AttrAddress {
640+ const u32 dlOffset = mapping.offset + dlExtra;
641+ if (mapping.attrType == GX_INDEX8) {
642+ return {fmt::format (" ubuf.array_start[{}] + raw_fetch_u8_1(&vbuf, ubuf.vtx_start + {} * {}u + {}u) * {}u + {}u" ,
643+ attr - GX_VA_POS, vidx, vtxStride, dlOffset, mapping.stride , within),
644+ " abuf" sv, mapping.le };
645+ }
646+ if (mapping.attrType == GX_INDEX16) {
647+ return {
648+ fmt::format (" ubuf.array_start[{}] + raw_fetch_u16_1(&vbuf, ubuf.vtx_start + {} * {}u + {}u, false) * {}u + {}u" ,
649+ attr - GX_VA_POS, vidx, vtxStride, dlOffset, mapping.stride , within),
650+ " abuf" sv, mapping.le };
651+ }
652+ return {fmt::format (" ubuf.vtx_start + {} * {}u + {}u" , vidx, vtxStride, dlOffset + within), " vbuf" sv, false };
653+ }
654+
632655auto attr_load (const ShaderConfig& config, GXAttr attr, std::string_view vidx) -> std::string {
633656 const auto & mapping = config.attrs [attr];
634657 if (mapping.attrType == GX_NONE) {
635658 return vtx_attr (config, attr);
636659 }
637- auto buf = " vbuf" sv;
638- auto offs = fmt::format (" ubuf.vtx_start + {} * {}u + {}u" , vidx, config.vtxStride , mapping.offset );
639- auto le = false ; // Vertex buffer is always big endian (for now)
640- if (mapping.attrType == GX_INDEX8) {
641- offs = fmt::format (" ubuf.array_start[{}] + raw_fetch_u8_1(&{}, {}) * {}u" , attr - GX_VA_POS, buf, offs,
642- mapping.stride );
643- buf = " abuf" sv;
644- le = mapping.le ;
645- } else if (mapping.attrType == GX_INDEX16) {
646- offs = fmt::format (" ubuf.array_start[{}] + raw_fetch_u16_1(&{}, {}, {}) * {}u" , attr - GX_VA_POS, buf, offs, le,
647- mapping.stride );
648- buf = " abuf" sv;
649- le = mapping.le ;
650- }
660+ const auto [offs, buf, le] = attr_address (mapping, attr, vidx, config.vtxStride , 0u , 0u );
651661 switch (attr) {
652662 case GX_VA_PNMTXIDX:
653663 return fmt::format (" (raw_fetch_u8_1(&{}, {}) / 3u)" , buf, offs);
@@ -668,7 +678,12 @@ auto attr_load(const ShaderConfig& config, GXAttr attr, std::string_view vidx) -
668678 return posLoad;
669679 }
670680 case GX_VA_NRM:
671- // TODO check for NBT/NBT3
681+ // NBT: normal only here; binormal/tangent loaded via attr_load_nbt_slice
682+ if (mapping.cnt > 3 ) {
683+ auto nrmMapping = mapping;
684+ nrmMapping.cnt = 3 ;
685+ return fetch_attr (nrmMapping, buf, offs, le);
686+ }
672687 return fetch_attr (mapping, buf, offs, le);
673688 case GX_VA_CLR0:
674689 case GX_VA_CLR1:
@@ -692,6 +707,42 @@ auto attr_load(const ShaderConfig& config, GXAttr attr, std::string_view vidx) -
692707 }
693708}
694709
710+ enum class NbtSlice : u8 {
711+ N,
712+ B,
713+ T,
714+ };
715+
716+ auto attr_load_nbt_slice (const ShaderConfig& config, NbtSlice slice, std::string_view vidx) -> std::string {
717+ const auto & mapping = config.attrs [GX_VA_NRM];
718+ if (mapping.attrType == GX_NONE || mapping.cnt != 9 ) {
719+ Log.fatal (" attr_load_nbt_slice: GX_TG_BINRM/TANGENT requires GX_NRM_NBT or GX_NRM_NBT3" );
720+ }
721+ const auto sliceIdx = static_cast <u32 >(slice);
722+ const auto compsize = comp_type_size (GX_VA_NRM, static_cast <GXCompType>(mapping.compType ));
723+ u32 dlExtra = 0 ;
724+ if (mapping.nbt3 ) {
725+ if (mapping.attrType == GX_INDEX8) {
726+ dlExtra = sliceIdx;
727+ } else if (mapping.attrType == GX_INDEX16) {
728+ dlExtra = sliceIdx * 2u ;
729+ }
730+ }
731+ const u32 within = sliceIdx * 3u * compsize;
732+ const auto [offs, buf, le] = attr_address (mapping, GX_VA_NRM, vidx, config.vtxStride , dlExtra, within);
733+ auto sliceMapping = mapping;
734+ sliceMapping.cnt = 3 ;
735+ return fetch_attr (sliceMapping, buf, offs, le);
736+ }
737+
738+ static constexpr std::string_view nbt_slice_local (NbtSlice slice) noexcept {
739+ return slice == NbtSlice::B ? " in_binrm" : " in_tangent" ;
740+ }
741+
742+ static constexpr bool is_emboss_texgen (GXTexGenType type) noexcept {
743+ return type >= GX_TG_BUMP0 && type <= GX_TG_BUMP7;
744+ }
745+
695746auto lighting_func (const ShaderConfig& config, const ColorChannelConfig& cc, u8 i, bool alpha) -> std::string {
696747 std::string_view swizzle = alpha ? " .a" sv : " " sv;
697748 std::string outVar;
@@ -783,7 +834,7 @@ auto lighting_func(const ShaderConfig& config, const ColorChannelConfig& cc, u8
783834 alpha ? " a" sv : " " sv);
784835}
785836
786- wgpu::ShaderModule build_shader (const ShaderConfig& config) noexcept {
837+ std::string build_shader_source (const ShaderConfig& config) noexcept {
787838 ZoneScoped;
788839 const auto hash = xxh3_hash (config);
789840 const auto info = build_shader_info (config);
@@ -916,6 +967,24 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config) noexcept {
916967 vtxXfrAttrsPre += fmt::format (" \n let {} = {};" , vtx_attr (config, attr), attr_load (config, attr, vidxAttr));
917968 }
918969 }
970+ bool needsBinrm = false ;
971+ bool needsTangent = false ;
972+ for (int i = 0 ; i < info.sampledTexCoords .size (); ++i) {
973+ if (!info.sampledTexCoords .test (i)) {
974+ continue ;
975+ }
976+ const bool emboss = is_emboss_texgen (config.tcgs [i].type );
977+ needsBinrm = needsBinrm || config.tcgs [i].src == GX_TG_BINRM || emboss;
978+ needsTangent = needsTangent || config.tcgs [i].src == GX_TG_TANGENT || emboss;
979+ }
980+ if (needsBinrm) {
981+ vtxXfrAttrsPre += fmt::format (" \n let {} = {};" , nbt_slice_local (NbtSlice::B),
982+ attr_load_nbt_slice (config, NbtSlice::B, vidxAttr));
983+ }
984+ if (needsTangent) {
985+ vtxXfrAttrsPre += fmt::format (" \n let {} = {};" , nbt_slice_local (NbtSlice::T),
986+ attr_load_nbt_slice (config, NbtSlice::T, vidxAttr));
987+ }
919988
920989 if (config.lineMode == 0 ) {
921990 vtxXfrAttrsPre += fmt::format (
@@ -1097,6 +1166,19 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config) noexcept {
10971166 } else {
10981167 vtxOutAttrs += fmt::format (" \n @location({}) tex{}_uv: vec2f," , vtxOutIdx++, i);
10991168 }
1169+ if (is_emboss_texgen (tcg.type )) {
1170+ // Emboss bump: offset the source texcoord by the light projected onto tangent/binormal
1171+ const u32 lightIdx = tcg.type - GX_TG_BUMP0;
1172+ vtxXfrAttrs += fmt::format (
1173+ " \n let bump_ldir{0} = normalize(ubuf.lights[{1}].pos - mv_pos);"
1174+ " \n let bump_tan{0} = vec4f(in_tangent, 0.0) * ubuf.nrm_mtx[in_pnmtxidx];"
1175+ " \n let bump_bin{0} = vec4f(in_binrm, 0.0) * ubuf.nrm_mtx[in_pnmtxidx];"
1176+ " \n out.tex{0}_uv = tc{2}_proj.xy + vec2f(dot(bump_ldir{0}, bump_tan{0}), dot(bump_ldir{0}, "
1177+ " bump_bin{0}));" ,
1178+ i, lightIdx, tcg.embossSrc );
1179+ fragmentFnPre += fmt::format (" \n var tex{0}_uv = in.tex{0}_uv.xy;" , i);
1180+ continue ;
1181+ }
11001182 if (tcg.src >= GX_TG_TEX0 && tcg.src <= GX_TG_TEX7) {
11011183 vtxXfrAttrs += fmt::format (" \n var tc{} = vec4f({}, 1.0, 1.0);" , i,
11021184 vtx_attr (config, GXAttr (GX_VA_TEX0 + (tcg.src - GX_TG_TEX0))));
@@ -1108,6 +1190,10 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config) noexcept {
11081190 vtxXfrAttrs += fmt::format (" \n var tc{} = {};" , i, vtx_attr (config, GX_VA_CLR0));
11091191 } else if (tcg.src == GX_TG_COLOR1) {
11101192 vtxXfrAttrs += fmt::format (" \n var tc{} = {};" , i, vtx_attr (config, GX_VA_CLR1));
1193+ } else if (tcg.src == GX_TG_BINRM) {
1194+ vtxXfrAttrs += fmt::format (" \n var tc{} = vec4f({}, 1.0);" , i, nbt_slice_local (NbtSlice::B));
1195+ } else if (tcg.src == GX_TG_TANGENT) {
1196+ vtxXfrAttrs += fmt::format (" \n var tc{} = vec4f({}, 1.0);" , i, nbt_slice_local (NbtSlice::T));
11111197 } else
11121198 UNLIKELY FATAL (" unhandled tcg src {}" , underlying (tcg.src ));
11131199 if (tcg.type == GX_TG_MTX2x4 || tcg.type == GX_TG_MTX3x4) {
@@ -1468,8 +1554,9 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config) noexcept {
14681554 if (discard.constant == 1 ) {
14691555 fragmentFn += " \n // Alpha compare\n discard;" ;
14701556 } else if (discard.constant != 0 ) {
1471- fragmentFn += " \n // Alpha compare"
1472- " \n let alphaCompare = u32(round(clamp(prev.a, 0.0, 1.0) * 255.0));" ;
1557+ fragmentFn +=
1558+ " \n // Alpha compare"
1559+ " \n let alphaCompare = u32(round(clamp(prev.a, 0.0, 1.0) * 255.0));" ;
14731560 fragmentFn += fmt::format (" \n if ({}) {{ discard; }}" , discard.expr );
14741561 }
14751562 }
@@ -1844,6 +1931,13 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4f {{{6}{5}
18441931 Log.info (" Generated shader: {}" , shaderSource);
18451932 }
18461933
1934+ return shaderSource;
1935+ }
1936+
1937+ wgpu::ShaderModule build_shader (const ShaderConfig& config) noexcept {
1938+ ZoneScoped;
1939+ const auto shaderSource = build_shader_source (config);
1940+ const auto hash = xxh3_hash (config);
18471941 wgpu::ShaderSourceWGSL wgslDescriptor{};
18481942 wgslDescriptor.code = shaderSource.c_str ();
18491943 const auto label = fmt::format (" GX Shader {:x}" , hash);
0 commit comments