You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

298 lines
8.3 KiB
Plaintext

/******************************************************************************/
/*
Project - MudBun
Publisher - Long Bunny Labs
http://LongBunnyLabs.com
Author - Ming-Lun "Allen" Chou
http://AllenChou.net
*/
/******************************************************************************/
#ifndef MUDBUN_BONE_FUNCS
#define MUDBUN_BONE_FUNCS
#include "BrushDefs.cginc"
#include "BrushFuncs.cginc"
#define kAutoRiggingNew (0)
#define kAutoRiggingDefault (1)
int autoRiggingAlgorithm = kAutoRiggingNew;
float4 normalize_bone_weight(float4 boneWeight)
{
return saturate(boneWeight / comp_sum(boneWeight) - 0.01f);
}
// reference: sdf_brush_apply
float bone_weight_t(SdfBrush brush, float distA, float distB)
{
float t = 0.0f;
switch (brush.op)
{
case kSdfUnionCubic:
case kSdfUnionChamfer:
t = dist_blend_weight(distA, distB, 2.5f);
break;
case kSdfSubtractCubic:
case kSdfSubtractChamfer:
t = 1.0f - saturate(2.0f * distB / max(kEpsilon, brush.blend));
break;
case kSdfIntersectCubic:
case kSdfIntersectChamfer:
t = 1.0f - saturate(-2.0f * distB / max(kEpsilon, brush.blend));
break;
case kSdfPipe:
t = saturate(-distB / max(kEpsilon, brush.blend));
break;
case kSdfEngrave:
t = 1.0f - saturate(abs(distB) / max(kEpsilon, brush.blend));
break;
/*
case kSdfDye:
t = 1.0f - saturate(max(0.0f, distB) / max(kEpsilon, brush.blend));
break;
*/
default:
if (is_sdf_dye(brush.op))
{
t = 1.0f - saturate(max(0.0f, distB) / max(kEpsilon, brush.blend));
}
break;
}
return t;
}
int blend_bone_weights(float brushRes, SdfBrush brush, int brushBoneIndex, inout float4 boneRes, inout int4 boneIndex, inout float4 boneWeight)
{
int iBone = -1;
switch (autoRiggingAlgorithm)
{
case kAutoRiggingNew:
{
boneWeight *=
float4
(
1.0f - bone_weight_t(brush, boneRes.x, brushRes),
1.0f - bone_weight_t(brush, boneRes.y, brushRes),
1.0f - bone_weight_t(brush, boneRes.z, brushRes),
1.0f - bone_weight_t(brush, boneRes.w, brushRes)
);
float minBoneRes = min(boneRes.x, min(boneRes.y, min(boneRes.z, boneRes.w)));
float brushWeight = bone_weight_t(brush, minBoneRes, brushRes);
// this could probably be vectorized, but this is for off-line compute jobs so it's not that important
if (brushWeight > boneWeight.x)
{
boneWeight.xyzw = float4(brushWeight, boneWeight.xyz);
boneRes.xyzw = float4(brushRes, boneRes.xyz);
boneIndex.xyzw = float4(brushBoneIndex, boneIndex.xyz);
iBone = 0;
}
else if (brushWeight > boneWeight.y)
{
boneWeight.yzw = float3(brushWeight, boneWeight.yz);
boneRes.yzw = float3(brushRes, boneRes.yz);
boneIndex.yzw = float3(brushBoneIndex, boneIndex.yz);
iBone = 1;
}
else if (brushWeight > boneWeight.z)
{
boneWeight.zw = float2(brushWeight, boneWeight.z);
boneRes.zw = float2(brushRes, boneRes.z);
boneIndex.zw = float2(brushBoneIndex, boneIndex.z);
iBone = 2;
}
else if (brushWeight > boneWeight.w)
{
boneWeight.w = brushWeight;
boneRes.w = brushRes;
boneIndex.w = brushBoneIndex;
iBone = 3;
}
else
{
return -1;
}
}
break;
case kAutoRiggingDefault:
{
// this could probably be vectorized, but this is for off-line compute jobs so it's not that important
if (brushRes < boneRes.x)
{
boneRes.xyzw = float4(brushRes, boneRes.xyz);
boneIndex.xyzw = float4(brushBoneIndex, boneIndex.xyz);
iBone = 0;
}
else if (brushRes < boneRes.y)
{
boneRes.yzw = float3(brushRes, boneRes.yz);
boneIndex.yzw = float3(brushBoneIndex, boneIndex.yz);
iBone = 1;
}
else if (brushRes < boneRes.z)
{
boneRes.zw = float2(brushRes, boneRes.z);
boneIndex.zw = float2(brushBoneIndex, boneIndex.z);
iBone = 2;
}
else if (brushRes < boneRes.w)
{
boneRes.w = brushRes;
boneIndex.w = brushBoneIndex;
iBone = 3;
}
else
{
return -1;
}
boneWeight = 1.0f / max(kEpsilon, boneRes);
}
break;
}
// TODO: variable tightness?
//boneWeight = pow(boneWeight, 0.25f);
boneWeight = normalize_bone_weight(boneWeight);
boneWeight = step(0.02f, boneWeight) * boneWeight; // trim tiny weights
boneWeight = normalize_bone_weight(boneWeight);
return iBone;
}
#include "../Customization/CustomBone.cginc"
void sdf_apply_brush_bone_weights(float3 p, SdfBrush b, float brushRes, inout float4 boneRes, inout int4 boneIndex, inout float4 boneWeight)
{
float3 pRel = quat_rot(quat_inv(b.rotation), p - b.position);
switch (b.type)
{
case kSdfCurveSimple:
{
float2 curveRes = sdf_bezier(p, b.data0.xyz, b.data2.xyz, b.data1.xyz);
float brushRes = curveRes.x;
float resA = brushRes;
float resB = brushRes;
float resC = brushRes;
int brushBoneIndexA = b.boneIndex;
int brushBoneIndexB = b.boneIndex + 1;
int brushBoneIndexC = b.boneIndex + 2;
int iBoneA = blend_bone_weights(resA, b, brushBoneIndexA, boneRes, boneIndex, boneWeight);
int iBoneB = blend_bone_weights(resB, b, brushBoneIndexB, boneRes, boneIndex, boneWeight);
int iBoneC = blend_bone_weights(resC, b, brushBoneIndexC, boneRes, boneIndex, boneWeight);
float aBoneWeight[4] = { boneWeight.x, boneWeight.y, boneWeight.z, boneWeight.w };
if (iBoneA >= 0 && iBoneB >= 0)
{
float t = bone_weight_t(b, resA, resB);
float boneWeightAB = aBoneWeight[iBoneA] + aBoneWeight[iBoneB];
aBoneWeight[iBoneA] += (1.0f - t) * boneWeightAB;
aBoneWeight[iBoneB] += t * boneWeightAB;
}
if (iBoneB >= 0 && iBoneC >= 0)
{
float t = bone_weight_t(b, resB, resC);
float boneWeightBC = aBoneWeight[iBoneB] + aBoneWeight[iBoneC];
aBoneWeight[iBoneB] += (1.0f - t) * boneWeightBC;
aBoneWeight[iBoneC] += t * boneWeightBC;
}
boneWeight = float4(aBoneWeight[0], aBoneWeight[1], aBoneWeight[2], aBoneWeight[3]);
break;
}
case kSdfCurveFull:
{
int numPoints = int(b.data0.x) - 2;
bool useNoise = false;//(b.data0.z > 0.0f);
for (int i = 0; i < numPoints; ++i)
{
int iBrush = b.index + (useNoise ? 3 : 2) + i;
float3 pointPos = aBrush[iBrush].data0.xyz;
/*
float maxSegDist = 0.0f;
if (i > 0)
{
float3 prevPointPos = aBrush[iBrush - 1].data0.xyz;
maxSegDist = max(maxSegDist, length(prevPointPos - pointPos));
}
if (i < numPoints - 1)
{
float3 nextPointPos = aBrush[iBrush - 1].data0.xyz;
maxSegDist = max(maxSegDist, length(nextPointPos - pointPos));
}
float pDist = length(pRel - pointPos);
if (maxSegDist > 0.0f && pDist > maxSegDist)
continue;
*/
float pointRes = sdf_sphere(p - aBrush[iBrush].data0.xyz, aBrush[iBrush].data0.w);
int pointBoneIndex = b.boneIndex + i;
blend_bone_weights(pointRes, b, pointBoneIndex, boneRes, boneIndex, boneWeight);
}
break;
}
case kSdfBox:
case kSdfSphere:
case kSdfCylinder:
case kSdfTorus:
case kSdfSolidAngle:
case kSdfParticle:
case kSdfParticleSystem:
case kSdfNoiseVolume:
{
blend_bone_weights(brushRes, b, b.boneIndex, boneRes, boneIndex, boneWeight);
break;
}
case kSdfNoOp:
break;
default:
{
apply_custom_brush_bone_weights(p, pRel, b, brushRes, boneRes, boneIndex, boneWeight);
break;
}
}
}
void compute_brush_bone_weights(float3 p, out int4 boneIndex, out float4 boneWeight)
{
boneIndex = -1;
boneWeight = 1e-6f;
float res = kInfinity;
float4 boneRes = kInfinity;
for (int iBrush = 0; iBrush < numBrushes; ++iBrush)
{
res = sdf_brush(res, p, aBrush[iBrush]);
// not sure why Metal doesn't like this check...
#if !defined(SHADER_API_METAL)
if ((aBrush[iBrush].flags & kSdfBrushFlagsCountAsBone) == 0)
continue;
#endif
sdf_apply_brush_bone_weights(p, aBrush[iBrush], res, boneRes, boneIndex, boneWeight);
}
}
#endif