#include "EffekseerMaterialCompilerMetal.h" #include "../3rdParty/LLGI/src/Metal/LLGI.CompilerMetal.h" #include "../Common/ShaderGeneratorCommon.h" #include namespace Effekseer { static void Serialize(std::vector& dst, const LLGI::CompilerResult& result) { uint32_t binarySize = 0; binarySize += sizeof(uint32_t); for (size_t i = 0; i < result.Binary.size(); i++) { binarySize += sizeof(uint32_t); binarySize += result.Binary[i].size(); } dst.resize(binarySize); uint32_t offset = 0; uint32_t count = result.Binary.size(); memcpy(dst.data() + offset, &count, sizeof(int32_t)); offset += sizeof(int32_t); for (size_t i = 0; i < result.Binary.size(); i++) { uint32_t size = result.Binary[i].size(); memcpy(dst.data() + offset, &size, sizeof(int32_t)); offset += sizeof(int32_t); memcpy(dst.data() + offset, result.Binary[i].data(), result.Binary[i].size()); offset += result.Binary[i].size(); } } namespace Metal { static const char* material_light_vs = R"( float3 GetLightDirection(constant ShaderUniform1& u) { return float3(0,0,0); } float3 GetLightColor(constant ShaderUniform1& u) { return float3(0,0,0); } float3 GetLightAmbientColor(constant ShaderUniform1& u) { return float3(0,0,0); } )"; static const char* material_light_ps = R"( float3 GetLightDirection(constant ShaderUniform2& u) { return u.lightDirection.xyz; } float3 GetLightColor(constant ShaderUniform2& u) { return u.lightColor.xyz; } float3 GetLightAmbientColor(constant ShaderUniform2& u) { return u.lightAmbientColor.xyz; } )"; static const char* material_common_define = R"( #include #pragma clang diagnostic ignored "-Wparentheses-equality" using namespace metal; #define FRAC fract #define LERP mix template inline auto MOD(T1 x, T2 y) -> decltype(x - y * floor(x/y)) { return x - y * floor(x/y); } #define FLT_EPSILON 1.192092896e-07f static inline __attribute__((always_inline)) float3 PositivePow(thread const float3& base, thread const float3& power) { return pow(fast::max(abs(base), float3(FLT_EPSILON, FLT_EPSILON, FLT_EPSILON)), power); } static inline __attribute__((always_inline)) float3 LinearToSRGB(thread const float3& c) { return fast::max(1.055 * PositivePow(c, 0.416666667) - 0.055, 0.0); } static inline __attribute__((always_inline)) float4 LinearToSRGB(thread const float4& c) { float3 param = c.xyz; return float4(LinearToSRGB(param), c.w); } static inline __attribute__((always_inline)) float4 ConvertFromSRGBTexture(thread const float4& c, constant float4& predefined_uniform) { if (predefined_uniform.z == 0.0) { return c; } float4 param = c; return LinearToSRGB(param); } static inline __attribute__((always_inline)) float3 SRGBToLinear(thread const float3& c) { return fast::min(c, c * (c * (c * 0.305306011 + 0.682171111) + 0.012522878)); } static inline __attribute__((always_inline)) float4 SRGBToLinear(thread const float4& c) { float3 param = c.xyz; return float4(SRGBToLinear(param), c.w); } static inline __attribute__((always_inline)) float4 ConvertToScreen(thread const float4& c, constant float4& predefined_uniform) { if (predefined_uniform.z == 0.0) { return c; } float4 param = c; return SRGBToLinear(param); } )"; static const char* material_common_define_vs = R"( // Dummy float CalcDepthFade(float2 screenUV, float meshZ, float softParticleParam) { return 1.0f; } )"; static const char g_material_model_vs_src_pre[] = R"( struct ShaderInput1 { float4 a_Position [[attribute(0)]]; float3 a_Normal [[attribute(1)]]; float3 a_Binormal [[attribute(2)]]; float3 a_Tangent [[attribute(3)]]; float2 a_TexCoord [[attribute(4)]]; float4 a_Color [[attribute(5)]]; }; struct ShaderOutput1 { float4 gl_Position [[position]]; float4 v_VColor; float2 v_UV1; float2 v_UV2; float3 v_WorldP; float3 v_WorldN; float3 v_WorldT; float3 v_WorldB; float4 v_PosP; //$C_OUT1$ //$C_OUT2$ }; struct ShaderUniform1 { float4x4 ProjectionMatrix; float4x4 ModelMatrix[40]; float4 UVOffset[40]; float4 ModelColor[40]; float4 mUVInversed; float4 predefined_uniform; float4 cameraPosition; //$UNIFORMS$ }; )"; static const char g_material_model_vs_src_suf1[] = R"( vertex ShaderOutput1 main0 (ShaderInput1 i [[stage_in]], constant ShaderUniform1& u [[buffer(0)]], uint instanceIndex [[instance_id]] //$IN_TEX$ ) { ShaderOutput1 o; float4x4 modelMatrix = u.ModelMatrix[instanceIndex]; float4 uvOffset = u.UVOffset[instanceIndex]; float4 modelColor = u.ModelColor[instanceIndex]; float3x3 modelMatRot; modelMatRot[0] = modelMatrix[0].xyz; modelMatRot[1] = modelMatrix[1].xyz; modelMatRot[2] = modelMatrix[2].xyz; float3 worldPos = (modelMatrix * i.a_Position).xyz; float3 worldNormal = normalize(modelMatRot * i.a_Normal); float3 worldBinormal = normalize(modelMatRot * i.a_Binormal); float3 worldTangent = normalize(modelMatRot * i.a_Tangent); float3 objectScale = float3(1.0, 1.0, 1.0); // Calculate ObjectScale objectScale.x = length(modelMatRot * float3(1.0, 0.0, 0.0)); objectScale.y = length(modelMatRot * float3(0.0, 1.0, 0.0)); objectScale.z = length(modelMatRot * float3(0.0, 0.0, 1.0)); // UV float2 uv1 = i.a_TexCoord.xy * uvOffset.zw + uvOffset.xy; float2 uv2 = i.a_TexCoord.xy; float3 pixelNormalDir = worldNormal; float4 vcolor = modelColor; // Dummy float2 screenUV = float2(0.0f, 0.0f); float meshZ = 0.0f; )"; static const char g_material_model_vs_src_suf2[] = R"( worldPos = worldPos + worldPositionOffset; o.v_WorldP = worldPos; o.v_WorldN = worldNormal; o.v_WorldB = worldBinormal; o.v_WorldT = worldTangent; o.v_UV1 = uv1; o.v_UV2 = uv2; o.v_VColor = vcolor; o.gl_Position = u.ProjectionMatrix * float4(worldPos, 1.0); o.v_PosP = o.gl_Position; //o.v_ScreenUV.xy = o.gl_Position.xy / o.gl_Position.w; //o.v_ScreenUV.xy = float2(o.v_ScreenUV.x + 1.0, o.v_ScreenUV.y + 1.0) * 0.5; return o; } )"; static const char g_material_sprite_vs_src_pre_simple[] = R"( struct ShaderInput1 { float4 atPosition [[attribute(0)]]; float4 atColor [[attribute(1)]]; float4 atTexCoord [[attribute(2)]]; }; struct ShaderOutput1 { float4 gl_Position [[position]]; float4 v_VColor; float2 v_UV1; float2 v_UV2; float3 v_WorldP; float3 v_WorldN; float3 v_WorldT; float3 v_WorldB; float4 v_PosP; }; struct ShaderUniform1 { float4x4 uMatCamera; float4x4 uMatProjection; float4 mUVInversed; float4 predefined_uniform; float4 cameraPosition; //$UNIFORMS$ }; )"; static const char g_material_sprite_vs_src_pre[] = R"( struct ShaderInput1 { float4 atPosition [[attribute(0)]]; float4 atColor [[attribute(1)]]; float3 atNormal [[attribute(2)]]; float3 atTangent [[attribute(3)]]; float2 atTexCoord [[attribute(4)]]; float2 atTexCoord2 [[attribute(5)]]; //$C_IN1$ //$C_IN2$ }; struct ShaderOutput1 { float4 gl_Position [[position]]; float4 v_VColor; float2 v_UV1; float2 v_UV2; float3 v_WorldP; float3 v_WorldN; float3 v_WorldT; float3 v_WorldB; float4 v_PosP; //$C_OUT1$ //$C_OUT2$ }; struct ShaderUniform1 { float4x4 uMatCamera; float4x4 uMatProjection; float4 mUVInversed; float4 predefined_uniform; float4 cameraPosition; //$UNIFORMS$ }; )"; static const char g_material_sprite_vs_src_suf1_simple[] = R"( vertex ShaderOutput1 main0 (ShaderInput1 i [[stage_in]], constant ShaderUniform1& u [[buffer(0)]] //$IN_TEX$ ) { ShaderOutput1 o; float3 worldPos = i.atPosition.xyz; float3 objectScale = float3(1.0, 1.0, 1.0); // UV float2 uv1 = i.atTexCoord.xy; float2 uv2 = uv1; // NBT float3 worldNormal = float3(0.0, 0.0, 0.0); float3 worldBinormal = float3(0.0, 0.0, 0.0); float3 worldTangent = float3(0.0, 0.0, 0.0); o.v_WorldN = worldNormal; o.v_WorldB = worldBinormal; o.v_WorldT = worldTangent; float3 pixelNormalDir = worldNormal; float4 vcolor = i.atColor; // Dummy float2 screenUV = float2(0.0f, 0.0f); float meshZ = 0.0f; )"; static const char g_material_sprite_vs_src_suf1[] = R"( vertex ShaderOutput1 main0 (ShaderInput1 i [[stage_in]], constant ShaderUniform1& u [[buffer(0)]] //$IN_TEX$ ) { ShaderOutput1 o; float3 worldPos = i.atPosition.xyz; float3 objectScale = float3(1.0, 1.0, 1.0); // UV float2 uv1 = i.atTexCoord.xy; float2 uv2 = i.atTexCoord2.xy; // NBT float3 worldNormal = (i.atNormal - float3(0.5, 0.5, 0.5)) * 2.0; float3 worldTangent = (i.atTangent - float3(0.5, 0.5, 0.5)) * 2.0; float3 worldBinormal = cross(worldNormal, worldTangent); o.v_WorldN = worldNormal; o.v_WorldB = worldBinormal; o.v_WorldT = worldTangent; float3 pixelNormalDir = worldNormal; float4 vcolor = i.atColor; // Dummy float2 screenUV = float2(0.0f, 0.0f); float meshZ = 0.0f; )"; static const char g_material_sprite_vs_src_suf2[] = R"( worldPos = worldPos + worldPositionOffset; float4 cameraPos = u.uMatCamera * float4(worldPos, 1.0); cameraPos = cameraPos / cameraPos.w; o.gl_Position = u.uMatProjection * cameraPos; o.v_WorldP = worldPos; o.v_VColor = vcolor; o.v_UV1 = uv1; o.v_UV2 = uv2; o.v_PosP = o.gl_Position; //o.v_ScreenUV.xy = o.gl_Position.xy / o.gl_Position.w; //o.v_ScreenUV.xy = float2(o.v_ScreenUV.x + 1.0, o.v_ScreenUV.y + 1.0) * 0.5; return o; } )"; static const char g_material_fs_src_pre[] = R"( struct ShaderInput2 { float4 v_VColor [[ centroid_no_perspective ]]; float2 v_UV1 [[ centroid_no_perspective ]]; float2 v_UV2 [[ centroid_no_perspective ]]; float3 v_WorldP; float3 v_WorldN; float3 v_WorldT; float3 v_WorldB; float4 v_PosP; //$C_PIN1$ //$C_PIN2$ }; struct ShaderOutput2 { float4 gl_FragColor; }; struct ShaderUniform2 { float4 mUVInversedBack; float4 predefined_uniform; float4 cameraPosition; float4 reconstructionParam1; float4 reconstructionParam2; //$UNIFORMS$ }; )"; static const char g_material_fs_src_suf1[] = R"( float ReplacedDepthFade(texture2d efk_depth, sampler s_efk_depth, float4 reconstructionParam1, float4 reconstructionParam2, float magnification, float2 screenUV, float meshZ, float softParticleParam) { float backgroundZ = efk_depth.sample(s_efk_depth, screenUV).x; float distance = softParticleParam * magnification; float2 rescale = reconstructionParam1.xy; float4 params = reconstructionParam2; float2 zs = float2(backgroundZ * rescale.x + rescale.y, meshZ); float2 depth = (zs * params.w - params.y) / (params.x - zs * params.z); float dir = sign(depth.x); depth *= dir; return min(max((depth.x - depth.y) / distance, 0.0), 1.0); } #ifdef _MATERIAL_LIT_ #define lightScale 3.14 /* float saturate(float v) { return max(min(v, 1.0), 0.0); } */ float calcD_GGX(float roughness, float dotNH) { float alpha = roughness*roughness; float alphaSqr = alpha*alpha; float pi = 3.14159; float denom = dotNH * dotNH *(alphaSqr-1.0) + 1.0; return (alpha / denom) * (alpha / denom) / pi; } float calcF(float F0, float dotLH) { float dotLH5 = pow(1.0-dotLH,5.0); return F0 + (1.0-F0)*(dotLH5); } float calcG_Schlick(float roughness, float dotNV, float dotNL) { // UE4 float k = (roughness + 1.0) * (roughness + 1.0) / 8.0; // float k = roughness * roughness / 2.0; float gV = dotNV*(1.0 - k) + k; float gL = dotNL*(1.0 - k) + k; return 1.0 / (gV * gL); } float calcLightingGGX(float3 N, float3 V, float3 L, float roughness, float F0) { float3 H = normalize(V+L); float dotNL = saturate( dot(N,L) ); float dotLH = saturate( dot(L,H) ); float dotNH = saturate( dot(N,H) ) - 0.001; float dotNV = saturate( dot(N,V) ) + 0.001; float D = calcD_GGX(roughness, dotNH); float F = calcF(F0, dotLH); float G = calcG_Schlick(roughness, dotNV, dotNL); return dotNL * D * F * G / 4.0; } float3 calcDirectionalLightDiffuseColor(float3 lightColor, float3 diffuseColor, float3 normal, float3 lightDir, float ao) { float3 color = float3(0.0,0.0,0.0); float NoL = dot(normal,lightDir); color.xyz = lightColor.xyz * lightScale * max(NoL,0.0) * ao / 3.14; color.xyz = color.xyz * diffuseColor.xyz; return color; } #endif fragment ShaderOutput2 main0 (ShaderInput2 i [[stage_in]], constant ShaderUniform2& u [[buffer(0)]] //$IN_TEX$ ) { ShaderOutput2 o; float2 uv1 = i.v_UV1; float2 uv2 = i.v_UV2; float3 worldPos = i.v_WorldP; float3 worldNormal = i.v_WorldN; float3 worldTangent = i.v_WorldT; float3 worldBinormal = i.v_WorldB; float3 pixelNormalDir = worldNormal; float4 vcolor = i.v_VColor; float3 objectScale = float3(1.0, 1.0, 1.0); float2 screenUV = i.v_PosP.xy / i.v_PosP.w; float meshZ = i.v_PosP.z / i.v_PosP.w; screenUV.xy = float2(screenUV.x + 1.0, screenUV.y + 1.0) * 0.5; float2 screenUV_distort = screenUV; screenUV = float2(screenUV.x, u.mUVInversedBack.z + u.mUVInversedBack.w * screenUV.y); )"; static const char g_material_fs_src_suf2_lit[] = R"( float3 viewDir = normalize(u.cameraPosition.xyz - worldPos); float3 diffuse = calcDirectionalLightDiffuseColor(u.lightColor.xyz, baseColor, pixelNormalDir, u.lightDirection.xyz, ambientOcclusion); float3 specular = u.lightColor.xyz * lightScale * calcLightingGGX(pixelNormalDir, viewDir, u.lightDirection.xyz, roughness, 0.9); float4 Output = float4(metallic * specular + (1.0 - metallic) * diffuse + baseColor * u.lightAmbientColor.xyz * ambientOcclusion, opacity); Output.xyz = Output.xyz + emissive.xyz; if(opacityMask <= 0.0) discard_fragment(); if(opacity <= 0.0) discard_fragment(); o.gl_FragColor = ConvertToScreen(Output, u.predefined_uniform); return o; } )"; static const char g_material_fs_src_suf2_unlit[] = R"( if(opacityMask <= 0.0) discard_fragment(); if(opacity <= 0.0) discard_fragment(); o.gl_FragColor = ConvertToScreen(float4(emissive, opacity), u.predefined_uniform); return o; } )"; static const char g_material_fs_src_suf2_refraction[] = R"( float airRefraction = 1.0; float3x3 tmpvar_1; tmpvar_1[0] = u.cameraMat[0].xyz; tmpvar_1[1] = u.cameraMat[1].xyz; tmpvar_1[2] = u.cameraMat[2].xyz; float3 dir = float3x3(tmpvar_1) * pixelNormalDir; float2 distortUV = dir.xy * (refraction - airRefraction); distortUV += screenUV_distort; distortUV = float2(distortUV.x, u.mUVInversedBack.z + u.mUVInversedBack.w * distortUV.y); distortUV.y = 1.0 - distortUV.y; float4 bg = efk_background.sample(s_efk_background, distortUV); o.gl_FragColor = bg; if(opacityMask <= 0.0) discard_fragment(); if(opacity <= 0.0) discard_fragment(); return o; } )"; static const char g_getUV_helper_vs[] = "float2(IN.x, u.mUVInversed.x + u.mUVInversed.y * IN.y)"; static const char g_getUVBack_helper_vs[] = "float2(IN.x, u.mUVInversed.z + u.mUVInversed.w * IN.y)"; static const char g_getUV_helper_fs[] = "float2(IN.x, u.mUVInversedBack.x + u.mUVInversedBack.y * IN.y)"; static const char g_getUVBack_helper_fs[] = "float2(IN.x, u.mUVInversedBack.z + u.mUVInversedBack.w * IN.y)"; /* static const char g_getUV_helper_vs[] = R"( float2 OUT = IN; OUT.y = u.mUVInversed.x + u.mUVInversed.y * OUT.y; )"; static const char g_getUVBack_helper_vs[] = R"( float2 OUT = IN; OUT.y = u.mUVInversed.z + u.mUVInversed.w * OUT.y; )"; static const char g_getUV_helper_fs[] = R"( float2 OUT = IN; OUT.y = u.mUVInversedBack.x + u.mUVInversedBack.y * OUT.y; )"; static const char g_getUVBack_helper_fs[] = R"( float2 OUT = IN; OUT.y = u.mUVInversedBack.z + u.mUVInversedBack.w * OUT.y; )"; */ std::string Replace(std::string target, std::string from_, std::string to_) { std::string::size_type Pos(target.find(from_)); while (Pos != std::string::npos) { target.replace(Pos, from_.length(), to_); Pos = target.find(from_, Pos + to_.length()); } return target; } struct ShaderData { std::string CodeVS; std::string CodePS; }; std::string GetType(int32_t i) { if (i == 1) return "float"; if (i == 2) return "float2"; if (i == 3) return "float3"; if (i == 4) return "float4"; if (i == 16) return "float4x4"; assert(0); return ""; } std::string GetElement(int32_t i) { if (i == 1) return ".x"; if (i == 2) return ".xy"; if (i == 3) return ".xyz"; if (i == 4) return ".xyzw"; assert(0); return ""; } std::string GetUVReplacement(const std::string& varName, int stage) { auto helper = (stage == 0) ? g_getUV_helper_vs : g_getUV_helper_fs; return Replace(helper, "IN", varName); } std::string GetUVBackReplacement(const std::string& varName, int stage) { auto helper = (stage == 0) ? g_getUVBack_helper_vs : g_getUVBack_helper_fs; return Replace(helper, "IN", varName); } void ExportUniform(std::ostringstream& maincode, int32_t type, const char* name) { maincode << " " << GetType(type) << " " << name << ";" << std::endl; } void ExportTexture(std::ostringstream& maincode, const char* name, int& index) { maincode << ", texture2d " << name << " [[texture(" << index << ")]],"; maincode << "sampler s_" << name << " [[sampler(" << index << ")]]" << std::endl; index++; } void ExportHeader(std::ostringstream& maincode, MaterialFile* materialFile, int stage, bool isSprite) { maincode << material_common_define; if (stage == 0) { maincode << material_common_define_vs; } if (stage == 0) { if (isSprite) { if (materialFile->GetIsSimpleVertex()) { maincode << g_material_sprite_vs_src_pre_simple; } else { maincode << g_material_sprite_vs_src_pre; } } else { maincode << g_material_model_vs_src_pre; } } else { maincode << g_material_fs_src_pre; } bool hasGradient = false; bool hasNoise = false; bool hasLight = false; for (const auto& type : materialFile->RequiredMethods) { if (type == MaterialFile::RequiredPredefinedMethodType::Gradient) { hasGradient = true; } else if (type == MaterialFile::RequiredPredefinedMethodType::Noise) { hasNoise = true; } else if (type == MaterialFile::RequiredPredefinedMethodType::Light) { hasLight = true; } } if (hasGradient) { maincode << Effekseer::Shader::GetGradientFunctions(); } if (hasNoise) { maincode << Effekseer::Shader::GetNoiseFunctions(); } if (hasLight) { if (stage == 0) { maincode << material_light_vs; } else { maincode << material_light_ps; } } for (const auto& gradient : materialFile->FixedGradients) { maincode << Effekseer::Shader::GetFixedGradient(gradient.Name.c_str(), gradient.Data); } } void ExportMain( std::ostringstream& maincode, MaterialFile* materialFile, int stage, bool isSprite, MaterialShaderType shaderType, const std::string& baseCode, const std::string& textures) { std::string suf1; if (stage == 0) { if (isSprite) { if (materialFile->GetIsSimpleVertex()) { suf1 = g_material_sprite_vs_src_suf1_simple; } else { suf1 = g_material_sprite_vs_src_suf1; } } else { suf1 = g_material_model_vs_src_suf1; } suf1 = Replace(suf1, "//$IN_TEX$", textures); maincode << suf1; if (materialFile->GetCustomData1Count() > 0) { maincode << GetType(materialFile->GetCustomData1Count()) + " customData1 = "; maincode << (isSprite ? "i.atCustomData1" : "u.customData1") + GetElement(materialFile->GetCustomData1Count()) + ";\n"; maincode << "o.v_CustomData1 = customData1" + GetElement(materialFile->GetCustomData1Count()) + ";\n"; } if (materialFile->GetCustomData2Count() > 0) { maincode << GetType(materialFile->GetCustomData2Count()) + " customData2 = "; maincode << (isSprite ? "i.atCustomData2" : "u.customData2") + GetElement(materialFile->GetCustomData2Count()) + ";\n"; maincode << "o.v_CustomData2 = customData2" + GetElement(materialFile->GetCustomData2Count()) + ";\n"; } maincode << baseCode; if (isSprite) { maincode << g_material_sprite_vs_src_suf2; } else { maincode << g_material_model_vs_src_suf2; } } else { suf1 = g_material_fs_src_suf1; suf1 = Replace(suf1, "//$IN_TEX$", textures); maincode << suf1; if (materialFile->GetCustomData1Count() > 0) { maincode << GetType(materialFile->GetCustomData1Count()) + " customData1 = i.v_CustomData1;\n"; } if (materialFile->GetCustomData2Count() > 0) { maincode << GetType(materialFile->GetCustomData2Count()) + " customData2 = i.v_CustomData2;\n"; } maincode << baseCode; if (shaderType == MaterialShaderType::Refraction || shaderType == MaterialShaderType::RefractionModel) { maincode << g_material_fs_src_suf2_refraction; } else { if (materialFile->GetShadingModel() == Effekseer::ShadingModelType::Lit) { maincode << g_material_fs_src_suf2_lit; } else if (materialFile->GetShadingModel() == Effekseer::ShadingModelType::Unlit) { maincode << g_material_fs_src_suf2_unlit; } } } } ShaderData GenerateShader(MaterialFile* materialFile, MaterialShaderType shaderType, int32_t maximumUniformCount, int32_t maximumTextureCount) { bool isSprite = shaderType == MaterialShaderType::Standard || shaderType == MaterialShaderType::Refraction; bool isRefrection = materialFile->GetHasRefraction() && (shaderType == MaterialShaderType::Refraction || shaderType == MaterialShaderType::RefractionModel); ShaderData shaderData; for (int stage = 0; stage < 2; stage++) { std::ostringstream maincode; ExportHeader(maincode, materialFile, stage, isSprite); std::ostringstream userUniforms; std::ostringstream textures; int t_index = 0; if (!isSprite && stage == 0) { if (materialFile->GetCustomData1Count() > 0) { ExportUniform(userUniforms, 4, "customData1"); } if (materialFile->GetCustomData2Count() > 0) { ExportUniform(userUniforms, 4, "customData2"); } } int32_t actualTextureCount = std::min(maximumTextureCount, materialFile->GetTextureCount()); for (size_t i = 0; i < actualTextureCount; i++) { // auto textureIndex = materialFile->GetTextureIndex(i); auto textureName = materialFile->GetTextureName(i); ExportTexture(textures, textureName, t_index); } if (stage == 1) { ExportUniform(userUniforms, 4, "lightDirection"); ExportUniform(userUniforms, 4, "lightColor"); ExportUniform(userUniforms, 4, "lightAmbientColor"); } if (materialFile->GetShadingModel() == ::Effekseer::ShadingModelType::Lit && stage == 1) { maincode << "#define _MATERIAL_LIT_ 1" << std::endl; } else if (materialFile->GetShadingModel() == ::Effekseer::ShadingModelType::Unlit) { } if (isRefrection && stage == 1) { ExportUniform(userUniforms, 16, "cameraMat"); } ExportTexture(textures, "efk_background", t_index); ExportTexture(textures, "efk_depth", t_index); for (int32_t i = 0; i < materialFile->GetUniformCount(); i++) { auto uniformName = materialFile->GetUniformName(i); ExportUniform(userUniforms, 4, uniformName); } auto baseCode = std::string(materialFile->GetGenericCode()); baseCode = Replace(baseCode, "$F1$", "float"); baseCode = Replace(baseCode, "$F2$", "float2"); baseCode = Replace(baseCode, "$F3$", "float3"); baseCode = Replace(baseCode, "$F4$", "float4"); baseCode = Replace(baseCode, "$TIME$", "predefined_uniform.x"); for (size_t i = 0; i < materialFile->Gradients.size(); i++) { // TODO : remove a magic number for (size_t j = 0; j < 13; j++) { ExportUniform(userUniforms, 4, (materialFile->Gradients[i].Name + "_" + std::to_string(j)).c_str()); } } baseCode = Replace(baseCode, "$EFFECTSCALE$", "predefined_uniform.y"); baseCode = Replace(baseCode, "$LOCALTIME$", "predefined_uniform.w"); baseCode = Replace(baseCode, "$UV$", "uv"); baseCode = Replace(baseCode, "$MOD", "mod"); // replace uniforms int32_t actualUniformCount = std::min(maximumUniformCount, materialFile->GetUniformCount()); for (size_t i = 0; i < actualUniformCount; i++) { auto name = materialFile->GetUniformName(i); baseCode = Replace(baseCode, name, std::string("u.") + name); } for (size_t i = actualUniformCount; i < materialFile->GetUniformCount(); i++) { auto name = materialFile->GetUniformName(i); baseCode = Replace(baseCode, name, std::string("float4(0,0,0,0)")); } for (size_t i = 0; i < materialFile->Gradients.size(); i++) { const auto name = materialFile->Gradients[i].Name + "_"; baseCode = Replace(baseCode, name, std::string("u.") + name); } baseCode = Replace(baseCode, "predefined_uniform", std::string("u.") + "predefined_uniform"); baseCode = Replace(baseCode, "cameraPosition", std::string("u.") + "cameraPosition"); // replace textures for (size_t i = 0; i < actualTextureCount; i++) { std::string prefix; std::string suffix; if (materialFile->GetTextureColorType(i) == Effekseer::TextureColorType::Color) { prefix = "ConvertFromSRGBTexture("; suffix = ",u.predefined_uniform)"; } auto textureIndex = materialFile->GetTextureIndex(i); auto textureName = std::string(materialFile->GetTextureName(i)); std::string keyP = "$TEX_P" + std::to_string(textureIndex) + "$"; std::string keyS = "$TEX_S" + std::to_string(textureIndex) + "$"; std::size_t posP = baseCode.find(keyP); while (posP != std::string::npos) { std::size_t posS = baseCode.find(keyS, posP); if (posS == std::string::npos) break; // get var between prefix and suffix std::size_t varPos = posP + keyP.length(); std::string varName = baseCode.substr(varPos, posS - varPos); std::ostringstream texSample; texSample << prefix; texSample << textureName << ".sample(s_" << textureName << ", "; texSample << GetUVReplacement(varName, stage) << ")"; texSample << suffix; baseCode = baseCode.replace(posP, posS + keyS.length() - posP, texSample.str()); posP = baseCode.find(keyP, posP + texSample.str().length()); } } // invalid texture for (size_t i = actualTextureCount; i < materialFile->GetTextureCount(); i++) { auto textureIndex = materialFile->GetTextureIndex(i); auto textureName = std::string(materialFile->GetTextureName(i)); std::string keyP = "$TEX_P" + std::to_string(textureIndex) + "$"; std::string keyS = "$TEX_S" + std::to_string(textureIndex) + "$"; baseCode = Replace(baseCode, keyP, "float4("); baseCode = Replace(baseCode, keyS, ",0.0,1.0)"); } // Depth if (stage == 1) { baseCode = Replace(baseCode, "CalcDepthFade(", "ReplacedDepthFade(efk_depth, s_efk_depth, u.reconstructionParam1, u.reconstructionParam2,u.predefined_uniform.y,"); } if (std::find(materialFile->RequiredMethods.begin(), materialFile->RequiredMethods.end(), MaterialFile::RequiredPredefinedMethodType::Light) != materialFile->RequiredMethods.end()) { baseCode = Replace(baseCode, "GetLightDirection()", "GetLightDirection(u)"); baseCode = Replace(baseCode, "GetLightColor()", "GetLightColor(u)"); baseCode = Replace(baseCode, "GetLightAmbientColor()", "GetLightAmbientColor(u)"); } ExportMain(maincode, materialFile, stage, isSprite, shaderType, baseCode, textures.str()); maincode.str(Replace(maincode.str(), "//$UNIFORMS$", userUniforms.str())); if (stage == 0) { shaderData.CodeVS = maincode.str(); } else { shaderData.CodePS = maincode.str(); } } // custom data if (materialFile->GetCustomData1Count() > 0) { if (isSprite) { shaderData.CodeVS = Replace(shaderData.CodeVS, "//$C_IN1$", GetType(materialFile->GetCustomData1Count()) + " atCustomData1 [[attribute(6)]];"); } shaderData.CodeVS = Replace(shaderData.CodeVS, "//$C_OUT1$", GetType(materialFile->GetCustomData1Count()) + " v_CustomData1;"); shaderData.CodePS = Replace(shaderData.CodePS, "//$C_PIN1$", GetType(materialFile->GetCustomData1Count()) + " v_CustomData1;"); } if (materialFile->GetCustomData2Count() > 0) { if (isSprite) { shaderData.CodeVS = Replace(shaderData.CodeVS, "//$C_IN2$", GetType(materialFile->GetCustomData2Count()) + " atCustomData2 [[attribute(7)]];"); } shaderData.CodeVS = Replace(shaderData.CodeVS, "//$C_OUT2$", GetType(materialFile->GetCustomData2Count()) + " v_CustomData2;"); shaderData.CodePS = Replace(shaderData.CodePS, "//$C_PIN2$", GetType(materialFile->GetCustomData2Count()) + " v_CustomData2;"); } return shaderData; } } // namespace Metal } // namespace Effekseer namespace Effekseer { class CompiledMaterialBinaryMetal : public CompiledMaterialBinary, public ReferenceObject { private: std::array, static_cast(MaterialShaderType::Max)> vertexShaders_; std::array, static_cast(MaterialShaderType::Max)> pixelShaders_; public: CompiledMaterialBinaryMetal() { } virtual ~CompiledMaterialBinaryMetal() { } void SetVertexShaderData(MaterialShaderType type, const std::vector& data) { vertexShaders_.at(static_cast(type)) = data; } void SetPixelShaderData(MaterialShaderType type, const std::vector& data) { pixelShaders_.at(static_cast(type)) = data; } const uint8_t* GetVertexShaderData(MaterialShaderType type) const override { return vertexShaders_.at(static_cast(type)).data(); } int32_t GetVertexShaderSize(MaterialShaderType type) const override { return vertexShaders_.at(static_cast(type)).size(); } const uint8_t* GetPixelShaderData(MaterialShaderType type) const override { return pixelShaders_.at(static_cast(type)).data(); } int32_t GetPixelShaderSize(MaterialShaderType type) const override { return pixelShaders_.at(static_cast(type)).size(); } int AddRef() override { return ReferenceObject::AddRef(); } int Release() override { return ReferenceObject::Release(); } int GetRef() override { return ReferenceObject::GetRef(); } }; CompiledMaterialBinary* MaterialCompilerMetal::Compile(MaterialFile* materialFile, int32_t maximumUniformCount, int32_t maximumTextureCount) { auto binary = new CompiledMaterialBinaryMetal(); // auto compiler = LLGI::CreateSharedPtr(new LLGI::CompilerMetal()); auto convertToVectorVS = [](const std::string& str) -> std::vector { std::vector ret; std::vector buffer; // HACK buffer.reserve(7 + str.size() + 1); buffer.push_back('m'); buffer.push_back('t'); buffer.push_back('l'); buffer.push_back('c'); buffer.push_back('o'); buffer.push_back('d'); buffer.push_back('e'); auto len = str.size() + 1; for (int i = 0; i < len; i++) { buffer.push_back(str[i]); } buffer[buffer.size() - 1] = 0; LLGI::CompilerResult result; result.Binary.resize(1); result.Binary[0].resize(buffer.size()); memcpy(result.Binary[0].data(), buffer.data(), buffer.size()); // compiler->Compile(result, str.c_str(), LLGI::ShaderStageType::Vertex); if (result.Binary.size() > 0) { Serialize(ret, result); } else { std::cout << "VertexShader Compile Error" << std::endl; std::cout << result.Message << std::endl; std::cout << str << std::endl; } return ret; }; auto convertToVectorPS = [](const std::string& str) -> std::vector { std::vector ret; std::vector buffer; // HACK buffer.reserve(7 + str.size() + 1); buffer.push_back('m'); buffer.push_back('t'); buffer.push_back('l'); buffer.push_back('c'); buffer.push_back('o'); buffer.push_back('d'); buffer.push_back('e'); auto len = str.size() + 1; for (int i = 0; i < len; i++) { buffer.push_back(str[i]); } buffer[buffer.size() - 1] = 0; LLGI::CompilerResult result; result.Binary.resize(1); result.Binary[0].resize(buffer.size()); memcpy(result.Binary[0].data(), buffer.data(), buffer.size()); // compiler->Compile(result, str.c_str(), LLGI::ShaderStageType::Pixel); if (result.Binary.size() > 0) { Serialize(ret, result); } else { std::cout << "PixelShader Compile Error" << std::endl; std::cout << result.Message << std::endl; std::cout << str << std::endl; } return ret; }; auto saveBinary = [&materialFile, &binary, &convertToVectorVS, &convertToVectorPS, &maximumUniformCount, &maximumTextureCount](MaterialShaderType type) { auto shader = Metal::GenerateShader(materialFile, type, maximumUniformCount, maximumTextureCount); binary->SetVertexShaderData(type, convertToVectorVS(shader.CodeVS)); binary->SetPixelShaderData(type, convertToVectorPS(shader.CodePS)); }; if (materialFile->GetHasRefraction()) { saveBinary(MaterialShaderType::Refraction); saveBinary(MaterialShaderType::RefractionModel); } saveBinary(MaterialShaderType::Standard); saveBinary(MaterialShaderType::Model); return binary; } CompiledMaterialBinary* MaterialCompilerMetal::Compile(MaterialFile* materialFile) { return Compile(materialFile, Effekseer::UserUniformSlotMax, Effekseer::UserTextureSlotMax); } } // namespace Effekseer #ifdef __SHARED_OBJECT__ extern "C" { #ifdef _WIN32 #define EFK_EXPORT __declspec(dllexport) #else #define EFK_EXPORT #endif EFK_EXPORT Effekseer::MaterialCompiler* EFK_STDCALL CreateCompiler() { return new Effekseer::MaterialCompilerMetal(); } } #endif