#ifndef TYFLOW_VAT_INCLUDED #define TYFLOW_VAT_INCLUDED #define TYFLOW_VAT_SKIN_MAX_BONES 7 #if defined(_VAT) && defined(_VAT_TYFLOW) && \ !defined(_TYFLOW_VAT_ABSOLUTE) && \ !defined(_TYFLOW_VAT_RELATIVE) && \ !defined(_TYFLOW_VAT_SKIN_R) && \ !defined(_TYFLOW_VAT_SKIN_PR) && \ !defined(_TYFLOW_VAT_SKIN_PRSAVE) && \ !defined(_TYFLOW_VAT_SKIN_PRSXYZ) #define _TYFLOW_VAT_ABSOLUTE #endif #if defined(_TYFLOW_VAT_SKIN_R) || \ defined(_TYFLOW_VAT_SKIN_PR) || \ defined(_TYFLOW_VAT_SKIN_PRSAVE) || \ defined(_TYFLOW_VAT_SKIN_PRSXYZ) #define TYFLOW_VAT_SKIN_MODE #endif struct TyflowVatMatrix3 { float3 row0; float3 row1; float3 row2; float3 row3; }; struct TyflowVatTMParts { float3 pos; float4 rot; float3 scale; }; inline int TyflowVatUnpackIntRGBA(int4 bytes) { return ((bytes.x << 24) + (bytes.y << 16) + (bytes.z << 8) + (bytes.w << 0)); } inline float TyflowVatUnpackFloatRGBA(int4 bytes) { int sign = (bytes.r & 128) > 0 ? -1 : 1; int expR = (bytes.r & 127) << 1; int expG = bytes.g >> 7; int exponent = expR + expG; int signifG = (bytes.g & 127) << 16; int signifB = bytes.b << 8; float significand = (signifG + signifB + bytes.a) / pow(2, 23); significand += 1; return sign * significand * pow(2, exponent - 127); } inline half TyflowVatUnpackHalfRGBA(int2 bytes) { uint value = (bytes.x << 8) | bytes.y; uint sign = (value & 0x8000) > 0; int exponent = (value & 0x7C00) >> 10; uint mantissa = (value & 0x03FF); if ((value & 0x7FFF) == 0) { return sign ? -0 : 0; } if (exponent == 0x001F) { if (mantissa == 0) { return sign ? -1e+28 : 1e+28; } return 1e+28; } if (exponent > 0) { float result = pow(2.0, exponent - 15) * (1 + mantissa * (1 / 1024.0f)); return sign ? -result : result; } float subnormal = pow(2.0, -24) * mantissa; return sign ? -subnormal : subnormal; } float4 TyflowVatSampleTexel(uint x, uint y) { float2 uv = float2( ((float)x + 0.5f) * _VATTex_TexelSize.x, 1.0f - (((float)y + 0.5f) * _VATTex_TexelSize.y)); return SAMPLE_TEXTURE2D_LOD(_VATTex, sampler_point_clamp, uv, 0); } float4 TyflowVatSampleEncodedTexel(uint x, uint y) { float4 sample = TyflowVatSampleTexel(x, y); if (_LinearToGamma > 0.5f) { sample.r = LinearToGammaSpaceExact(sample.r); sample.g = LinearToGammaSpaceExact(sample.g); sample.b = LinearToGammaSpaceExact(sample.b); } return sample; } int TyflowVatTex2DInt(int arrInx) { uint width = (uint)_VATTex_TexelSize.z; uint x = (uint)arrInx % width; uint y = (uint)arrInx / width; float4 bytes = TyflowVatSampleEncodedTexel(x, y); int4 byteInts = int4(round(bytes.x * 255), round(bytes.y * 255), round(bytes.z * 255), round(bytes.w * 255)); return TyflowVatUnpackIntRGBA(byteInts.wzyx); } float TyflowVatTex2DFloat(int arrInx) { uint width = (uint)_VATTex_TexelSize.z; uint x = (uint)arrInx % width; uint y = (uint)arrInx / width; float4 bytes = TyflowVatSampleEncodedTexel(x, y); int4 byteInts = int4(round(bytes.x * 255), round(bytes.y * 255), round(bytes.z * 255), round(bytes.w * 255)); return TyflowVatUnpackFloatRGBA(byteInts.wzyx); } half TyflowVatTex2DHalf(float arrInxF) { uint arrInx = (uint)floor(arrInxF + 0.1f); uint width = (uint)_VATTex_TexelSize.z; uint x = arrInx % width; uint y = arrInx / width; float4 bytes = TyflowVatSampleEncodedTexel(x, y); int4 byteInts = int4(round(bytes.x * 255), round(bytes.y * 255), round(bytes.z * 255), round(bytes.w * 255)); if (abs(arrInxF - round(arrInxF)) > 0.25f) { return TyflowVatUnpackHalfRGBA(byteInts.wz); } return TyflowVatUnpackHalfRGBA(byteInts.yx); } half2 TyflowVatTex2DHalfs2(float arrInxF) { uint arrInx = (uint)floor(arrInxF + 0.1f); uint width = (uint)_VATTex_TexelSize.z; uint x = arrInx % width; uint y = arrInx / width; float4 bytes = TyflowVatSampleEncodedTexel(x, y); int4 byteInts = int4(round(bytes.x * 255), round(bytes.y * 255), round(bytes.z * 255), round(bytes.w * 255)); return half2(TyflowVatUnpackHalfRGBA(byteInts.yx), TyflowVatUnpackHalfRGBA(byteInts.wz)); } float3 TyflowVatMultiplyPosition(TyflowVatMatrix3 matrixValue, float3 pos) { return float3( pos.x * matrixValue.row0[0] + pos.y * matrixValue.row1[0] + pos.z * matrixValue.row2[0] + matrixValue.row3[0], pos.x * matrixValue.row0[1] + pos.y * matrixValue.row1[1] + pos.z * matrixValue.row2[1] + matrixValue.row3[1], pos.x * matrixValue.row0[2] + pos.y * matrixValue.row1[2] + pos.z * matrixValue.row2[2] + matrixValue.row3[2]); } TyflowVatMatrix3 TyflowVatQuaternionToTM(float4 quat) { TyflowVatMatrix3 matrixValue; float x = quat.x; float y = quat.y; float z = quat.z; float w = quat.w; matrixValue.row0[0] = 1 - 2 * (y * y + z * z); matrixValue.row0[1] = 2 * (x * y + z * w); matrixValue.row0[2] = 2 * (x * z - y * w); matrixValue.row1[0] = 2 * (x * y - z * w); matrixValue.row1[1] = 1 - 2 * (x * x + z * z); matrixValue.row1[2] = 2 * (y * z + x * w); matrixValue.row2[0] = 2 * (x * z + y * w); matrixValue.row2[1] = 2 * (y * z - x * w); matrixValue.row2[2] = 1 - 2 * (x * x + y * y); matrixValue.row3 = float3(0, 0, 0); return matrixValue; } TyflowVatMatrix3 TyflowVatScaleTM(TyflowVatMatrix3 matrixValue, float3 scale) { matrixValue.row0 *= scale.x; matrixValue.row1 *= scale.y; matrixValue.row2 *= scale.z; return matrixValue; } TyflowVatMatrix3 TyflowVatTranslateTM(TyflowVatMatrix3 matrixValue, float3 translation) { matrixValue.row3 = translation; return matrixValue; } float4 TyflowVatQlerp(float4 a, float4 b, float blend) { float s1 = 1.0f - blend; float s2 = dot(a, b) < 0.0f ? -blend : blend; return normalize(float4( s1 * a.x + s2 * b.x, s1 * a.y + s2 * b.y, s1 * a.z + s2 * b.z, s1 * a.w + s2 * b.w)); } int TyflowVatGetMetaDataSize() { #if defined(_TYFLOW_VAT_SKIN_PRSXYZ) return 12; #else if (_DeformingSkin > 0.5f) { return 12; } return 3; #endif } float3 TyflowVatGetTMPos(float startIndex) { float3 result; for (int i = 0; i < 3; i++) { result[i] = TyflowVatTex2DFloat(startIndex + i); } return result; } float4 TyflowVatGetTMRot(float startIndex) { half2 rotHalfs1 = TyflowVatTex2DHalfs2(startIndex); half2 rotHalfs2 = TyflowVatTex2DHalfs2(startIndex + 1); return float4(-rotHalfs1.x, -rotHalfs1.y, -rotHalfs2.x, rotHalfs2.y); } float3 TyflowVatGetTMScaleXYZ(float startIndex) { half2 scaleHalfs1 = TyflowVatTex2DHalfs2(startIndex); half2 scaleHalfs2 = TyflowVatTex2DHalfs2(startIndex + 1); return float3(scaleHalfs1.x, scaleHalfs1.y, scaleHalfs2.x); } float3 TyflowVatGetTMScaleAve(float startIndex) { half scaleHalf = TyflowVatTex2DHalf(startIndex); return float3(scaleHalf, scaleHalf, scaleHalf); } TyflowVatMatrix3 TyflowVatTMFromParts(TyflowVatTMParts parts) { TyflowVatMatrix3 matrixValue = TyflowVatQuaternionToTM(parts.rot); matrixValue = TyflowVatScaleTM(matrixValue, parts.scale); matrixValue = TyflowVatTranslateTM(matrixValue, parts.pos); return matrixValue; } TyflowVatMatrix3 TyflowVatGetVertexInvTM(int tmInx) { TyflowVatMatrix3 matrixValue; int pixelsPerTM = TyflowVatGetMetaDataSize(); float tmRowInx = 2 + (pixelsPerTM * tmInx); matrixValue.row0 = TyflowVatGetTMPos(tmRowInx); tmRowInx += 3; matrixValue.row1 = TyflowVatGetTMPos(tmRowInx); tmRowInx += 3; matrixValue.row2 = TyflowVatGetTMPos(tmRowInx); tmRowInx += 3; matrixValue.row3 = TyflowVatGetTMPos(tmRowInx); return matrixValue; } TyflowVatTMParts TyflowVatGetVertexTMPartsAtFrame(int tmInx, int frame, int numTMs) { float4 rot = float4(0, 0, 0, 1); float3 pos = float3(0, 0, 0); float3 scale = float3(1, 1, 1); float pixelsPerTM = 0; #if defined(_TYFLOW_VAT_SKIN_R) pixelsPerTM = 2; #elif defined(_TYFLOW_VAT_SKIN_PR) pixelsPerTM = 5; #elif defined(_TYFLOW_VAT_SKIN_PRSXYZ) pixelsPerTM = 7; #elif defined(_TYFLOW_VAT_SKIN_PRSAVE) pixelsPerTM = 6; #endif float frameTMInx = (2 + numTMs * TyflowVatGetMetaDataSize()) + (frame * numTMs * pixelsPerTM) + (pixelsPerTM * tmInx); #if defined(_TYFLOW_VAT_SKIN_R) pos = TyflowVatGetTMPos(2 + tmInx * TyflowVatGetMetaDataSize()); rot = TyflowVatGetTMRot(frameTMInx); #elif defined(_TYFLOW_VAT_SKIN_PR) pos = TyflowVatGetTMPos(frameTMInx); frameTMInx += 3; rot = TyflowVatGetTMRot(frameTMInx); #elif defined(_TYFLOW_VAT_SKIN_PRSXYZ) pos = TyflowVatGetTMPos(frameTMInx); frameTMInx += 3; rot = TyflowVatGetTMRot(frameTMInx); frameTMInx += 2; scale = TyflowVatGetTMScaleXYZ(frameTMInx); #elif defined(_TYFLOW_VAT_SKIN_PRSAVE) pos = TyflowVatGetTMPos(frameTMInx); frameTMInx += 3; rot = TyflowVatGetTMRot(frameTMInx); frameTMInx += 2; scale = TyflowVatGetTMScaleAve(frameTMInx); #endif TyflowVatTMParts parts; parts.pos = pos; parts.rot = rot; parts.scale = scale; return parts; } float4 TyflowVatGetVertexValueAtFrame(uint vertexIndex, int vertexCount, int frame, int frameOffset) { float4 result = float4(0, 0, 0, 0); vertexIndex += (vertexCount * frame) + (vertexCount * frameOffset); if (_RGBAEncoded > 0.5f) { if (_RGBAHalf > 0.5f) { vertexIndex *= 3; for (int i = 0; i < 3; i++) { float arrInxF = (vertexIndex + i) * 0.5f; result[i] = TyflowVatTex2DHalf(arrInxF); } } else { vertexIndex *= 3; for (int i = 0; i < 3; i++) { result[i] = TyflowVatTex2DFloat(vertexIndex + i); } } } else { uint width = (uint)_VATTex_TexelSize.z; uint x = vertexIndex % width; uint y = vertexIndex / width; result = TyflowVatSampleTexel(x, y); } return result; } float3 TyflowVatGetLocalVertexPosFromTM(float3 pos, TyflowVatMatrix3 invTM) { float3 localPos = ((pos / _ImportScale) * float3(-1, 1, 1)); return TyflowVatMultiplyPosition(invTM, localPos); } float3 TyflowVatGetLocalVertexPosFromPos(float3 pos, float boneInx) { int tmInx = 2 + (int)round(boneInx) * TyflowVatGetMetaDataSize(); float3 tmPos = float3(0, 0, 0); for (int i = 0; i < 3; i++) { tmPos[i] = TyflowVatTex2DFloat(tmInx + i); } return ((pos / _ImportScale) * float3(-1, 1, 1)) - tmPos; } float2 TyflowVatGetSkinTexcoord(AttributesParticle input, int index) { if (index == 0) return input.Custom1.xy; if (index == 1) return input.Custom2.xy; #if !defined(_FLIPBOOKBLENDING_ON) if (index == 2) return input.vatTexcoord4; #endif if (index == 3) return input.vatTexcoord5; if (index == 4) return input.vatTexcoord6; if (index == 5) return input.vatTexcoord7; if (index == 6) return input.vatTexcoord8; return float2(0, 0); } void ApplyTyflowVAT(AttributesParticle input, inout float4 positionOS, inout float3 normalOS) { #if defined(SHADOWS_DEPTH) if (_AffectsShadows < 0.5f) { return; } #endif float frameBase = _Frame; float frameCustomData = GetCustomData(NB_CUSTOM_DATA_FLAG_2, FLAGBIT_POS_2_CUSTOMDATA_VAT_FRAME, -1.0f, input.Custom1, input.Custom2); if (frameCustomData >= 0.0f) { frameBase = saturate(frameCustomData) * max(_Frames - 1.0f, 0.0f); } float frame = abs(frameBase + ((_Autoplay > 0.5f) ? (time * 30.0f * _AutoplaySpeed) : 0.0f)); frame = (_Loop > 0.5f) ? fmod(frame, _Frames) : min(frame, _Frames - 1.0f); if ((_Loop > 0.5f) && (_InterpolateLoop < 0.5f) && (frame >= _Frames - 1.0f)) { frame = _Frames - 1.0f; } uint frame0 = (uint)floor(frame); uint frame1 = (uint)ceil(frame) % (uint)_Frames; float frameInterp = frame - frame0; #if defined(TYFLOW_VAT_SKIN_MODE) if (CheckLocalFlags1(FLAG_BIT_PARTICLE_1_IS_PARTICLE_SYSTEM)) { return; } else { int numTMs = TyflowVatTex2DInt(1); float3 combinedPos = float3(0, 0, 0); float3 combinedNormal = float3(0, 0, 0); int loopCount = (_DeformingSkin > 0.5f) ? (int)_SkinBoneCount : 1; [unroll] for (int i = 0; i < TYFLOW_VAT_SKIN_MAX_BONES; i++) { if (i >= loopCount) { break; } float weight = 1.0f; float tmInx = round(input.Custom1.y); if (_DeformingSkin > 0.5f) { float2 texcoord = TyflowVatGetSkinTexcoord(input, i); tmInx = round(texcoord.x); weight = texcoord.y; } TyflowVatTMParts tmParts0 = TyflowVatGetVertexTMPartsAtFrame((int)tmInx, frame0, numTMs); if (_FrameInterpolation > 0.5f) { TyflowVatTMParts tmParts1 = TyflowVatGetVertexTMPartsAtFrame((int)tmInx, frame1, numTMs); tmParts0.pos = lerp(tmParts0.pos, tmParts1.pos, frameInterp); tmParts0.rot = TyflowVatQlerp(tmParts0.rot, tmParts1.rot, frameInterp); tmParts0.scale = lerp(tmParts0.scale, tmParts1.scale, frameInterp); } TyflowVatMatrix3 tm = TyflowVatTMFromParts(tmParts0); TyflowVatMatrix3 invStartTM; #if defined(_TYFLOW_VAT_SKIN_PRSXYZ) bool useInvStartTM = true; #else bool useInvStartTM = _DeformingSkin > 0.5f; #endif if (useInvStartTM) { invStartTM = TyflowVatGetVertexInvTM((int)tmInx); } else { invStartTM.row0 = float3(0, 0, 0); invStartTM.row1 = float3(0, 0, 0); invStartTM.row2 = float3(0, 0, 0); invStartTM.row3 = float3(0, 0, 0); } float3 localPos; if (useInvStartTM) { localPos = TyflowVatGetLocalVertexPosFromTM(positionOS.xyz, invStartTM); } else { localPos = TyflowVatGetLocalVertexPosFromPos(positionOS.xyz, tmInx); } float3 pos = TyflowVatMultiplyPosition(tm, localPos) * float3(-1, 1, 1); combinedPos += (pos * _ImportScale) * weight; #if !defined(SHADOWS_DEPTH) { float3 animatedNormal = normalOS; tm.row3 = float3(0, 0, 0); if (useInvStartTM) { invStartTM.row3 = float3(0, 0, 0); float3 localNormal = TyflowVatGetLocalVertexPosFromTM(animatedNormal, invStartTM); animatedNormal = normalize(TyflowVatMultiplyPosition(tm, localNormal)) * float3(-1, 1, 1); } else { tm = TyflowVatTranslateTM(tm, float3(0, 0, 0)); animatedNormal = normalize(TyflowVatMultiplyPosition(tm, animatedNormal * float3(-1, 1, 1))) * float3(-1, 1, 1); } combinedNormal += animatedNormal * weight; } #endif } positionOS.xyz = combinedPos; normalOS = combinedNormal; } #elif defined(_TYFLOW_VAT_ABSOLUTE) || defined(_TYFLOW_VAT_RELATIVE) { float2 tyflowVatIndexData = CheckLocalFlags1(FLAG_BIT_PARTICLE_1_IS_PARTICLE_SYSTEM) ? input.texcoords.zw : input.Custom1.xy; uint vertexIndex = (uint)round(tyflowVatIndexData.x); uint vertexCount = (uint)round(tyflowVatIndexData.y); float4 vertexOffset0 = TyflowVatGetVertexValueAtFrame(vertexIndex, vertexCount, frame0, 0); float4 vertexOffset = vertexOffset0; if (_FrameInterpolation > 0.5f) { float4 vertexOffset1 = TyflowVatGetVertexValueAtFrame(vertexIndex, vertexCount, frame1, 0); vertexOffset = lerp(vertexOffset0, vertexOffset1, frameInterp); } #if defined(_TYFLOW_VAT_RELATIVE) positionOS.xyz += (vertexOffset * float4(-1, 1, 1, 1) * _ImportScale).xyz; #else positionOS.xyz = (vertexOffset * float4(-1, 1, 1, 1) * _ImportScale).xyz; #endif #if !defined(SHADOWS_DEPTH) if (_VATIncludesNormals > 0.5f) { float4 normal0 = TyflowVatGetVertexValueAtFrame(vertexIndex, vertexCount, frame0, (int)_Frames); float4 animatedNormal = normal0; if (_FrameInterpolation > 0.5f) { float4 normal1 = TyflowVatGetVertexValueAtFrame(vertexIndex, vertexCount, frame1, (int)_Frames); animatedNormal = lerp(normal0, normal1, frameInterp) * float4(-1, 1, 1, 1); } normalOS = normalize(animatedNormal.xyz); } #endif } #endif } #endif