anim + bp preview + bump snooper

This commit is contained in:
Asval
2026-09-05 17:04:59 +02:00
parent 83b6a46579
commit 1f28ff7270
26 changed files with 88 additions and 1111 deletions

View File

@@ -103,24 +103,6 @@
<None Remove="Resources\edit.png" />
<None Remove="Resources\go_to_directory.png" />
<None Remove="Resources\npcleftside.png" />
<None Remove="Resources\default.frag" />
<None Remove="Resources\default.vert" />
<None Remove="Resources\spline.vert" />
<None Remove="Resources\grid.frag" />
<None Remove="Resources\grid.vert" />
<None Remove="Resources\skybox.frag" />
<None Remove="Resources\skybox.vert" />
<None Remove="Resources\framebuffer.frag" />
<None Remove="Resources\framebuffer.vert" />
<None Remove="Resources\outline.frag" />
<None Remove="Resources\outline.vert" />
<None Remove="Resources\picking.frag" />
<None Remove="Resources\picking.vert" />
<None Remove="Resources\light.frag" />
<None Remove="Resources\light.vert" />
<None Remove="Resources\bone.frag" />
<None Remove="Resources\bone.vert" />
<None Remove="Resources\collision.vert" />
<None Remove="Resources\fa-brands-400.otf" />
<None Remove="Resources\fa-solid-900.otf" />
<None Remove="Resources\fa-regular-400.otf" />
@@ -129,29 +111,11 @@
<ItemGroup>
<EmbeddedResource Include="Resources\Json.xshd" />
<EmbeddedResource Include="Resources\Ini.xshd" />
<EmbeddedResource Include="Resources\spline.vert" />
<EmbeddedResource Include="Resources\Verse.xshd" />
<EmbeddedResource Include="Resources\Xml.xshd" />
<EmbeddedResource Include="Resources\Cpp.xshd" />
<EmbeddedResource Include="Resources\Lua.xshd" />
<EmbeddedResource Include="Resources\Changelog.xshd" />
<EmbeddedResource Include="Resources\default.frag" />
<EmbeddedResource Include="Resources\default.vert" />
<EmbeddedResource Include="Resources\grid.frag" />
<EmbeddedResource Include="Resources\grid.vert" />
<EmbeddedResource Include="Resources\skybox.frag" />
<EmbeddedResource Include="Resources\skybox.vert" />
<EmbeddedResource Include="Resources\framebuffer.frag" />
<EmbeddedResource Include="Resources\framebuffer.vert" />
<EmbeddedResource Include="Resources\outline.frag" />
<EmbeddedResource Include="Resources\outline.vert" />
<EmbeddedResource Include="Resources\picking.frag" />
<EmbeddedResource Include="Resources\picking.vert" />
<EmbeddedResource Include="Resources\light.frag" />
<EmbeddedResource Include="Resources\light.vert" />
<EmbeddedResource Include="Resources\bone.frag" />
<EmbeddedResource Include="Resources\bone.vert" />
<EmbeddedResource Include="Resources\collision.vert" />
<EmbeddedResource Include="Resources\fa-brands-400.otf" />
<EmbeddedResource Include="Resources\fa-solid-900.otf" />
<EmbeddedResource Include="Resources\fa-regular-400.otf" />

View File

@@ -0,0 +1,16 @@
using CUE4Parse_Conversion.Options;
using FModel.Settings;
using Snooper.Hosting;
namespace FModel.Framework;
public sealed class FModelBridgeHost : IBridgeHost
{
public string Name => "FModel";
public string ExportDirectory => UserSettings.Default.ModelDirectory;
public bool OwnsLoadOptions => true;
public bool CanBrowseAssets => true;
public ExportOptions CreateExportOptions() => UserSettings.GetExportOptions();
}

View File

@@ -807,6 +807,19 @@
HorizontalAlignment="Right" VerticalAlignment="Stretch"
HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
<StackPanel Orientation="Horizontal">
<StatusBarItem Margin="0 0 10 0" Padding="0">
<StatusBarItem.Style>
<Style TargetType="StatusBarItem">
<Style.Triggers>
<DataTrigger Binding="{Binding PendingRequest, Source={x:Static viewModels:SnooperViewModel.Instance}}" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</StatusBarItem.Style>
<TextBlock VerticalAlignment="Center" Text="{Binding PendingRequest.DisplayText, Source={x:Static viewModels:SnooperViewModel.Instance}, Mode=OneWay, StringFormat={}{0}: double-click one}" />
</StatusBarItem>
<StatusBarItem Margin="0 0 10 0" Padding="0" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
<Button x:Name="ExportSessionButton"
HorizontalContentAlignment="Stretch"

View File

@@ -1,11 +0,0 @@
#version 460 core
in vec3 fPos;
in vec3 fColor;
out vec4 FragColor;
void main()
{
FragColor = vec4(fColor, 1.0);
}

View File

@@ -1,19 +0,0 @@
#version 460 core
layout (location = 0) in vec3 vPos;
layout (location = 1) in vec3 vColor;
uniform mat4 uView;
uniform mat4 uProjection;
uniform mat4 uInstanceMatrix;
out vec3 fPos;
out vec3 fColor;
void main()
{
gl_PointSize = 7.5;
gl_Position = uProjection * uView * uInstanceMatrix * vec4(vPos, 1.0);
fPos = vec3(uInstanceMatrix * vec4(vPos, 1.0));
fColor = vColor;
}

View File

@@ -1,20 +0,0 @@
#version 460 core
layout (location = 0) in vec3 vPos;
uniform mat4 uView;
uniform mat4 uProjection;
uniform mat4 uInstanceMatrix;
uniform mat4 uCollisionMatrix;
uniform float uScaleDown;
out vec3 fPos;
out vec3 fColor;
void main()
{
gl_PointSize = 7.5;
gl_Position = uProjection * uView * uInstanceMatrix * uCollisionMatrix * vec4(vPos.xzy * uScaleDown, 1.0);
fPos = vec3(uInstanceMatrix * uCollisionMatrix * vec4(vPos.xzy * uScaleDown, 1.0));
fColor = vec3(1.0);
}

View File

@@ -1,294 +0,0 @@
#version 460 core
#define PI 3.1415926535897932384626433832795
#define MAX_UV_COUNT 8
#define MAX_LIGHT_COUNT 100
in vec3 fPos;
in vec3 fNormal;
in vec3 fTangent;
in vec2 fTexCoords;
flat in int fTexLayer;
in vec4 fColor;
struct Texture
{
sampler2D Sampler;
vec4 Color;
};
struct Boost
{
vec3 Color;
float Exponent;
};
struct AoParams
{
sampler2D Sampler;
float AmbientOcclusion;
Boost ColorBoost;
bool HasColorBoost;
};
struct Parameters
{
Texture Diffuse[MAX_UV_COUNT];
Texture Normals[MAX_UV_COUNT];
Texture SpecularMasks[MAX_UV_COUNT];
Texture Emissive[MAX_UV_COUNT];
AoParams Ao;
bool HasAo;
vec4 EmissiveRegion;
float RoughnessMin;
float RoughnessMax;
float EmissiveMult;
};
struct BaseLight
{
vec4 Color;
vec3 Position;
float Intensity;
};
//struct PointLight
//{
// BaseLight Light;
//
// float Linear;
// float Quadratic;
//};
//
//struct SpotLight
//{
// BaseLight Light;
//
// float InnerConeAngle;
// float OuterConeAngle;
// float Attenuation;
//};
struct Light {
BaseLight Base;
float InnerConeAngle;
float OuterConeAngle;
float Attenuation;
float Linear;
float Quadratic;
int Type; // 0 Point, 1 Spot
};
uniform Parameters uParameters;
uniform Light uLights[MAX_LIGHT_COUNT];
uniform int uNumLights;
uniform int uUvCount;
uniform float uOpacity;
uniform bool uHasVertexColors;
uniform vec3 uSectionColor;
uniform bool bVertexColors[6];
uniform vec3 uViewPos;
out vec4 FragColor;
int LayerToIndex()
{
return clamp(fTexLayer, 0, uUvCount - 1);
}
vec4 SamplerToVector(sampler2D s, vec2 coords)
{
return texture(s, coords);
}
vec4 SamplerToVector(sampler2D s)
{
return SamplerToVector(s, fTexCoords);
}
vec3 ComputeNormals(int layer)
{
vec3 normal = SamplerToVector(uParameters.Normals[layer].Sampler).rgb * 2.0 - 1.0;
vec3 t = normalize(fTangent);
vec3 n = normalize(fNormal);
vec3 b = -normalize(cross(n, t));
mat3 tbn = mat3(t, b, n);
return normalize(tbn * normal);
}
vec3 schlickFresnel(vec3 fLambert, float metallic, float hDotv)
{
vec3 f0 = vec3(0.04);
f0 = mix(f0, fLambert, metallic);
return f0 + (1.0 - f0) * pow(clamp(1.0 - hDotv, 0.0, 1.0), 5);
}
float ggxDistribution(float roughness, float nDoth)
{
float alpha2 = roughness * roughness * roughness * roughness;
float d = nDoth * nDoth * (alpha2- 1.0) + 1.0;
return alpha2 / (PI * d * d);
}
float geomSmith(float roughness, float dp)
{
float k = (roughness + 1.0) * (roughness + 1.0) / 8.0;
float denom = dp * (1.0 - k) + k;
return dp / denom;
}
vec3 CalcLight(int layer, vec3 normals, vec3 position, vec3 color, float attenuation, bool global)
{
vec3 fLambert = SamplerToVector(uParameters.Diffuse[layer].Sampler).rgb * uParameters.Diffuse[layer].Color.rgb;
vec3 specular_masks = SamplerToVector(uParameters.SpecularMasks[layer].Sampler).rgb;
float cavity = specular_masks.g;
if (uParameters.HasAo)
{
cavity = SamplerToVector(uParameters.Ao.Sampler).g;
}
float roughness = mix(uParameters.RoughnessMin, uParameters.RoughnessMax, specular_masks.b);
vec3 l = normalize(uViewPos - fPos);
vec3 n = normals;
vec3 v = normalize(position - fPos);
vec3 h = normalize(v + l);
float nDotH = max(dot(n, h), 0.0);
float hDotv = max(dot(h, v), 0.0);
float nDotL = max(dot(n, l), 0.0);
float nDotV = max(dot(n, v), 0.0);
vec3 f = schlickFresnel(fLambert, specular_masks.g, hDotv);
vec3 kS = f;
vec3 kD = 1.0 - kS;
kD *= 1.0 - cavity;
vec3 specBrdfNom = ggxDistribution(roughness, nDotH) * geomSmith(roughness, nDotL) * geomSmith(roughness, nDotV) * f;
float specBrdfDenom = 4.0 * nDotV * nDotL + 0.0001;
vec3 specBrdf = specBrdfNom / specBrdfDenom;
vec3 diffuseBrdf = fLambert;
if (!global) diffuseBrdf = kD * fLambert / PI;
return (diffuseBrdf + specBrdf) * color * attenuation * nDotL;
}
vec3 CalcBaseLight(int layer, vec3 normals, BaseLight base, float attenuation, bool global)
{
return CalcLight(layer, normals, base.Position, base.Color.rgb * base.Intensity, attenuation, global);
}
vec3 CalcPointLight(int layer, vec3 normals, Light light)
{
float distanceToLight = length(light.Base.Position - fPos);
float attenuation = 1.0 / (1.0 + light.Linear * distanceToLight + light.Quadratic * pow(distanceToLight, 2));
return CalcBaseLight(layer, normals, light.Base, attenuation, true);
}
vec3 CalcSpotLight(int layer, vec3 normals, Light light)
{
vec3 v = normalize(light.Base.Position - fPos);
float inner = cos(radians(light.InnerConeAngle));
float outer = cos(radians(light.OuterConeAngle));
float distanceToLight = length(light.Base.Position - fPos);
float theta = dot(v, normalize(-vec3(0, -1, 0)));
float epsilon = inner - outer;
float attenuation = 1.0 / (1.0 + light.Attenuation * pow(distanceToLight, 2));
light.Base.Intensity *= smoothstep(0.0, 1.0, (theta - outer) / epsilon);
if(theta > outer)
{
return CalcBaseLight(layer, normals, light.Base, attenuation, true);
}
else
{
return vec3(0.0);
}
}
void main()
{
int layer = LayerToIndex();
vec3 normals = ComputeNormals(layer);
vec3 lightDir = normalize(uViewPos - fPos);
float diffuseFactor = max(dot(normals, lightDir), 0.4);
if (bVertexColors[1])
{
FragColor = vec4(diffuseFactor * uSectionColor, uOpacity);
}
else if (bVertexColors[2] && uHasVertexColors)
{
FragColor = vec4(diffuseFactor * fColor.rgb, fColor.a);
}
else if (bVertexColors[3])
{
FragColor = vec4(normals, uOpacity);
}
else if (bVertexColors[4])
{
vec4 diffuse = SamplerToVector(uParameters.Diffuse[0].Sampler);
FragColor = vec4(diffuseFactor * diffuse.rgb, diffuse.a);
}
else
{
vec4 diffuse = SamplerToVector(uParameters.Diffuse[layer].Sampler);
vec3 result = diffuseFactor * diffuse.rgb * uParameters.Diffuse[layer].Color.rgb;
if (uParameters.HasAo)
{
vec3 m = SamplerToVector(uParameters.Ao.Sampler).rgb;
if (uParameters.Ao.HasColorBoost)
{
vec3 color = uParameters.Ao.ColorBoost.Color * uParameters.Ao.ColorBoost.Exponent;
result = mix(result, result * color, m.b);
}
result *= m.r;
}
vec2 coords = fTexCoords;
if (coords.x > uParameters.EmissiveRegion.x &&
coords.y > uParameters.EmissiveRegion.y &&
coords.x < uParameters.EmissiveRegion.z &&
coords.y < uParameters.EmissiveRegion.w)
{
coords.x -= uParameters.EmissiveRegion.x;
coords.y -= uParameters.EmissiveRegion.y;
coords.x *= 1.0 / (uParameters.EmissiveRegion.z - uParameters.EmissiveRegion.x);
coords.y *= 1.0 / (uParameters.EmissiveRegion.w - uParameters.EmissiveRegion.y);
vec4 emissive = SamplerToVector(uParameters.Emissive[layer].Sampler, coords);
result += uParameters.Emissive[layer].Color.rgb * emissive.rgb * uParameters.EmissiveMult;
}
{
result += CalcLight(layer, normals, uViewPos, vec3(1.0), 1.0, false);
vec3 lights = vec3(uNumLights > 0 ? 0 : 1);
for (int i = 0; i < uNumLights; i++)
{
if (uLights[i].Type == 0)
{
lights += CalcPointLight(layer, normals, uLights[i]);
}
else if (uLights[i].Type == 1)
{
lights += CalcSpotLight(layer, normals, uLights[i]);
}
}
result *= lights; // use * to darken the scene, + to lighten it
}
result = result / (result + vec3(1.0));
FragColor = vec4(pow(result, vec3(1.0 / 2.2)), uOpacity);
}
}

View File

@@ -1,111 +0,0 @@
#version 460 core
layout (location = 1) in vec3 vPos;
layout (location = 2) in vec3 vNormal;
layout (location = 3) in vec3 vTangent;
layout (location = 4) in vec2 vTexCoords;
layout (location = 5) in int vTexLayer;
layout (location = 6) in float vColor;
layout (location = 7) in vec4 vBoneInfluence;
layout (location = 8) in vec4 vBoneInfluenceExtra;
layout (location = 9) in mat4 vInstanceMatrix;
layout (location = 13) in vec3 vMorphTargetPos;
layout (location = 14) in vec3 vMorphTargetTangent;
layout(std430, binding = 1) buffer BoneMatrices
{
mat4 uFinalBonesMatrix[];
};
layout(std430, binding = 2) buffer RestBoneMatrices
{
mat4 uRestBonesMatrix[];
};
uniform mat4 uView;
uniform mat4 uProjection;
uniform float uMorphTime;
uniform bool uIsAnimated;
out vec3 fPos;
out vec3 fNormal;
out vec3 fTangent;
out vec2 fTexCoords;
flat out int fTexLayer;
out vec4 fColor;
vec4 unpackARGB(int color)
{
float a = float((color >> 24) & 0xFF);
float r = float((color >> 16) & 0xFF);
float g = float((color >> 8) & 0xFF);
float b = float((color >> 0) & 0xFF);
return vec4(r, g, b, a);
}
vec2 unpackBoneIDsAndWeights(int packedData)
{
return vec2(float((packedData >> 16) & 0xFFFF), float(packedData & 0xFFFF));
}
void main()
{
vec4 bindPos = vec4(mix(vPos, vMorphTargetPos, uMorphTime), 1.0);
vec4 bindNormal = vec4(vNormal, 1.0);
vec4 bindTangent = vec4(mix(vTangent, vMorphTargetTangent, uMorphTime), 1.0);
vec4 finalPos = vec4(0.0);
vec4 finalNormal = vec4(0.0);
vec4 finalTangent = vec4(0.0);
if (uIsAnimated)
{
vec4 boneInfluences[2];
boneInfluences[0] = vBoneInfluence;
boneInfluences[1] = vBoneInfluenceExtra;
for (int i = 0; i < 2; i++)
{
for(int j = 0 ; j < 4; j++)
{
vec2 boneInfluence = unpackBoneIDsAndWeights(int(boneInfluences[i][j]));
int boneIndex = int(boneInfluence.x);
float weight = boneInfluence.y;
mat4 boneMatrix = uFinalBonesMatrix[boneIndex] * inverse(uRestBonesMatrix[boneIndex]);
mat4 inverseBoneMatrix = transpose(inverse(boneMatrix));
finalPos += boneMatrix * bindPos * weight;
finalNormal += inverseBoneMatrix * bindNormal * weight;
finalTangent += inverseBoneMatrix * bindTangent * weight;
}
}
finalPos = normalize(finalPos);
finalNormal = normalize(finalNormal);
finalTangent = normalize(finalTangent);
}
else if (uIsSpline)
{
GpuSplineMeshParams params = uSplineParameters[gl_InstanceID];
float distanceAlong = GetAxisValueRef(params.ForwardAxis, bindPos.xzy);
vec3 computed = ComputeRatioAlongSpline(params, distanceAlong);
mat4 sliceTransform = CalcSliceTransformAtSplineOffset(params, computed);
SetAxisValueRef(params.ForwardAxis, bindPos.xzy, 0.0);
finalPos = (sliceTransform * bindPos.xzyw).xzyw;
finalNormal = bindNormal;
finalTangent = bindTangent;
}
else
{
finalPos = bindPos;
finalNormal = bindNormal;
finalTangent = bindTangent;
}
gl_Position = uProjection * uView * vInstanceMatrix * finalPos;
fPos = vec3(vInstanceMatrix * finalPos);
fNormal = vec3(transpose(inverse(vInstanceMatrix)) * finalNormal);
fTangent = vec3(transpose(inverse(vInstanceMatrix)) * finalTangent);
fTexCoords = vTexCoords;
fTexLayer = vTexLayer;
fColor = unpackARGB(int(vColor)) / 255.0;
}

View File

@@ -1,12 +0,0 @@
#version 460 core
in vec2 fTexCoords;
uniform sampler2D screenTexture;
out vec4 FragColor;
void main()
{
FragColor = texture(screenTexture, fTexCoords);
}

View File

@@ -1,12 +0,0 @@
#version 460 core
layout (location = 0) in vec2 vPos;
layout (location = 1) in vec2 vTexCoords;
out vec2 fTexCoords;
void main()
{
gl_Position = vec4(vPos.x, vPos.y, 0.0, 1.0);
fTexCoords = vTexCoords;
}

View File

@@ -1,63 +0,0 @@
#version 460 core
// --------------------- IN ---------------------
in OUT_IN_VARIABLES {
vec3 nearPoint;
vec3 farPoint;
mat4 proj;
mat4 view;
float near;
float far;
} inVar;
// --------------------- OUT --------------------
out vec4 FragColor;
// ------------------- UNIFORM ------------------
uniform vec3 uCamDir;
vec4 grid(vec3 fragPos, float scale) {
vec2 coord = fragPos.xz * scale;
vec2 derivative = fwidth(coord);
vec2 grid = abs(fract(coord - 0.5) - 0.5) / derivative;
float line = min(grid.x, grid.y);
float minimumz = min(derivative.y, 1) * 0.1;
float minimumx = min(derivative.x, 1) * 0.1;
vec4 color = vec4(0.102, 0.102, 0.129, 1.0 - min(line, 1.0));
if(abs(fragPos.x) < minimumx)
color.z = 1.0;
if(abs(fragPos.z) < minimumz)
color.x = 1.0;
return color;
}
float computeDepth(vec3 pos) {
vec4 clip_space_pos = inVar.proj * inVar.view * vec4(pos.xyz, 1.0);
float clip_space_depth = clip_space_pos.z / clip_space_pos.w;
float far = gl_DepthRange.far;
float near = gl_DepthRange.near;
float depth = (((far-near) * clip_space_depth) + near + far) / 2.0;
return depth;
}
float computeLinearDepth(vec3 pos) {
vec4 clip_space_pos = inVar.proj * inVar.view * vec4(pos.xyz, 1.0);
float clip_space_depth = (clip_space_pos.z / clip_space_pos.w) * 2.0 - 1.0;
float linearDepth = (2.0 * inVar.near * inVar.far) / (inVar.far + inVar.near - clip_space_depth * (inVar.far - inVar.near));
return linearDepth / inVar.far;
}
void main() {
float t = -inVar.nearPoint.y / (inVar.farPoint.y - inVar.nearPoint.y);
vec3 fragPos3D = inVar.nearPoint + t * (inVar.farPoint - inVar.nearPoint);
gl_FragDepth = computeDepth(fragPos3D);
float linearDepth = computeLinearDepth(fragPos3D);
float fading = max(0, (0.5 - linearDepth));
FragColor = (grid(fragPos3D, 10) + grid(fragPos3D, 1)) * float(t > 0);
FragColor.a *= fading;
}

View File

@@ -1,36 +0,0 @@
#version 460 core
layout (location = 0) in vec3 vPos;
// --------------------- OUT ---------------------
out OUT_IN_VARIABLES {
vec3 nearPoint;
vec3 farPoint;
mat4 proj;
mat4 view;
float near;
float far;
} outVar;
uniform mat4 proj;
uniform mat4 view;
uniform float uNear;
uniform float uFar;
vec3 UnprojectPoint(vec2 xy, float z) {
mat4 viewInv = inverse(view);
mat4 projInv = inverse(proj);
vec4 unprojectedPoint = viewInv * projInv * vec4(xy, z, 1.0);
return unprojectedPoint.xyz / unprojectedPoint.w;
}
void main()
{
outVar.near = uNear;
outVar.far = uFar;
outVar.proj = proj;
outVar.view = view;
outVar.nearPoint = UnprojectPoint(vPos.xy, -1.0).xyz;
outVar.farPoint = UnprojectPoint(vPos.xy, 1.0).xyz;
gl_Position = vec4(vPos, 1.0);
}

View File

@@ -1,16 +0,0 @@
#version 460 core
uniform sampler2D uIcon;
uniform vec4 uColor;
in vec2 fTexCoords;
out vec4 FragColor;
void main()
{
vec4 color = uColor * texture(uIcon, fTexCoords);
if (color.a < 0.1) discard;
FragColor = uColor;
}

View File

@@ -1,22 +0,0 @@
#version 460 core
layout (location = 0) in vec3 vPos;
layout (location = 9) in mat4 vInstanceMatrix;
uniform mat4 uView;
uniform mat4 uProjection;
out vec2 fTexCoords;
void main()
{
float scale = 0.075;
mat4 result;
result[0] = vec4(scale, 0.0, 0.0, 0.0);
result[1] = vec4(0.0, scale, 0.0, 0.0);
result[2] = vec4(0.0, 0.0, scale, 0.0);
result[3] = vInstanceMatrix[3];
gl_Position = uProjection * uView * result * vec4(inverse(mat3(uView)) * vPos, 1.0);
fTexCoords = -vPos.xy * 0.5 + 0.5; // fits the whole rectangle
}

View File

@@ -1,8 +0,0 @@
#version 460 core
out vec4 FragColor;
void main()
{
FragColor = vec4(0.929, 0.588, 0.196, 1.0);
}

View File

@@ -1,83 +0,0 @@
#version 460 core
layout (location = 1) in vec3 vPos;
layout (location = 2) in vec3 vNormal;
layout (location = 7) in vec4 vBoneInfluence;
layout (location = 8) in vec4 vBoneInfluenceExtra;
layout (location = 9) in mat4 vInstanceMatrix;
layout (location = 13) in vec3 vMorphTargetPos;
layout(std430, binding = 1) buffer BoneMatrices
{
mat4 uFinalBonesMatrix[];
};
layout(std430, binding = 2) buffer RestBoneMatrices
{
mat4 uRestBonesMatrix[];
};
uniform mat4 uView;
uniform vec3 uViewPos;
uniform mat4 uProjection;
uniform float uMorphTime;
uniform bool uIsAnimated;
vec2 unpackBoneIDsAndWeights(int packedData)
{
return vec2(float((packedData >> 16) & 0xFFFF), float(packedData & 0xFFFF));
}
vec4 calculateScale(vec4 bindPos, vec4 bindNormal)
{
vec4 worldPos = vInstanceMatrix * bindPos;
float scaleFactor = length(uViewPos - worldPos.xyz) * 0.0035;
return transpose(inverse(vInstanceMatrix)) * bindNormal * scaleFactor;
}
void main()
{
vec4 bindPos = vec4(mix(vPos, vMorphTargetPos, uMorphTime), 1.0);
vec4 bindNormal = vec4(vNormal, 1.0);
bindPos.xyz += calculateScale(bindPos, bindNormal).xyz;
vec4 finalPos = vec4(0.0);
vec4 finalNormal = vec4(0.0);
if (uIsAnimated)
{
vec4 boneInfluences[2];
boneInfluences[0] = vBoneInfluence;
boneInfluences[1] = vBoneInfluenceExtra;
for(int i = 0 ; i < 2; i++)
{
for(int j = 0; j < 4; j++)
{
vec2 boneInfluence = unpackBoneIDsAndWeights(int(boneInfluences[i][j]));
int boneIndex = int(boneInfluence.x);
float weight = boneInfluence.y;
mat4 boneMatrix = uFinalBonesMatrix[boneIndex] * inverse(uRestBonesMatrix[boneIndex]);
finalPos += boneMatrix * bindPos * weight;
finalNormal += transpose(inverse(boneMatrix)) * bindNormal * weight;
}
}
}
else if (uIsSpline)
{
GpuSplineMeshParams params = uSplineParameters[gl_InstanceID];
float distanceAlong = GetAxisValueRef(params.ForwardAxis, bindPos.xzy);
vec3 computed = ComputeRatioAlongSpline(params, distanceAlong);
mat4 sliceTransform = CalcSliceTransformAtSplineOffset(params, computed);
SetAxisValueRef(params.ForwardAxis, bindPos.xzy, 0.0);
finalPos = (sliceTransform * bindPos.xzyw).xzyw;
finalNormal = bindNormal;
}
else
{
finalPos = bindPos;
finalNormal = bindNormal;
}
gl_Position = uProjection * uView * vInstanceMatrix * finalPos;
}

View File

@@ -1,13 +0,0 @@
#version 460 core
uniform uint uA;
uniform uint uB;
uniform uint uC;
uniform uint uD;
out uvec4 FragColor;
void main()
{
FragColor = uvec4(uA, uB, uC, uD);
}

View File

@@ -1,63 +0,0 @@
#version 460 core
layout (location = 1) in vec3 vPos;
layout (location = 7) in vec4 vBoneInfluence;
layout (location = 8) in vec4 vBoneInfluenceExtra;
layout (location = 9) in mat4 vInstanceMatrix;
layout (location = 13) in vec3 vMorphTargetPos;
layout(std430, binding = 1) buffer BoneMatrices
{
mat4 uFinalBonesMatrix[];
};
layout(std430, binding = 2) buffer RestBoneMatrices
{
mat4 uRestBonesMatrix[];
};
uniform mat4 uView;
uniform mat4 uProjection;
uniform float uMorphTime;
uniform bool uIsAnimated;
vec2 unpackBoneIDsAndWeights(int packedData)
{
return vec2(float((packedData >> 16) & 0xFFFF), float(packedData & 0xFFFF));
}
void main()
{
vec4 bindPos = vec4(mix(vPos, vMorphTargetPos, uMorphTime), 1.0);
vec4 finalPos = vec4(0.0);
if (uIsAnimated)
{
vec4 boneInfluences[2];
boneInfluences[0] = vBoneInfluence;
boneInfluences[1] = vBoneInfluenceExtra;
for(int i = 0 ; i < 2; i++)
{
for(int j = 0; j < 4; j++)
{
vec2 boneInfluence = unpackBoneIDsAndWeights(int(boneInfluences[i][j]));
int boneIndex = int(boneInfluence.x);
float weight = boneInfluence.y;
finalPos += uFinalBonesMatrix[boneIndex] * inverse(uRestBonesMatrix[boneIndex]) * bindPos * weight;
}
}
}
else if (uIsSpline)
{
GpuSplineMeshParams params = uSplineParameters[gl_InstanceID];
float distanceAlong = GetAxisValueRef(params.ForwardAxis, bindPos.xzy);
vec3 computed = ComputeRatioAlongSpline(params, distanceAlong);
mat4 sliceTransform = CalcSliceTransformAtSplineOffset(params, computed);
SetAxisValueRef(params.ForwardAxis, bindPos.xzy, 0.0);
finalPos = (sliceTransform * bindPos.xzyw).xzyw;
}
else finalPos = bindPos;
gl_Position = uProjection * uView * vInstanceMatrix * finalPos;
}

View File

@@ -1,12 +0,0 @@
#version 460 core
in vec3 fPos;
uniform samplerCube cubemap;
out vec4 FragColor;
void main()
{
FragColor = texture(cubemap, fPos);
}

View File

@@ -1,16 +0,0 @@
#version 460 core
layout (location = 0) in vec3 vPos;
uniform mat4 uView;
uniform mat4 uProjection;
out vec3 fPos;
void main()
{
fPos = vPos;
vec4 pos = uProjection * uView * vec4(vPos, 1.0);
gl_Position = pos.xyww;
}

View File

@@ -1,216 +0,0 @@
#version 460 core
// yeeted from minshu https://github.com/FabianFG/CUE4Parse/commit/61cef25b8eef4160651ee41e2b1ceefc5135803f
struct GpuSplineMeshParams {
int ForwardAxis;
float SplineBoundaryMin;
float SplineBoundaryMax;
bool bSmoothInterpRollScale;
vec3 MeshOrigin;
vec3 MeshBoxExtent;
vec3 StartPos;
float StartRoll;
vec3 StartTangent;
vec2 StartScale;
vec2 StartOffset;
vec3 EndPos;
float EndRoll;
vec3 EndTangent;
vec2 EndScale;
vec2 EndOffset;
vec3 SplineUpDir;
};
layout(std430, binding = 3) buffer SplineParameters
{
GpuSplineMeshParams uSplineParameters[];
};
uniform bool uIsSpline;
vec3 getSafeNormal(vec3 vector) {
float squareSum = dot(vector, vector);
if (squareSum == 1.0) {
return vector;
}
if (squareSum < 1e-8) {
return vec3(0.0); // Return a zero vector
}
// Calculate the scale factor to normalize the vector
float scale = inversesqrt(squareSum);
return vector * scale;
}
float GetAxisValueRef(int forwardAxis, vec3 pos)
{
switch (forwardAxis)
{
case 0: return pos.x;
case 1: return pos.y;
case 2: return pos.z;
default: return 0;
}
}
void SetAxisValueRef(int forwardAxis, inout vec3 pos, float v)
{
switch (forwardAxis)
{
case 0: pos.x = v; break;
case 1: pos.y = v; break;
case 2: pos.z = v; break;
}
}
vec3 SplineEvalTangent(GpuSplineMeshParams params, float a)
{
vec3 c = (6 * params.StartPos) + (3 * params.StartTangent) + (3 * params.EndTangent) - (6 * params.EndPos);
vec3 d = (-6 * params.StartPos) - (4 * params.StartTangent) - (2 * params.EndTangent) + (6 * params.EndPos);
vec3 e = params.StartTangent;
float a2 = a * a;
return (c * a2) + (d * a) + e;
}
vec3 SplineEvalDir(GpuSplineMeshParams params, float a)
{
return getSafeNormal(SplineEvalTangent(params, a));
}
vec3 SplineEvalPos(GpuSplineMeshParams params, float a)
{
float a2 = a * a;
float a3 = a2 * a;
return (((2 * a3) - (3 * a2) + 1) * params.StartPos) + ((a3 - (2 * a2) + a) * params.StartTangent) + ((a3 - a2) * params.EndTangent) + (((-2 * a3) + (3 * a2)) * params.EndPos);
}
vec3 ComputeRatioAlongSpline(GpuSplineMeshParams params, float distanceAlong)
{
float alpha = 0.0;
float minT = 0.0;
float maxT = 1.0;
const float SmallNumber = 1e-8;
bool bHasCustomBoundary = abs(params.SplineBoundaryMin - params.SplineBoundaryMax) > SmallNumber;
if (bHasCustomBoundary)
{
float splineLength = params.SplineBoundaryMax - params.SplineBoundaryMin;
if (splineLength > 0)
{
alpha = (distanceAlong - params.SplineBoundaryMin) / splineLength;
}
float boundMin = GetAxisValueRef(params.ForwardAxis, params.MeshOrigin - params.MeshBoxExtent);
float boundMax = GetAxisValueRef(params.ForwardAxis, params.MeshOrigin + params.MeshBoxExtent);
float boundMinT = (boundMin - params.SplineBoundaryMin) / (params.SplineBoundaryMax - params.SplineBoundaryMin);
float boundMaxT = (boundMax - params.SplineBoundaryMin) / (params.SplineBoundaryMax - params.SplineBoundaryMin);
const float MaxSplineExtrapolation = 4.0;
minT = max(-MaxSplineExtrapolation, boundMinT);
maxT = min(boundMaxT, MaxSplineExtrapolation);
}
else
{
float meshMinZ = GetAxisValueRef(params.ForwardAxis, params.MeshOrigin) - GetAxisValueRef(params.ForwardAxis, params.MeshBoxExtent);
float meshRangeZ = 2 * GetAxisValueRef(params.ForwardAxis, params.MeshBoxExtent);
if (meshRangeZ > SmallNumber) {
alpha = (distanceAlong - meshMinZ) / meshRangeZ;
}
}
return vec3(alpha, minT, maxT);
}
mat4 CalcSliceTransformAtSplineOffset(GpuSplineMeshParams params, vec3 computed)
{
float alpha = computed.x;
float minT = computed.y;
float maxT = computed.z;
float hermiteAlpha = params.bSmoothInterpRollScale ? smoothstep(0.0, 1.0, alpha) : alpha;
vec3 splinePos;
vec3 splineDir;
if (alpha < minT)
{
vec3 startTangent = SplineEvalTangent(params, minT);
splinePos = SplineEvalPos(params, minT) + (startTangent * (alpha - minT));
splineDir = getSafeNormal(startTangent);
}
else if (alpha > maxT)
{
vec3 endTangent = SplineEvalTangent(params, maxT);
splinePos = SplineEvalPos(params, maxT) + (endTangent * (alpha - maxT));
splineDir = getSafeNormal(endTangent);
}
else
{
splinePos = SplineEvalPos(params, alpha);
splineDir = SplineEvalDir(params, alpha);
}
// base
vec3 baseXVec = getSafeNormal(cross(params.SplineUpDir, splineDir));
vec3 baseYVec = getSafeNormal(cross(splineDir, baseXVec));
// Offset the spline by the desired amount
vec2 sliceOffset = mix(params.StartOffset, params.EndOffset, hermiteAlpha);
splinePos += sliceOffset.x * baseXVec;
splinePos += sliceOffset.y * baseYVec;
// Apply Roll
float useRoll = mix(params.StartRoll, params.EndRoll, hermiteAlpha);
float cosAng = cos(useRoll);
float sinAng = sin(useRoll);
vec3 xVec = (cosAng * baseXVec) - (sinAng * baseYVec);
vec3 yVec = (cosAng * baseYVec) + (sinAng * baseXVec);
// Find Scale
vec2 useScale = mix(params.StartScale, params.EndScale, hermiteAlpha);
// Build overall transform
mat4 sliceTransform = mat4(0);
vec3 scale;
switch (params.ForwardAxis) {
case 0:
sliceTransform[0] = vec4(splineDir, 0.0);
sliceTransform[1] = vec4(xVec, 0.0);
sliceTransform[2] = vec4(yVec, 0.0);
sliceTransform[3] = vec4(splinePos, 1.0);
scale = vec3(1.0, useScale.x, useScale.y);
break;
case 1:
sliceTransform[0] = vec4(yVec, 0.0);
sliceTransform[1] = vec4(splineDir, 0.0);
sliceTransform[2] = vec4(xVec, 0.0);
sliceTransform[3] = vec4(splinePos, 1.0);
scale = vec3(useScale.y, 1.0, useScale.x);
break;
case 2:
sliceTransform[0] = vec4(xVec, 0.0);
sliceTransform[1] = vec4(yVec, 0.0);
sliceTransform[2] = vec4(splineDir, 0.0);
sliceTransform[3] = vec4(splinePos, 1.0);
scale = vec3(useScale.x, useScale.y, 1.0);
break;
}
mat4 scaleMatrix = mat4(
vec4(scale.x, 0.0, 0.0, 0.0),
vec4(0.0, scale.y, 0.0, 0.0),
vec4(0.0, 0.0, scale.z, 0.0),
vec4(0.0, 0.0, 0.0, 1.0)
);
return sliceTransform * scaleMatrix;
}

View File

@@ -68,6 +68,7 @@ using CUE4Parse.UE4.Wwise;
using CUE4Parse.Utils;
using CUE4Parse_Conversion.Exporters;
using CUE4Parse_Conversion.Sounds;
using CUE4Parse.UE4.Assets.Exports.GeometryCollection;
using EpicManifestParser;
using EpicManifestParser.UE;
using FModel.Creator;
@@ -98,20 +99,6 @@ public class CUE4ParseViewModel : ViewModel
private static readonly HttpClient _chunkClient = ManifestParseOptions.CreateDefaultClient();
private bool _modelIsOverwritingMaterial;
public bool ModelIsOverwritingMaterial
{
get => _modelIsOverwritingMaterial;
set => SetProperty(ref _modelIsOverwritingMaterial, value);
}
private bool _modelIsWaitingAnimation;
public bool ModelIsWaitingAnimation
{
get => _modelIsWaitingAnimation;
set => SetProperty(ref _modelIsWaitingAnimation, value);
}
public AbstractVfsFileProvider Provider { get; }
public GameDirectoryViewModel GameDirectory { get; }
public AssetsFolderViewModel AssetsFolder { get; }
@@ -1228,6 +1215,11 @@ public class CUE4ParseViewModel : ViewModel
var dummy = ((AbstractUePackage) pkg).ConstructObject(pointer.Class, pkg);
switch (dummy)
{
case not null when isNone && SnooperViewModel.Instance.TryDeliver(pointer.Object.Value):
{
// the viewer asked for this kind of asset and took it
return true;
}
case UVerseDigest when isNone && pointer.Object.Value is UVerseDigest verseDigest:
{
if (!TabControl.CanAddTabs) return false;
@@ -1515,40 +1507,20 @@ public class CUE4ParseViewModel : ViewModel
return false;
}
case UWorld when isNone && UserSettings.Default.PreviewWorlds:
// case UBlueprintGeneratedClass when isNone && UserSettings.Default.PreviewWorlds && TabControl.SelectedTab.ParentExportType switch
// {
// "JunoBuildInstructionsItemDefinition" => true,
// "JunoBuildingSetAccountItemDefinition" => true,
// "JunoBuildingPropAccountItemDefinition" => true,
// _ => false
// }:
case UPaperSprite when isNone && UserSettings.Default.PreviewMaterials:
case UBlueprintGeneratedClass when isNone && UserSettings.Default.PreviewWorlds:
// case UPaperSprite when isNone && UserSettings.Default.PreviewMaterials:
case UStaticMesh when isNone && UserSettings.Default.PreviewStaticMeshes:
case UGeometryCollection when isNone && UserSettings.Default.PreviewStaticMeshes:
case USkeletalMesh when isNone && UserSettings.Default.PreviewSkeletalMeshes:
case USkeleton when isNone && UserSettings.Default.SaveSkeletonAsMesh:
case UMaterialInstance when isNone && UserSettings.Default.PreviewMaterials && !ModelIsOverwritingMaterial &&
!(Provider.ProjectName.Equals("FortniteGame", StringComparison.OrdinalIgnoreCase) &&
(pkg.Name.Contains("/MI_OfferImages/", StringComparison.OrdinalIgnoreCase) ||
pkg.Name.Contains("/RenderSwitch_Materials/", StringComparison.OrdinalIgnoreCase) ||
pkg.Name.Contains("/MI_BPTile/", StringComparison.OrdinalIgnoreCase))):
case UAnimationAsset when isNone && UserSettings.Default.PreviewAnimations:
// case UMaterialInstance when isNone && UserSettings.Default.PreviewMaterials &&
// !(Provider.ProjectName.Equals("FortniteGame", StringComparison.OrdinalIgnoreCase) &&
// (pkg.Name.Contains("/MI_OfferImages/", StringComparison.OrdinalIgnoreCase) ||
// pkg.Name.Contains("/RenderSwitch_Materials/", StringComparison.OrdinalIgnoreCase) ||
// pkg.Name.Contains("/MI_BPTile/", StringComparison.OrdinalIgnoreCase))):
{
SnooperViewModel.Instance.Load(pointer.Object.Value);
// if (SnooperViewer.TryLoadExport(cancellationToken, dummy, pointer.Object))
// SnooperViewer.Run();
return true;
}
case UMaterialInstance when isNone && ModelIsOverwritingMaterial && pointer.Object.Value is UMaterialInstance m:
{
// SnooperViewer.Renderer.Swap(m);
// SnooperViewer.Run();
return true;
}
case UAnimSequenceBase when isNone && UserSettings.Default.PreviewAnimations || ModelIsWaitingAnimation:
{
// animate all animations using their specified skeleton or when we explicitly asked for a loaded model to be animated (ignoring whether we wanted to preview animations)
// SnooperViewer.Renderer.Animate(pointer.Object.Value);
// SnooperViewer.Run();
return true;
}
case UStaticMesh when HasFlag(bulk, EBulkType.Meshes):

View File

@@ -39,7 +39,6 @@ public class MenuCommand : ViewModelCommand<ApplicationViewModel>
break;
case "Views_3dViewer":
SnooperViewModel.Instance.Load(null);
SnooperViewModel.Instance.Run();
break;
case "Views_ExportSession":
Helper.OpenWindow<AdonisWindow>("Export Session", () => new ExportSessionWindow().Show());

View File

@@ -4,6 +4,7 @@ using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Forms;
using CUE4Parse.UE4.Assets.Exports;
using CUE4Parse.UE4.Assets.Exports.Animation;
using CUE4Parse.UE4.Assets.Exports.GeometryCollection;
using CUE4Parse.UE4.Assets.Exports.SkeletalMesh;
using CUE4Parse.UE4.Assets.Exports.StaticMesh;
@@ -11,6 +12,8 @@ using CUE4Parse.UE4.Objects.Engine;
using Editor;
using FModel.Framework;
using FModel.Services;
using FModel.Settings;
using Snooper.Hosting;
using Snooper.Rendering.Actors;
using Snooper.Rendering.Components;
using Snooper.Rendering.Components.Light;
@@ -26,6 +29,15 @@ public class SnooperViewModel : ViewModel, IDisposable
private readonly SnooperHost _host;
/// <summary>
/// Mirrors <see cref="Bridge.PendingRequest"/> for bindings.
/// </summary>
public AssetRequest PendingRequest
{
get;
private set => SetProperty(ref field, value);
}
private SnooperViewModel()
{
var scale = GetDpiScale();
@@ -33,20 +45,33 @@ public class SnooperViewModel : ViewModel, IDisposable
var width = Convert.ToInt32(SystemParameters.MaximizedPrimaryScreenWidth * .9 * scale);
var height = Convert.ToInt32(SystemParameters.MaximizedPrimaryScreenHeight * .85 * scale);
Bridge.Host = new FModelBridgeHost();
Bridge.PendingRequestChanged += request => PendingRequest = request;
_host = new SnooperHost(() => new EditorWindow(htz, width, height, ApplicationService.ApplicationView.CUE4Parse.Provider, false, true));
}
public void Load(UObject? obj)
{
SetOptions();
Actor? actor = obj switch
{
UStaticMesh sm => new MeshActor(sm),
USkeletalMesh sk => new MeshActor(sk),
UGeometryCollection gc => new MeshActor(gc),
UAnimationAsset anim => new MeshActor(anim),
UBlueprintGeneratedClass bp => new BlueprintActor(bp),
UWorld w => new WorldActor(w),
_ => null
};
if (actor != null && actor.Components.Count == 0 && actor.Children.Count == 0)
{
// there's nothing to show
return;
}
var editor = _host.Window;
editor.Invoke(() =>
{
@@ -67,6 +92,15 @@ public class SnooperViewModel : ViewModel, IDisposable
});
}
public bool TryDeliver(UObject obj)
{
if (!Bridge.TryDeliver(obj)) return false;
var editor = _host.Window;
editor.Invoke(editor.Show);
return true;
}
private Actor CreateScene(bool transparentGrid)
{
var scene = new Actor("Scene");
@@ -88,10 +122,16 @@ public class SnooperViewModel : ViewModel, IDisposable
return scene;
}
public void Run()
private void SetOptions()
{
var editor = _host.Window;
editor.Invoke(editor.Show);
var settings = UserSettings.Default;
var options = Bridge.Options;
options.NaniteMeshFormat = settings.NaniteMeshExportFormat;
options.MaterialDepth = settings.MaterialExportFormat;
options.TexturePlatform = settings.CurrentDir.TexturePlatform;
options.LoadMorphTargets = settings.SaveMorphTargets;
options.MaxTextureMipSize = settings.PreviewMaxTextureSize;
}
[DllImport("user32.dll")]

Submodule Snooper updated: 896134700b...52c9142077