@ikijs/editor 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1841,8 +1841,58 @@ var HAIR_FRONT_DEPTH = 0.1;
1841
1841
  var HAIR_BACK_DEPTH = -0.08;
1842
1842
  var NOD_TRAVEL = 30;
1843
1843
  var NOD_BEND = 0.5;
1844
- var HAIR_BACK_NOD_DEPTH = -0.07;
1844
+ var HAIR_BACK_NOD_DEPTH = -0.135;
1845
+ function headNodParallaxUnit(gridHalfHeight) {
1846
+ return gridHalfHeight * HEAD_CYLINDER_RADIUS_FACTOR * Math.sin(HEAD_TURN_MAX_DEG * NOD_BEND * (Math.PI / 180));
1847
+ }
1848
+ var FEATURE_DEPTH = {
1849
+ eye: 0.04,
1850
+ iris: 0.04,
1851
+ pupil: 0.04,
1852
+ highlight: 0.04,
1853
+ lash: 0.04,
1854
+ brow: 0.04,
1855
+ blush: 0.04,
1856
+ mouth: 0.04,
1857
+ mouth_open: 0.04,
1858
+ nose: 0.08
1859
+ };
1860
+ var HAIR_FRONT_NOD_DEPTH = FEATURE_DEPTH.brow;
1861
+ function roleFamily(role) {
1862
+ return role.replace(/_[LR]$/, "");
1863
+ }
1864
+ function featureParallaxBindings(role, options) {
1865
+ if (!options.hasNose) return [];
1866
+ const depth = FEATURE_DEPTH[roleFamily(role)];
1867
+ if (depth === void 0) return [];
1868
+ const bindings = [];
1869
+ const shiftX = depth * (options.parallaxUnit ?? 0);
1870
+ if (shiftX !== 0) {
1871
+ bindings.push({
1872
+ parameter: StandardParameter.AngleX,
1873
+ channel: "translateX",
1874
+ from: -shiftX,
1875
+ to: shiftX
1876
+ });
1877
+ }
1878
+ const shiftY = depth * (options.parallaxUnitY ?? 0);
1879
+ if (shiftY !== 0) {
1880
+ bindings.push({
1881
+ parameter: StandardParameter.AngleY,
1882
+ channel: "translateY",
1883
+ from: -shiftY,
1884
+ to: shiftY
1885
+ });
1886
+ }
1887
+ return bindings;
1888
+ }
1845
1889
  function bindingsForRole(spec, role, cropW, cropH, options = {}) {
1890
+ return [
1891
+ ...roleOwnBindings(spec, role, cropW, cropH, options),
1892
+ ...featureParallaxBindings(role, options)
1893
+ ];
1894
+ }
1895
+ function roleOwnBindings(spec, role, cropW, cropH, options) {
1846
1896
  const isEyeStack = EYE_STACK_PREFIXES.some((p) => role.startsWith(p));
1847
1897
  if (isEyeStack && spec.eyeSide !== void 0) {
1848
1898
  const isGazeRole = role.startsWith("iris_") || role.startsWith("pupil_") || role.startsWith("highlight_");
@@ -1956,7 +2006,8 @@ function bindingsForRole(spec, role, cropW, cropH, options = {}) {
1956
2006
  });
1957
2007
  }
1958
2008
  }
1959
- const shiftY = (isFront ? 0 : HAIR_BACK_NOD_DEPTH) * (options.parallaxUnitY ?? 0);
2009
+ const frontDepth = options.hasNose ? HAIR_FRONT_NOD_DEPTH : 0;
2010
+ const shiftY = (isFront ? frontDepth : HAIR_BACK_NOD_DEPTH) * (options.parallaxUnitY ?? 0);
1960
2011
  if (shiftY !== 0) {
1961
2012
  parallax.push({
1962
2013
  parameter: StandardParameter.AngleY,
@@ -2001,14 +2052,18 @@ function bakeHairBackTurnWarp(mesh, parameter) {
2001
2052
  const radius = halfWidth * HAIR_BACK_BEND_RADIUS_FACTOR;
2002
2053
  const bulge = HAIR_BACK_FAR_BULGE * halfWidth;
2003
2054
  const DEG_TO_RAD = Math.PI / 180;
2004
- const keyforms = [-HEAD_TURN_MAX_DEG, 0, HEAD_TURN_MAX_DEG].map((deg) => {
2055
+ const keyforms = HEAD_TURN_STOPS.map((deg) => {
2005
2056
  const theta = deg * DEG_TO_RAD;
2006
2057
  const s = Math.sign(deg);
2058
+ const turnFraction = Math.abs(deg) / HEAD_TURN_MAX_DEG;
2059
+ const fullTheta = s * HEAD_TURN_MAX_DEG * DEG_TO_RAD;
2060
+ const bulgeAtStop = bulge * deg / HEAD_TURN_MAX_DEG;
2007
2061
  const offsets = [];
2008
2062
  for (let i = 0; i < mesh.vertices.length; i += 2) {
2009
2063
  const x = mesh.vertices[i];
2010
2064
  const far = Math.max(0, -s * x / halfWidth);
2011
- offsets.push(pinnedCylinderBend(x, radius, theta) - s * bulge * far, 0);
2065
+ const bend = far > 0 ? turnFraction * pinnedCylinderBend(x, radius, fullTheta) : pinnedCylinderBend(x, radius, theta);
2066
+ offsets.push(bend - bulgeAtStop * far, 0);
2012
2067
  }
2013
2068
  return { value: deg, offsets };
2014
2069
  });
@@ -2048,6 +2103,7 @@ function generateIkiFromLayerSet(layers, canvas) {
2048
2103
  validateLayerInputs(layers, canvas);
2049
2104
  const hasHair = layers.some((l) => l.role === "hair_front");
2050
2105
  const hasMouthOpen = layers.some((l) => l.role === "mouth_open");
2106
+ const hasNose = layers.some((l) => l.role === "nose");
2051
2107
  const bodyLayer = layers.find((l) => l.role === "body");
2052
2108
  const parameters = [
2053
2109
  {
@@ -2226,7 +2282,7 @@ function generateIkiFromLayerSet(layers, canvas) {
2226
2282
  const faceCenterY = faceTransform.y;
2227
2283
  const halfH = Math.max(faceCenterY - unionMinY, unionMaxY - faceCenterY);
2228
2284
  const parallaxUnit = headTurnParallaxUnit(halfW);
2229
- const parallaxUnitY = headTurnParallaxUnit(halfH);
2285
+ const parallaxUnitY = headNodParallaxUnit(halfH);
2230
2286
  const faceBottom = faceTransform.y - faceCropH / 2;
2231
2287
  const neckPivot = {
2232
2288
  x: faceCenterX,
@@ -2347,7 +2403,8 @@ function generateIkiFromLayerSet(layers, canvas) {
2347
2403
  const roleBindings = bindingsForRole(spec, role, cropW, cropH, {
2348
2404
  hasMouthOpen,
2349
2405
  parallaxUnit,
2350
- parallaxUnitY
2406
+ parallaxUnitY,
2407
+ hasNose
2351
2408
  });
2352
2409
  const deformerId = spec.deformer === "none" ? void 0 : spec.deformer;
2353
2410
  if (spec.mesh) {