@promptbook/wizard 0.112.0-133 → 0.112.0-135
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/esm/index.es.js +795 -39
- package/esm/index.es.js.map +1 -1
- package/esm/src/avatars/types/AvatarVisualDefinition.d.ts +1 -1
- package/esm/src/avatars/visuals/octopus3d4AvatarVisual.d.ts +7 -0
- package/esm/src/utils/validators/url/isValidAgentUrl.d.ts +5 -0
- package/esm/src/version.d.ts +1 -1
- package/package.json +2 -2
- package/umd/index.umd.js +795 -39
- package/umd/index.umd.js.map +1 -1
- package/umd/src/avatars/types/AvatarVisualDefinition.d.ts +1 -1
- package/umd/src/avatars/visuals/octopus3d4AvatarVisual.d.ts +7 -0
- package/umd/src/utils/validators/url/isValidAgentUrl.d.ts +5 -0
- package/umd/src/version.d.ts +1 -1
package/umd/index.umd.js
CHANGED
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
* @generated
|
|
53
53
|
* @see https://github.com/webgptorg/promptbook
|
|
54
54
|
*/
|
|
55
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.112.0-
|
|
55
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.112.0-135';
|
|
56
56
|
/**
|
|
57
57
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
58
58
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -19186,6 +19186,11 @@
|
|
|
19186
19186
|
* - `isValidAgentUrl` *(this one)* which tests just agent URL
|
|
19187
19187
|
* - `isValidPipelineUrl` which tests just pipeline URL
|
|
19188
19188
|
*
|
|
19189
|
+
* Note: This is a pure structural validator and does not block private/internal network
|
|
19190
|
+
* addresses. Callers that fetch the URL server-side from an untrusted network context must
|
|
19191
|
+
* additionally apply the `assertSafeUrl` SSRF guard from the Agents Server before any
|
|
19192
|
+
* outbound request.
|
|
19193
|
+
*
|
|
19189
19194
|
* @public exported from `@promptbook/utils`
|
|
19190
19195
|
*/
|
|
19191
19196
|
function isValidAgentUrl(url) {
|
|
@@ -19199,12 +19204,6 @@
|
|
|
19199
19204
|
// TODO: [🐠]
|
|
19200
19205
|
return false;
|
|
19201
19206
|
}
|
|
19202
|
-
/*
|
|
19203
|
-
Note: [👣][🧠] Is it secure to allow pipeline URLs on private and unsecured networks?
|
|
19204
|
-
if (isUrlOnPrivateNetwork(url)) {
|
|
19205
|
-
return false;
|
|
19206
|
-
}
|
|
19207
|
-
*/
|
|
19208
19207
|
return true;
|
|
19209
19208
|
}
|
|
19210
19209
|
// TODO: [🐠] Maybe more info why the URL is invalid
|
|
@@ -22594,7 +22593,7 @@
|
|
|
22594
22593
|
*
|
|
22595
22594
|
* @private helper of `minecraft2AvatarVisual`
|
|
22596
22595
|
*/
|
|
22597
|
-
const LIGHT_DIRECTION$
|
|
22596
|
+
const LIGHT_DIRECTION$4 = normalizeVector3({
|
|
22598
22597
|
x: 0.4,
|
|
22599
22598
|
y: -0.65,
|
|
22600
22599
|
z: 0.92,
|
|
@@ -22818,7 +22817,7 @@
|
|
|
22818
22817
|
corners: projectedCorners,
|
|
22819
22818
|
texture: faceDefinition.texture,
|
|
22820
22819
|
averageDepth: transformedCorners.reduce((depthSum, corner) => depthSum + corner.z, 0) / transformedCorners.length,
|
|
22821
|
-
lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION$
|
|
22820
|
+
lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION$4), -1, 1),
|
|
22822
22821
|
outlineColor: cuboid.outlineColor,
|
|
22823
22822
|
};
|
|
22824
22823
|
});
|
|
@@ -23505,7 +23504,7 @@
|
|
|
23505
23504
|
*
|
|
23506
23505
|
* @private helper of `octopus3AvatarVisual`
|
|
23507
23506
|
*/
|
|
23508
|
-
function formatAlphaHex$
|
|
23507
|
+
function formatAlphaHex$2(opacity) {
|
|
23509
23508
|
return Math.round(Math.min(1, Math.max(0, opacity)) * 255)
|
|
23510
23509
|
.toString(16)
|
|
23511
23510
|
.padStart(2, '0');
|
|
@@ -23884,7 +23883,7 @@
|
|
|
23884
23883
|
context.beginPath();
|
|
23885
23884
|
context.moveTo(-radiusX * 0.74, radiusY * 0.2);
|
|
23886
23885
|
context.quadraticCurveTo(0, radiusY * 0.38, radiusX * 0.74, radiusY * 0.2);
|
|
23887
|
-
context.strokeStyle = `${palette.highlight}${formatAlphaHex$
|
|
23886
|
+
context.strokeStyle = `${palette.highlight}${formatAlphaHex$2(eyeStyle.lowerLidOpacity)}`;
|
|
23888
23887
|
context.lineWidth = radiusX * 0.08;
|
|
23889
23888
|
context.lineCap = 'round';
|
|
23890
23889
|
context.stroke();
|
|
@@ -23965,7 +23964,7 @@
|
|
|
23965
23964
|
context.beginPath();
|
|
23966
23965
|
context.moveTo(-projectedRadiusX * 0.74, projectedRadiusY * 0.2);
|
|
23967
23966
|
context.quadraticCurveTo(0, projectedRadiusY * 0.38, projectedRadiusX * 0.74, projectedRadiusY * 0.2);
|
|
23968
|
-
context.strokeStyle = `${palette.highlight}${formatAlphaHex(eyeStyle.lowerLidOpacity)}`;
|
|
23967
|
+
context.strokeStyle = `${palette.highlight}${formatAlphaHex$1(eyeStyle.lowerLidOpacity)}`;
|
|
23969
23968
|
context.lineWidth = projectedRadiusX * 0.08;
|
|
23970
23969
|
context.lineCap = 'round';
|
|
23971
23970
|
context.stroke();
|
|
@@ -24011,7 +24010,7 @@
|
|
|
24011
24010
|
*
|
|
24012
24011
|
* @private helper of the 3D octopus avatar visuals
|
|
24013
24012
|
*/
|
|
24014
|
-
function formatAlphaHex(opacity) {
|
|
24013
|
+
function formatAlphaHex$1(opacity) {
|
|
24015
24014
|
return Math.round(clampNumber$1(opacity, 0, 1) * 255)
|
|
24016
24015
|
.toString(16)
|
|
24017
24016
|
.padStart(2, '0');
|
|
@@ -24023,7 +24022,7 @@
|
|
|
24023
24022
|
*
|
|
24024
24023
|
* @private helper of `octopus3dAvatarVisual`
|
|
24025
24024
|
*/
|
|
24026
|
-
const LIGHT_DIRECTION$
|
|
24025
|
+
const LIGHT_DIRECTION$3 = normalizeVector3({
|
|
24027
24026
|
x: 0.48,
|
|
24028
24027
|
y: -0.62,
|
|
24029
24028
|
z: 0.94,
|
|
@@ -24273,7 +24272,7 @@
|
|
|
24273
24272
|
corners: projectedCorners,
|
|
24274
24273
|
averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
|
|
24275
24274
|
transformedCorners.length,
|
|
24276
|
-
lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$
|
|
24275
|
+
lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$3), -1, 1),
|
|
24277
24276
|
fillStyle: resolveSurfacePatchFillStyle(palette, verticalProgress + verticalColorBias),
|
|
24278
24277
|
outlineColor,
|
|
24279
24278
|
});
|
|
@@ -24462,7 +24461,7 @@
|
|
|
24462
24461
|
*
|
|
24463
24462
|
* @private helper of `octopus3d2AvatarVisual`
|
|
24464
24463
|
*/
|
|
24465
|
-
const LIGHT_DIRECTION$
|
|
24464
|
+
const LIGHT_DIRECTION$2 = normalizeVector3({
|
|
24466
24465
|
x: 0.38,
|
|
24467
24466
|
y: -0.6,
|
|
24468
24467
|
z: 0.98,
|
|
@@ -24621,13 +24620,13 @@
|
|
|
24621
24620
|
*
|
|
24622
24621
|
* @private helper of `octopus3d2AvatarVisual`
|
|
24623
24622
|
*/
|
|
24624
|
-
const LATITUDE_PATCH_COUNT$
|
|
24623
|
+
const LATITUDE_PATCH_COUNT$2 = 12;
|
|
24625
24624
|
/**
|
|
24626
24625
|
* Number of longitude segments used by the single blobby octopus mesh.
|
|
24627
24626
|
*
|
|
24628
24627
|
* @private helper of `octopus3d2AvatarVisual`
|
|
24629
24628
|
*/
|
|
24630
|
-
const LONGITUDE_PATCH_COUNT$
|
|
24629
|
+
const LONGITUDE_PATCH_COUNT$2 = 24;
|
|
24631
24630
|
/**
|
|
24632
24631
|
* Resolves all visible projected patches for the single blobby octopus mesh.
|
|
24633
24632
|
*
|
|
@@ -24641,8 +24640,8 @@
|
|
|
24641
24640
|
*/
|
|
24642
24641
|
function resolveVisibleBlobbyOctopusPatches(options) {
|
|
24643
24642
|
const { center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, morphologyProfile, animationPhase, timeMs, } = options;
|
|
24644
|
-
const latitudePatchCount = LATITUDE_PATCH_COUNT$
|
|
24645
|
-
const longitudePatchCount = LONGITUDE_PATCH_COUNT$
|
|
24643
|
+
const latitudePatchCount = LATITUDE_PATCH_COUNT$2;
|
|
24644
|
+
const longitudePatchCount = LONGITUDE_PATCH_COUNT$2;
|
|
24646
24645
|
const surfacePatches = [];
|
|
24647
24646
|
const latitudeBoundaries = new Float64Array(latitudePatchCount + 1);
|
|
24648
24647
|
const longitudeBoundaries = new Float64Array(longitudePatchCount + 1);
|
|
@@ -24701,8 +24700,8 @@
|
|
|
24701
24700
|
transformedCorners[2].z +
|
|
24702
24701
|
transformedCorners[3].z) /
|
|
24703
24702
|
4,
|
|
24704
|
-
lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$
|
|
24705
|
-
fillStyle: resolveBlobbySurfacePatchFillStyle(palette, verticalProgress, Math.max(0, Math.cos(centerLongitude)), resolveLowerLobeWave(centerLongitude, morphologyProfile, animationPhase, timeMs)),
|
|
24703
|
+
lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$2), -1, 1),
|
|
24704
|
+
fillStyle: resolveBlobbySurfacePatchFillStyle$1(palette, verticalProgress, Math.max(0, Math.cos(centerLongitude)), resolveLowerLobeWave(centerLongitude, morphologyProfile, animationPhase, timeMs)),
|
|
24706
24705
|
outlineColor: verticalProgress < 0.58 ? `${palette.highlight}73` : `${palette.shadow}8a`,
|
|
24707
24706
|
});
|
|
24708
24707
|
}
|
|
@@ -24770,7 +24769,7 @@
|
|
|
24770
24769
|
*
|
|
24771
24770
|
* @private helper of `octopus3d2AvatarVisual`
|
|
24772
24771
|
*/
|
|
24773
|
-
function resolveBlobbySurfacePatchFillStyle(palette, verticalProgress, forwardness, lowerLobeWave) {
|
|
24772
|
+
function resolveBlobbySurfacePatchFillStyle$1(palette, verticalProgress, forwardness, lowerLobeWave) {
|
|
24774
24773
|
const tonalProgress = clampNumber$1(verticalProgress + lowerLobeWave * 0.12 - forwardness * 0.07, 0, 1);
|
|
24775
24774
|
if (tonalProgress < 0.16) {
|
|
24776
24775
|
return palette.highlight;
|
|
@@ -24816,7 +24815,7 @@
|
|
|
24816
24815
|
*
|
|
24817
24816
|
* @private helper of `octopus3d3AvatarVisual`
|
|
24818
24817
|
*/
|
|
24819
|
-
const LIGHT_DIRECTION = normalizeVector3({
|
|
24818
|
+
const LIGHT_DIRECTION$1 = normalizeVector3({
|
|
24820
24819
|
x: 0.34,
|
|
24821
24820
|
y: -0.62,
|
|
24822
24821
|
z: 1,
|
|
@@ -24826,7 +24825,7 @@
|
|
|
24826
24825
|
*
|
|
24827
24826
|
* @private helper of `octopus3d3AvatarVisual`
|
|
24828
24827
|
*/
|
|
24829
|
-
const OCTOPUS_TENTACLE_COUNT = 8;
|
|
24828
|
+
const OCTOPUS_TENTACLE_COUNT$1 = 8;
|
|
24830
24829
|
/**
|
|
24831
24830
|
* Cache keyed by the `createRandom` factory reference, which is stable for the lifetime of one
|
|
24832
24831
|
* mounted `<Avatar/>` component (created inside `resolveAvatarRenderDefinition` and held in a
|
|
@@ -24965,9 +24964,9 @@
|
|
|
24965
24964
|
* @private helper of `octopus3d3AvatarVisual`
|
|
24966
24965
|
*/
|
|
24967
24966
|
function createContinuousTentacleProfiles(createRandom, morphologyProfile) {
|
|
24968
|
-
return Array.from({ length: OCTOPUS_TENTACLE_COUNT }, (_, tentacleIndex) => {
|
|
24967
|
+
return Array.from({ length: OCTOPUS_TENTACLE_COUNT$1 }, (_, tentacleIndex) => {
|
|
24969
24968
|
const tentacleRandom = createRandom(`octopus3d3-tentacle-${tentacleIndex}`);
|
|
24970
|
-
const progress = tentacleIndex / (OCTOPUS_TENTACLE_COUNT - 1);
|
|
24969
|
+
const progress = tentacleIndex / (OCTOPUS_TENTACLE_COUNT$1 - 1);
|
|
24971
24970
|
return {
|
|
24972
24971
|
centerLongitude: -Math.PI * 0.86 +
|
|
24973
24972
|
progress * Math.PI * 1.72 +
|
|
@@ -25034,13 +25033,13 @@
|
|
|
25034
25033
|
*
|
|
25035
25034
|
* @private helper of `octopus3d3AvatarVisual`
|
|
25036
25035
|
*/
|
|
25037
|
-
const LATITUDE_PATCH_COUNT = 16;
|
|
25036
|
+
const LATITUDE_PATCH_COUNT$1 = 16;
|
|
25038
25037
|
/**
|
|
25039
25038
|
* Number of longitude segments used by the continuous Octopus 3D 3 mesh.
|
|
25040
25039
|
*
|
|
25041
25040
|
* @private helper of `octopus3d3AvatarVisual`
|
|
25042
25041
|
*/
|
|
25043
|
-
const LONGITUDE_PATCH_COUNT = 40;
|
|
25042
|
+
const LONGITUDE_PATCH_COUNT$1 = 40;
|
|
25044
25043
|
/**
|
|
25045
25044
|
* Resolves visible projected patches for the continuous octopus mesh.
|
|
25046
25045
|
*
|
|
@@ -25054,8 +25053,8 @@
|
|
|
25054
25053
|
*/
|
|
25055
25054
|
function resolveVisibleContinuousOctopusPatches(options) {
|
|
25056
25055
|
const { center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette } = options;
|
|
25057
|
-
const latitudePatchCount = LATITUDE_PATCH_COUNT;
|
|
25058
|
-
const longitudePatchCount = LONGITUDE_PATCH_COUNT;
|
|
25056
|
+
const latitudePatchCount = LATITUDE_PATCH_COUNT$1;
|
|
25057
|
+
const longitudePatchCount = LONGITUDE_PATCH_COUNT$1;
|
|
25059
25058
|
const surfacePatches = [];
|
|
25060
25059
|
const latitudeBoundaries = new Float64Array(latitudePatchCount + 1);
|
|
25061
25060
|
const longitudeBoundaries = new Float64Array(longitudePatchCount + 1);
|
|
@@ -25123,7 +25122,7 @@
|
|
|
25123
25122
|
transformedCorners[2].z +
|
|
25124
25123
|
transformedCorners[3].z) /
|
|
25125
25124
|
4,
|
|
25126
|
-
lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION), -1, 1),
|
|
25125
|
+
lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$1), -1, 1),
|
|
25127
25126
|
fillStyle: resolveContinuousSurfacePatchFillStyle(palette, verticalProgress, cachedCosByPatchCenterLongitude[longitudeIndex], cachedTentacleInfluencesByPatchCenterLongitude[longitudeIndex].core, cachedLobeWavesByPatchCenterLongitude[longitudeIndex]),
|
|
25128
25127
|
outlineColor: verticalProgress < 0.54 ? `${palette.highlight}69` : `${palette.shadow}78`,
|
|
25129
25128
|
});
|
|
@@ -25157,9 +25156,9 @@
|
|
|
25157
25156
|
const cosineLatitude = Math.max(0, Math.cos(latitude));
|
|
25158
25157
|
const verticalProgress = (Math.sin(latitude) + 1) / 2;
|
|
25159
25158
|
const upperBlend = Math.pow(1 - verticalProgress, 1.28);
|
|
25160
|
-
const lowerBlend = smoothStep(0.38, 1, verticalProgress);
|
|
25161
|
-
const tipBlend = smoothStep(0.68, 1, verticalProgress);
|
|
25162
|
-
const centerPull = resolveSignedAngularDistance(longitude, tentacleInfluence.centerLongitude);
|
|
25159
|
+
const lowerBlend = smoothStep$1(0.38, 1, verticalProgress);
|
|
25160
|
+
const tipBlend = smoothStep$1(0.68, 1, verticalProgress);
|
|
25161
|
+
const centerPull = resolveSignedAngularDistance$1(longitude, tentacleInfluence.centerLongitude);
|
|
25163
25162
|
const effectiveLongitude = longitude + centerPull * lowerBlend * tentacleInfluence.core * (0.24 + tipBlend * 0.2);
|
|
25164
25163
|
const mantleRipple = Math.sin(longitude * morphologyProfile.body.lobeCount +
|
|
25165
25164
|
animationPhase * 0.6 +
|
|
@@ -25225,7 +25224,7 @@
|
|
|
25225
25224
|
let weightedDepthScale = 0;
|
|
25226
25225
|
let weightedPhase = 0;
|
|
25227
25226
|
for (const tentacleProfile of options.tentacleProfiles) {
|
|
25228
|
-
const distance = Math.abs(resolveSignedAngularDistance(longitude, tentacleProfile.centerLongitude));
|
|
25227
|
+
const distance = Math.abs(resolveSignedAngularDistance$1(longitude, tentacleProfile.centerLongitude));
|
|
25229
25228
|
const width = 0.2 * tentacleProfile.widthScale;
|
|
25230
25229
|
const weight = Math.exp(-(distance * distance) / (width * width));
|
|
25231
25230
|
totalWeight += weight;
|
|
@@ -25265,7 +25264,7 @@
|
|
|
25265
25264
|
*/
|
|
25266
25265
|
function resolveContinuousLobeWave(options, longitude) {
|
|
25267
25266
|
const { morphologyProfile, animationPhase, timeMs } = options;
|
|
25268
|
-
return ((Math.cos(longitude * OCTOPUS_TENTACLE_COUNT +
|
|
25267
|
+
return ((Math.cos(longitude * OCTOPUS_TENTACLE_COUNT$1 +
|
|
25269
25268
|
animationPhase +
|
|
25270
25269
|
timeMs / (980 + morphologyProfile.body.lobeCount * 18)) +
|
|
25271
25270
|
1) /
|
|
@@ -25419,7 +25418,7 @@
|
|
|
25419
25418
|
*
|
|
25420
25419
|
* @private helper of `octopus3d3AvatarVisual`
|
|
25421
25420
|
*/
|
|
25422
|
-
function resolveSignedAngularDistance(sourceLongitude, targetLongitude) {
|
|
25421
|
+
function resolveSignedAngularDistance$1(sourceLongitude, targetLongitude) {
|
|
25423
25422
|
return Math.atan2(Math.sin(targetLongitude - sourceLongitude), Math.cos(targetLongitude - sourceLongitude));
|
|
25424
25423
|
}
|
|
25425
25424
|
/**
|
|
@@ -25427,11 +25426,767 @@
|
|
|
25427
25426
|
*
|
|
25428
25427
|
* @private helper of `octopus3d3AvatarVisual`
|
|
25429
25428
|
*/
|
|
25430
|
-
function smoothStep(edgeStart, edgeEnd, value) {
|
|
25429
|
+
function smoothStep$1(edgeStart, edgeEnd, value) {
|
|
25431
25430
|
const progress = clampNumber$1((value - edgeStart) / (edgeEnd - edgeStart), 0, 1);
|
|
25432
25431
|
return progress * progress * (3 - 2 * progress);
|
|
25433
25432
|
}
|
|
25434
25433
|
|
|
25434
|
+
/* eslint-disable no-magic-numbers */
|
|
25435
|
+
/**
|
|
25436
|
+
* Light direction used by the continuous Octopus 3D 4 mesh shading.
|
|
25437
|
+
*
|
|
25438
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25439
|
+
*/
|
|
25440
|
+
const LIGHT_DIRECTION = normalizeVector3({
|
|
25441
|
+
x: 0.32,
|
|
25442
|
+
y: -0.66,
|
|
25443
|
+
z: 1,
|
|
25444
|
+
});
|
|
25445
|
+
/**
|
|
25446
|
+
* Rim-light direction used to brighten the side of the silhouette.
|
|
25447
|
+
*
|
|
25448
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25449
|
+
*/
|
|
25450
|
+
const RIM_LIGHT_DIRECTION = normalizeVector3({
|
|
25451
|
+
x: -0.7,
|
|
25452
|
+
y: -0.18,
|
|
25453
|
+
z: 0.55,
|
|
25454
|
+
});
|
|
25455
|
+
/**
|
|
25456
|
+
* Real-octopus tentacle count used by the continuous lower mesh.
|
|
25457
|
+
*
|
|
25458
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25459
|
+
*/
|
|
25460
|
+
const OCTOPUS_TENTACLE_COUNT = 8;
|
|
25461
|
+
/**
|
|
25462
|
+
* Number of seeded skin spots painted across the octopus body.
|
|
25463
|
+
*
|
|
25464
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25465
|
+
*/
|
|
25466
|
+
const SKIN_SPOT_COUNT = 14;
|
|
25467
|
+
/**
|
|
25468
|
+
* Cache keyed by the `createRandom` factory reference, stable per mounted `<Avatar/>` component.
|
|
25469
|
+
*
|
|
25470
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25471
|
+
*/
|
|
25472
|
+
const octopus3d4StableStateCache = new WeakMap();
|
|
25473
|
+
/**
|
|
25474
|
+
* Returns the stable per-avatar state, computing it on first access and caching afterwards.
|
|
25475
|
+
*
|
|
25476
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25477
|
+
*/
|
|
25478
|
+
function getOctopus3d4StableState(createRandom) {
|
|
25479
|
+
const cached = octopus3d4StableStateCache.get(createRandom);
|
|
25480
|
+
if (cached !== undefined) {
|
|
25481
|
+
return cached;
|
|
25482
|
+
}
|
|
25483
|
+
const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
|
|
25484
|
+
const animationRandom = createRandom('octopus3d4-animation-profile');
|
|
25485
|
+
const eyeRandom = createRandom('octopus3d4-eye-profile');
|
|
25486
|
+
const leftEyePhaseOffset = eyeRandom() * 0.7;
|
|
25487
|
+
const rightEyePhaseOffset = eyeRandom() * 0.7;
|
|
25488
|
+
const state = {
|
|
25489
|
+
morphologyProfile,
|
|
25490
|
+
animationPhase: animationRandom() * Math.PI * 2,
|
|
25491
|
+
leftEyePhaseOffset,
|
|
25492
|
+
rightEyePhaseOffset,
|
|
25493
|
+
tentacleProfiles: createBlobbyContinuousTentacleProfiles(createRandom, morphologyProfile),
|
|
25494
|
+
skinSpots: createBlobbySkinSpots(createRandom),
|
|
25495
|
+
};
|
|
25496
|
+
octopus3d4StableStateCache.set(createRandom, state);
|
|
25497
|
+
return state;
|
|
25498
|
+
}
|
|
25499
|
+
/**
|
|
25500
|
+
* Octopus 3D 4 avatar visual.
|
|
25501
|
+
*
|
|
25502
|
+
* @private built-in avatar visual
|
|
25503
|
+
*/
|
|
25504
|
+
const octopus3d4AvatarVisual = {
|
|
25505
|
+
id: 'octopus3d4',
|
|
25506
|
+
title: 'Octopus 3D 4',
|
|
25507
|
+
description: 'Cute blobby 3D octopus rendered as one smooth continuous mesh with naturally undulating tentacles, rich multi-pass shading, pigment spots, and cursor-aware head and eyes.',
|
|
25508
|
+
isAnimated: true,
|
|
25509
|
+
supportsPointerTracking: true,
|
|
25510
|
+
render({ context, size, palette, createRandom, timeMs, interaction }) {
|
|
25511
|
+
const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset, tentacleProfiles, skinSpots } = getOctopus3d4StableState(createRandom);
|
|
25512
|
+
const sceneCenterX = size * 0.5;
|
|
25513
|
+
const sceneCenterY = size * 0.535;
|
|
25514
|
+
const bob = Math.sin(timeMs / 980 + animationPhase) * size * 0.013;
|
|
25515
|
+
const meshCenter = {
|
|
25516
|
+
x: interaction.bodyOffsetX * size * 0.052 + size * morphologyProfile.body.centerXJitterRatio * 0.44,
|
|
25517
|
+
y: -size * 0.07 + interaction.bodyOffsetY * size * 0.028 + bob,
|
|
25518
|
+
z: interaction.intensity * size * 0.02,
|
|
25519
|
+
};
|
|
25520
|
+
const rotationY = -0.08 +
|
|
25521
|
+
Math.sin(timeMs / 2800 + animationPhase) * 0.04 +
|
|
25522
|
+
interaction.bodyOffsetX * 0.24 +
|
|
25523
|
+
interaction.gazeX * 0.98;
|
|
25524
|
+
const rotationX = -0.07 +
|
|
25525
|
+
Math.cos(timeMs / 3200 + animationPhase * 0.7) * 0.02 -
|
|
25526
|
+
interaction.bodyOffsetY * 0.08 -
|
|
25527
|
+
interaction.gazeY * 0.42;
|
|
25528
|
+
const surfaceOptions = {
|
|
25529
|
+
radiusX: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.horizontalStretch * 1.12,
|
|
25530
|
+
radiusY: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.verticalStretch * 1.1,
|
|
25531
|
+
radiusZ: size *
|
|
25532
|
+
morphologyProfile.body.bodyRadiusRatio *
|
|
25533
|
+
(1.04 + (morphologyProfile.body.horizontalStretch - 1) * 0.2),
|
|
25534
|
+
morphologyProfile,
|
|
25535
|
+
timeMs,
|
|
25536
|
+
animationPhase,
|
|
25537
|
+
tentacleProfiles,
|
|
25538
|
+
};
|
|
25539
|
+
const surfacePatches = resolveVisibleBlobbyContinuousPatches({
|
|
25540
|
+
...surfaceOptions,
|
|
25541
|
+
center: meshCenter,
|
|
25542
|
+
rotationX,
|
|
25543
|
+
rotationY,
|
|
25544
|
+
sceneCenterX,
|
|
25545
|
+
sceneCenterY,
|
|
25546
|
+
size,
|
|
25547
|
+
palette,
|
|
25548
|
+
});
|
|
25549
|
+
const eyeLatitude = clampNumber$1(morphologyProfile.face.eyeCenterYOffsetRatio * 4.4 - 0.04, -0.24, 0.08);
|
|
25550
|
+
const eyeLongitude = clampNumber$1(morphologyProfile.face.eyeSpacingRatio * 3.0, 0.18, 0.32);
|
|
25551
|
+
const mouthLatitude = clampNumber$1(eyeLatitude + 0.21 + morphologyProfile.face.mouthYOffsetRatio, 0.08, 0.34);
|
|
25552
|
+
const mouthCenterLongitude = clampNumber$1(morphologyProfile.face.mouthCenterOffsetRatio * 5.6, -0.08, 0.08);
|
|
25553
|
+
const mouthHalfLongitude = clampNumber$1(eyeLongitude * 0.78, 0.15, 0.28);
|
|
25554
|
+
const mouthCurveLatitude = clampNumber$1(mouthLatitude + morphologyProfile.face.mouthCurveDepthRatio * 0.78, mouthLatitude + 0.03, 0.42);
|
|
25555
|
+
const eyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.78;
|
|
25556
|
+
const eyeRadiusY = eyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.92;
|
|
25557
|
+
drawAvatarFrame(context, size, palette);
|
|
25558
|
+
drawBlobbyContinuousAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs);
|
|
25559
|
+
drawBlobbyContinuousShadow(context, size, palette, interaction, timeMs, morphologyProfile);
|
|
25560
|
+
for (const surfacePatch of surfacePatches.sort((firstSurfacePatch, secondSurfacePatch) => firstSurfacePatch.averageDepth - secondSurfacePatch.averageDepth)) {
|
|
25561
|
+
drawBlobbyContinuousSurfacePatch(context, surfacePatch);
|
|
25562
|
+
}
|
|
25563
|
+
drawBlobbySkinSpots({
|
|
25564
|
+
context,
|
|
25565
|
+
surfaceOptions,
|
|
25566
|
+
center: meshCenter,
|
|
25567
|
+
rotationX,
|
|
25568
|
+
rotationY,
|
|
25569
|
+
sceneCenterX,
|
|
25570
|
+
sceneCenterY,
|
|
25571
|
+
size,
|
|
25572
|
+
palette,
|
|
25573
|
+
skinSpots,
|
|
25574
|
+
});
|
|
25575
|
+
drawBlobbyContinuousCurrents({
|
|
25576
|
+
context,
|
|
25577
|
+
surfaceOptions,
|
|
25578
|
+
center: meshCenter,
|
|
25579
|
+
rotationX,
|
|
25580
|
+
rotationY,
|
|
25581
|
+
sceneCenterX,
|
|
25582
|
+
sceneCenterY,
|
|
25583
|
+
size,
|
|
25584
|
+
palette,
|
|
25585
|
+
morphologyProfile,
|
|
25586
|
+
timeMs,
|
|
25587
|
+
animationPhase,
|
|
25588
|
+
});
|
|
25589
|
+
drawBlobbyContinuousSuckers({
|
|
25590
|
+
context,
|
|
25591
|
+
surfaceOptions,
|
|
25592
|
+
center: meshCenter,
|
|
25593
|
+
rotationX,
|
|
25594
|
+
rotationY,
|
|
25595
|
+
sceneCenterX,
|
|
25596
|
+
sceneCenterY,
|
|
25597
|
+
size,
|
|
25598
|
+
palette,
|
|
25599
|
+
});
|
|
25600
|
+
drawBlobbyContinuousGloss({
|
|
25601
|
+
context,
|
|
25602
|
+
surfaceOptions,
|
|
25603
|
+
center: meshCenter,
|
|
25604
|
+
rotationX,
|
|
25605
|
+
rotationY,
|
|
25606
|
+
sceneCenterX,
|
|
25607
|
+
sceneCenterY,
|
|
25608
|
+
size,
|
|
25609
|
+
palette,
|
|
25610
|
+
});
|
|
25611
|
+
drawProjectedOrganicEye(context, sampleBlobbyContinuousSurfacePoint(surfaceOptions, eyeLatitude, -eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
25612
|
+
drawProjectedOrganicEye(context, sampleBlobbyContinuousSurfacePoint(surfaceOptions, eyeLatitude, eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.85 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
25613
|
+
drawProjectedOrganicMouth(context, [
|
|
25614
|
+
sampleBlobbyContinuousSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude - mouthHalfLongitude),
|
|
25615
|
+
sampleBlobbyContinuousSurfacePoint(surfaceOptions, mouthCurveLatitude, mouthCenterLongitude),
|
|
25616
|
+
sampleBlobbyContinuousSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude + mouthHalfLongitude),
|
|
25617
|
+
], meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, palette, size);
|
|
25618
|
+
},
|
|
25619
|
+
};
|
|
25620
|
+
/**
|
|
25621
|
+
* Creates seeded tentacle-lobe profiles around the visible lower octopus body.
|
|
25622
|
+
*
|
|
25623
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25624
|
+
*/
|
|
25625
|
+
function createBlobbyContinuousTentacleProfiles(createRandom, morphologyProfile) {
|
|
25626
|
+
return Array.from({ length: OCTOPUS_TENTACLE_COUNT }, (_, tentacleIndex) => {
|
|
25627
|
+
const tentacleRandom = createRandom(`octopus3d4-tentacle-${tentacleIndex}`);
|
|
25628
|
+
const progress = tentacleIndex / (OCTOPUS_TENTACLE_COUNT - 1);
|
|
25629
|
+
return {
|
|
25630
|
+
centerLongitude: -Math.PI * 0.9 +
|
|
25631
|
+
progress * Math.PI * 1.8 +
|
|
25632
|
+
(tentacleRandom() - 0.5) * (0.06 + morphologyProfile.tentacles.rootSpreadScale * 0.025),
|
|
25633
|
+
widthScale: 0.92 + tentacleRandom() * 0.3 + (morphologyProfile.tentacles.baseWidthScale - 1) * 0.18,
|
|
25634
|
+
lengthScale: 0.9 + tentacleRandom() * 0.34 + (morphologyProfile.tentacles.flowLengthScale - 1) * 0.24,
|
|
25635
|
+
swayScale: 0.86 + tentacleRandom() * 0.4 + (morphologyProfile.tentacles.swayScale - 1) * 0.22,
|
|
25636
|
+
depthScale: 0.9 + tentacleRandom() * 0.3 + (morphologyProfile.tentacles.tipReachScale - 1) * 0.22,
|
|
25637
|
+
curlScale: 0.62 + tentacleRandom() * 0.46,
|
|
25638
|
+
primaryPhase: tentacleRandom() * Math.PI * 2,
|
|
25639
|
+
secondaryPhase: tentacleRandom() * Math.PI * 2,
|
|
25640
|
+
suckerSide: tentacleRandom() > 0.5 ? 1 : -1,
|
|
25641
|
+
};
|
|
25642
|
+
});
|
|
25643
|
+
}
|
|
25644
|
+
/**
|
|
25645
|
+
* Creates seeded skin pigment spots distributed across the upper octopus mesh.
|
|
25646
|
+
*
|
|
25647
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25648
|
+
*/
|
|
25649
|
+
function createBlobbySkinSpots(createRandom) {
|
|
25650
|
+
const spotRandom = createRandom('octopus3d4-skin-spots');
|
|
25651
|
+
return Array.from({ length: SKIN_SPOT_COUNT }, () => ({
|
|
25652
|
+
latitude: -0.45 + spotRandom() * 0.7,
|
|
25653
|
+
longitude: -0.5 + spotRandom() * 1.0,
|
|
25654
|
+
radiusScale: 0.0028 + spotRandom() * 0.0052,
|
|
25655
|
+
opacity: 0.16 + spotRandom() * 0.22,
|
|
25656
|
+
}));
|
|
25657
|
+
}
|
|
25658
|
+
/**
|
|
25659
|
+
* Draws the soft underwater atmosphere behind the continuous octopus mesh.
|
|
25660
|
+
*
|
|
25661
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25662
|
+
*/
|
|
25663
|
+
function drawBlobbyContinuousAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs) {
|
|
25664
|
+
const glowGradient = context.createRadialGradient(sceneCenterX + interaction.gazeX * size * 0.12, sceneCenterY - size * 0.18 + interaction.gazeY * size * 0.05, size * 0.04, sceneCenterX, sceneCenterY, size * (0.68 + interaction.intensity * 0.025));
|
|
25665
|
+
glowGradient.addColorStop(0, `${palette.highlight}74`);
|
|
25666
|
+
glowGradient.addColorStop(0.32, `${palette.accent}32`);
|
|
25667
|
+
glowGradient.addColorStop(1, `${palette.highlight}00`);
|
|
25668
|
+
context.fillStyle = glowGradient;
|
|
25669
|
+
context.fillRect(0, 0, size, size);
|
|
25670
|
+
const lowerGradient = context.createRadialGradient(sceneCenterX + Math.sin(timeMs / 1500) * size * 0.05, sceneCenterY + size * 0.3, size * 0.06, sceneCenterX, sceneCenterY + size * 0.3, size * 0.56);
|
|
25671
|
+
lowerGradient.addColorStop(0, `${palette.secondary}28`);
|
|
25672
|
+
lowerGradient.addColorStop(1, `${palette.secondary}00`);
|
|
25673
|
+
context.fillStyle = lowerGradient;
|
|
25674
|
+
context.fillRect(0, 0, size, size);
|
|
25675
|
+
}
|
|
25676
|
+
/**
|
|
25677
|
+
* Draws the soft lower shadow that anchors the octopus in the avatar frame.
|
|
25678
|
+
*
|
|
25679
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25680
|
+
*/
|
|
25681
|
+
function drawBlobbyContinuousShadow(context, size, palette, interaction, timeMs, morphologyProfile) {
|
|
25682
|
+
const cx = size * 0.5 + interaction.gazeX * size * 0.046;
|
|
25683
|
+
const cy = size * 0.9 + Math.sin(timeMs / 980) * size * 0.007;
|
|
25684
|
+
const rx = size * (0.2 + morphologyProfile.tentacles.rootSpreadScale * 0.024 + interaction.intensity * 0.022);
|
|
25685
|
+
const ry = size * 0.062;
|
|
25686
|
+
context.save();
|
|
25687
|
+
context.translate(cx, cy);
|
|
25688
|
+
context.scale(1, ry / rx);
|
|
25689
|
+
const blurRadius = rx * 1.42;
|
|
25690
|
+
const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
|
|
25691
|
+
shadowGradient.addColorStop(0, `${palette.shadow}82`);
|
|
25692
|
+
shadowGradient.addColorStop(0.45, `${palette.shadow}4a`);
|
|
25693
|
+
shadowGradient.addColorStop(0.8, `${palette.shadow}1c`);
|
|
25694
|
+
shadowGradient.addColorStop(1, `${palette.shadow}00`);
|
|
25695
|
+
context.fillStyle = shadowGradient;
|
|
25696
|
+
context.beginPath();
|
|
25697
|
+
context.arc(0, 0, blurRadius, 0, Math.PI * 2);
|
|
25698
|
+
context.fill();
|
|
25699
|
+
context.restore();
|
|
25700
|
+
}
|
|
25701
|
+
/**
|
|
25702
|
+
* Number of latitude segments used by the continuous Octopus 3D 4 mesh.
|
|
25703
|
+
*
|
|
25704
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25705
|
+
*/
|
|
25706
|
+
const LATITUDE_PATCH_COUNT = 20;
|
|
25707
|
+
/**
|
|
25708
|
+
* Number of longitude segments used by the continuous Octopus 3D 4 mesh.
|
|
25709
|
+
*
|
|
25710
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25711
|
+
*/
|
|
25712
|
+
const LONGITUDE_PATCH_COUNT = 48;
|
|
25713
|
+
/**
|
|
25714
|
+
* Resolves visible projected patches for the continuous Octopus 3D 4 mesh.
|
|
25715
|
+
*
|
|
25716
|
+
* Within a single frame, mesh corner samples and longitude-only computations (tentacle
|
|
25717
|
+
* influence and lobe wave) are quantized to the patch grid and computed once each rather
|
|
25718
|
+
* than re-evaluated for every patch corner.
|
|
25719
|
+
*
|
|
25720
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25721
|
+
*/
|
|
25722
|
+
function resolveVisibleBlobbyContinuousPatches(options) {
|
|
25723
|
+
const { center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette } = options;
|
|
25724
|
+
const latitudePatchCount = LATITUDE_PATCH_COUNT;
|
|
25725
|
+
const longitudePatchCount = LONGITUDE_PATCH_COUNT;
|
|
25726
|
+
const surfacePatches = [];
|
|
25727
|
+
const latitudeBoundaries = new Float64Array(latitudePatchCount + 1);
|
|
25728
|
+
const longitudeBoundaries = new Float64Array(longitudePatchCount + 1);
|
|
25729
|
+
for (let boundaryIndex = 0; boundaryIndex <= latitudePatchCount; boundaryIndex++) {
|
|
25730
|
+
latitudeBoundaries[boundaryIndex] = -Math.PI / 2 + (boundaryIndex / latitudePatchCount) * Math.PI;
|
|
25731
|
+
}
|
|
25732
|
+
for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
|
|
25733
|
+
longitudeBoundaries[boundaryIndex] = -Math.PI + (boundaryIndex / longitudePatchCount) * Math.PI * 2;
|
|
25734
|
+
}
|
|
25735
|
+
const cachedTentacleInfluencesByCornerLongitude = new Array(longitudePatchCount + 1);
|
|
25736
|
+
const cachedLobeWavesByCornerLongitude = new Float64Array(longitudePatchCount + 1);
|
|
25737
|
+
const cachedTentacleInfluencesByPatchCenterLongitude = new Array(longitudePatchCount);
|
|
25738
|
+
const cachedLobeWavesByPatchCenterLongitude = new Float64Array(longitudePatchCount);
|
|
25739
|
+
const cachedCosByPatchCenterLongitude = new Float64Array(longitudePatchCount);
|
|
25740
|
+
for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
|
|
25741
|
+
const cornerLongitude = longitudeBoundaries[boundaryIndex];
|
|
25742
|
+
cachedTentacleInfluencesByCornerLongitude[boundaryIndex] = resolveBlobbyTentacleInfluence(options, cornerLongitude);
|
|
25743
|
+
cachedLobeWavesByCornerLongitude[boundaryIndex] = resolveBlobbyLobeWave(options, cornerLongitude);
|
|
25744
|
+
}
|
|
25745
|
+
for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
|
|
25746
|
+
const patchCenterLongitude = (longitudeBoundaries[longitudeIndex] + longitudeBoundaries[longitudeIndex + 1]) / 2;
|
|
25747
|
+
cachedTentacleInfluencesByPatchCenterLongitude[longitudeIndex] = resolveBlobbyTentacleInfluence(options, patchCenterLongitude);
|
|
25748
|
+
cachedLobeWavesByPatchCenterLongitude[longitudeIndex] = resolveBlobbyLobeWave(options, patchCenterLongitude);
|
|
25749
|
+
cachedCosByPatchCenterLongitude[longitudeIndex] = Math.max(0, Math.cos(patchCenterLongitude));
|
|
25750
|
+
}
|
|
25751
|
+
const cornerCount = (latitudePatchCount + 1) * (longitudePatchCount + 1);
|
|
25752
|
+
const transformedCornerSamples = new Array(cornerCount);
|
|
25753
|
+
for (let latitudeBoundaryIndex = 0; latitudeBoundaryIndex <= latitudePatchCount; latitudeBoundaryIndex++) {
|
|
25754
|
+
const cornerLatitude = latitudeBoundaries[latitudeBoundaryIndex];
|
|
25755
|
+
for (let longitudeBoundaryIndex = 0; longitudeBoundaryIndex <= longitudePatchCount; longitudeBoundaryIndex++) {
|
|
25756
|
+
const cornerLongitude = longitudeBoundaries[longitudeBoundaryIndex];
|
|
25757
|
+
const cornerIndex = latitudeBoundaryIndex * (longitudePatchCount + 1) + longitudeBoundaryIndex;
|
|
25758
|
+
const cornerSample = sampleBlobbyContinuousSurfacePointWithLongitudeCache(options, cornerLatitude, cornerLongitude, cachedTentacleInfluencesByCornerLongitude[longitudeBoundaryIndex], cachedLobeWavesByCornerLongitude[longitudeBoundaryIndex]);
|
|
25759
|
+
transformedCornerSamples[cornerIndex] = transformScenePoint(cornerSample, center, rotationX, rotationY);
|
|
25760
|
+
}
|
|
25761
|
+
}
|
|
25762
|
+
for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
|
|
25763
|
+
const startLatitude = latitudeBoundaries[latitudeIndex];
|
|
25764
|
+
const endLatitude = latitudeBoundaries[latitudeIndex + 1];
|
|
25765
|
+
const centerLatitude = (startLatitude + endLatitude) / 2;
|
|
25766
|
+
const verticalProgress = (Math.sin(centerLatitude) + 1) / 2;
|
|
25767
|
+
const startCornerRowOffset = latitudeIndex * (longitudePatchCount + 1);
|
|
25768
|
+
const endCornerRowOffset = (latitudeIndex + 1) * (longitudePatchCount + 1);
|
|
25769
|
+
for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
|
|
25770
|
+
const transformedCorners = [
|
|
25771
|
+
transformedCornerSamples[startCornerRowOffset + longitudeIndex],
|
|
25772
|
+
transformedCornerSamples[startCornerRowOffset + longitudeIndex + 1],
|
|
25773
|
+
transformedCornerSamples[endCornerRowOffset + longitudeIndex + 1],
|
|
25774
|
+
transformedCornerSamples[endCornerRowOffset + longitudeIndex],
|
|
25775
|
+
];
|
|
25776
|
+
const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
|
|
25777
|
+
if (surfaceNormal.z <= 0.006) {
|
|
25778
|
+
continue;
|
|
25779
|
+
}
|
|
25780
|
+
const projectedCorners = [
|
|
25781
|
+
projectScenePoint(transformedCorners[0], size, sceneCenterX, sceneCenterY),
|
|
25782
|
+
projectScenePoint(transformedCorners[1], size, sceneCenterX, sceneCenterY),
|
|
25783
|
+
projectScenePoint(transformedCorners[2], size, sceneCenterX, sceneCenterY),
|
|
25784
|
+
projectScenePoint(transformedCorners[3], size, sceneCenterX, sceneCenterY),
|
|
25785
|
+
];
|
|
25786
|
+
const lightIntensity = clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION), -1, 1);
|
|
25787
|
+
const rimLightIntensity = Math.pow(clampNumber$1(dotProduct3D(surfaceNormal, RIM_LIGHT_DIRECTION), 0, 1), 2.6);
|
|
25788
|
+
surfacePatches.push({
|
|
25789
|
+
corners: projectedCorners,
|
|
25790
|
+
averageDepth: (transformedCorners[0].z +
|
|
25791
|
+
transformedCorners[1].z +
|
|
25792
|
+
transformedCorners[2].z +
|
|
25793
|
+
transformedCorners[3].z) /
|
|
25794
|
+
4,
|
|
25795
|
+
lightIntensity,
|
|
25796
|
+
rimLightIntensity,
|
|
25797
|
+
fillStyle: resolveBlobbySurfacePatchFillStyle(palette, verticalProgress, cachedCosByPatchCenterLongitude[longitudeIndex], cachedTentacleInfluencesByPatchCenterLongitude[longitudeIndex].core, cachedLobeWavesByPatchCenterLongitude[longitudeIndex]),
|
|
25798
|
+
outlineColor: verticalProgress < 0.52 ? `${palette.highlight}5c` : `${palette.shadow}66`,
|
|
25799
|
+
});
|
|
25800
|
+
}
|
|
25801
|
+
}
|
|
25802
|
+
return surfacePatches;
|
|
25803
|
+
}
|
|
25804
|
+
/**
|
|
25805
|
+
* Samples one point on the continuous Octopus 3D 4 surface.
|
|
25806
|
+
*
|
|
25807
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25808
|
+
*/
|
|
25809
|
+
function sampleBlobbyContinuousSurfacePoint(options, latitude, longitude) {
|
|
25810
|
+
return sampleBlobbyContinuousSurfacePointWithLongitudeCache(options, latitude, longitude, resolveBlobbyTentacleInfluence(options, longitude), resolveBlobbyLobeWave(options, longitude));
|
|
25811
|
+
}
|
|
25812
|
+
/**
|
|
25813
|
+
* Samples one point on the continuous Octopus 3D 4 surface using precomputed longitude-only values.
|
|
25814
|
+
*
|
|
25815
|
+
* The patch loop quantizes the mesh into a fixed corner grid, so the same longitude is reused
|
|
25816
|
+
* across every latitude row and each tentacle/lobe value is computed once per frame instead
|
|
25817
|
+
* of `latitudePatchCount * 4` times.
|
|
25818
|
+
*
|
|
25819
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25820
|
+
*/
|
|
25821
|
+
function sampleBlobbyContinuousSurfacePointWithLongitudeCache(options, latitude, longitude, tentacleInfluence, lowerLobeWave) {
|
|
25822
|
+
const { radiusX, radiusY, radiusZ, morphologyProfile, timeMs, animationPhase } = options;
|
|
25823
|
+
const cosineLatitude = Math.max(0, Math.cos(latitude));
|
|
25824
|
+
const verticalProgress = (Math.sin(latitude) + 1) / 2;
|
|
25825
|
+
const upperBlend = Math.pow(1 - verticalProgress, 1.32);
|
|
25826
|
+
const lowerBlend = smoothStep(0.34, 1, verticalProgress);
|
|
25827
|
+
const tipBlend = smoothStep(0.66, 1, verticalProgress);
|
|
25828
|
+
const centerPull = resolveSignedAngularDistance(longitude, tentacleInfluence.centerLongitude);
|
|
25829
|
+
const effectiveLongitude = longitude + centerPull * lowerBlend * tentacleInfluence.core * (0.26 + tipBlend * 0.22);
|
|
25830
|
+
const mantleRipple = Math.sin(longitude * morphologyProfile.body.lobeCount +
|
|
25831
|
+
animationPhase * 0.6 +
|
|
25832
|
+
timeMs / (1700 + morphologyProfile.body.lobeCount * 28)) *
|
|
25833
|
+
(0.018 + morphologyProfile.body.wobbleAmplitudeRatio * 0.78) *
|
|
25834
|
+
(0.32 + lowerBlend * 0.7);
|
|
25835
|
+
const primaryTentacleWave = Math.sin(timeMs / 740 + tentacleInfluence.primaryPhase + verticalProgress * 2.6) *
|
|
25836
|
+
lowerBlend *
|
|
25837
|
+
tentacleInfluence.core *
|
|
25838
|
+
tentacleInfluence.swayScale;
|
|
25839
|
+
const secondaryTentacleWave = Math.sin(timeMs / 470 + tentacleInfluence.secondaryPhase + verticalProgress * 4.2) *
|
|
25840
|
+
lowerBlend *
|
|
25841
|
+
tentacleInfluence.core *
|
|
25842
|
+
tentacleInfluence.swayScale *
|
|
25843
|
+
0.42;
|
|
25844
|
+
const tentacleCurl = Math.sin(timeMs / 1180 + tentacleInfluence.primaryPhase * 0.6 + verticalProgress * 3.2) *
|
|
25845
|
+
tipBlend *
|
|
25846
|
+
tentacleInfluence.core *
|
|
25847
|
+
tentacleInfluence.curlScale *
|
|
25848
|
+
0.36;
|
|
25849
|
+
const horizontalScale = 1.06 +
|
|
25850
|
+
mantleRipple +
|
|
25851
|
+
lowerBlend * (0.18 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.12) +
|
|
25852
|
+
lowerBlend * tentacleInfluence.core * (0.22 + lowerLobeWave * 0.14) -
|
|
25853
|
+
upperBlend * 0.08;
|
|
25854
|
+
const depthScale = 1.08 +
|
|
25855
|
+
upperBlend * 0.16 +
|
|
25856
|
+
Math.max(0, Math.cos(effectiveLongitude)) * 0.12 +
|
|
25857
|
+
lowerBlend * tentacleInfluence.core * (0.12 + tentacleInfluence.depthScale * 0.07) -
|
|
25858
|
+
Math.max(0, -Math.cos(effectiveLongitude)) * 0.05;
|
|
25859
|
+
const tentacleTubeRadius = lowerBlend *
|
|
25860
|
+
tentacleInfluence.core *
|
|
25861
|
+
(0.12 + tipBlend * 0.07 + tentacleInfluence.widthScale * 0.028) *
|
|
25862
|
+
radiusX;
|
|
25863
|
+
const planarRadiusX = cosineLatitude * radiusX * horizontalScale + tentacleTubeRadius;
|
|
25864
|
+
const planarRadiusZ = cosineLatitude * radiusZ * depthScale + tentacleTubeRadius * 0.74;
|
|
25865
|
+
const lowerDrop = lowerBlend *
|
|
25866
|
+
radiusY *
|
|
25867
|
+
(0.2 +
|
|
25868
|
+
tentacleInfluence.core *
|
|
25869
|
+
(0.42 +
|
|
25870
|
+
tentacleInfluence.lengthScale * 0.24 +
|
|
25871
|
+
(morphologyProfile.tentacles.flowLengthScale - 1) * 0.1));
|
|
25872
|
+
const combinedTentacleSway = (primaryTentacleWave + secondaryTentacleWave) * radiusX * (0.06 + tipBlend * 0.06);
|
|
25873
|
+
return {
|
|
25874
|
+
x: Math.sin(effectiveLongitude) * planarRadiusX +
|
|
25875
|
+
combinedTentacleSway +
|
|
25876
|
+
tentacleCurl * radiusX * 0.18,
|
|
25877
|
+
y: Math.sin(latitude) * radiusY * (1 + upperBlend * 0.14) -
|
|
25878
|
+
upperBlend * radiusY * 0.12 +
|
|
25879
|
+
lowerDrop +
|
|
25880
|
+
Math.sin(timeMs / 1380 + animationPhase + latitude * 1.5) * lowerBlend * radiusY * 0.022 +
|
|
25881
|
+
Math.cos(timeMs / 820 + tentacleInfluence.primaryPhase) *
|
|
25882
|
+
lowerBlend *
|
|
25883
|
+
tipBlend *
|
|
25884
|
+
tentacleInfluence.core *
|
|
25885
|
+
radiusY *
|
|
25886
|
+
0.04,
|
|
25887
|
+
z: Math.cos(effectiveLongitude) * planarRadiusZ +
|
|
25888
|
+
Math.cos(timeMs / 960 + tentacleInfluence.primaryPhase + verticalProgress) *
|
|
25889
|
+
lowerBlend *
|
|
25890
|
+
tentacleInfluence.core *
|
|
25891
|
+
radiusZ *
|
|
25892
|
+
0.044 +
|
|
25893
|
+
tentacleCurl * radiusZ * 0.14,
|
|
25894
|
+
};
|
|
25895
|
+
}
|
|
25896
|
+
/**
|
|
25897
|
+
* Blends nearby seeded tentacle profiles at one mesh longitude.
|
|
25898
|
+
*
|
|
25899
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25900
|
+
*/
|
|
25901
|
+
function resolveBlobbyTentacleInfluence(options, longitude) {
|
|
25902
|
+
let totalWeight = 0;
|
|
25903
|
+
let weightedSin = 0;
|
|
25904
|
+
let weightedCos = 0;
|
|
25905
|
+
let weightedWidthScale = 0;
|
|
25906
|
+
let weightedLengthScale = 0;
|
|
25907
|
+
let weightedSwayScale = 0;
|
|
25908
|
+
let weightedDepthScale = 0;
|
|
25909
|
+
let weightedCurlScale = 0;
|
|
25910
|
+
let weightedPrimaryPhase = 0;
|
|
25911
|
+
let weightedSecondaryPhase = 0;
|
|
25912
|
+
for (const tentacleProfile of options.tentacleProfiles) {
|
|
25913
|
+
const distance = Math.abs(resolveSignedAngularDistance(longitude, tentacleProfile.centerLongitude));
|
|
25914
|
+
const width = 0.22 * tentacleProfile.widthScale;
|
|
25915
|
+
const weight = Math.exp(-(distance * distance) / (width * width));
|
|
25916
|
+
totalWeight += weight;
|
|
25917
|
+
weightedSin += Math.sin(tentacleProfile.centerLongitude) * weight;
|
|
25918
|
+
weightedCos += Math.cos(tentacleProfile.centerLongitude) * weight;
|
|
25919
|
+
weightedWidthScale += tentacleProfile.widthScale * weight;
|
|
25920
|
+
weightedLengthScale += tentacleProfile.lengthScale * weight;
|
|
25921
|
+
weightedSwayScale += tentacleProfile.swayScale * weight;
|
|
25922
|
+
weightedDepthScale += tentacleProfile.depthScale * weight;
|
|
25923
|
+
weightedCurlScale += tentacleProfile.curlScale * weight;
|
|
25924
|
+
weightedPrimaryPhase += tentacleProfile.primaryPhase * weight;
|
|
25925
|
+
weightedSecondaryPhase += tentacleProfile.secondaryPhase * weight;
|
|
25926
|
+
}
|
|
25927
|
+
if (totalWeight < 0.0001) {
|
|
25928
|
+
return {
|
|
25929
|
+
core: 0,
|
|
25930
|
+
centerLongitude: longitude,
|
|
25931
|
+
widthScale: 1,
|
|
25932
|
+
lengthScale: 1,
|
|
25933
|
+
swayScale: 1,
|
|
25934
|
+
depthScale: 1,
|
|
25935
|
+
curlScale: 1,
|
|
25936
|
+
primaryPhase: 0,
|
|
25937
|
+
secondaryPhase: 0,
|
|
25938
|
+
};
|
|
25939
|
+
}
|
|
25940
|
+
return {
|
|
25941
|
+
core: clampNumber$1(totalWeight, 0, 1),
|
|
25942
|
+
centerLongitude: Math.atan2(weightedSin / totalWeight, weightedCos / totalWeight),
|
|
25943
|
+
widthScale: weightedWidthScale / totalWeight,
|
|
25944
|
+
lengthScale: weightedLengthScale / totalWeight,
|
|
25945
|
+
swayScale: weightedSwayScale / totalWeight,
|
|
25946
|
+
depthScale: weightedDepthScale / totalWeight,
|
|
25947
|
+
curlScale: weightedCurlScale / totalWeight,
|
|
25948
|
+
primaryPhase: weightedPrimaryPhase / totalWeight,
|
|
25949
|
+
secondaryPhase: weightedSecondaryPhase / totalWeight,
|
|
25950
|
+
};
|
|
25951
|
+
}
|
|
25952
|
+
/**
|
|
25953
|
+
* Resolves the soft lower wave that makes the continuous mesh read as a set of tentacles.
|
|
25954
|
+
*
|
|
25955
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25956
|
+
*/
|
|
25957
|
+
function resolveBlobbyLobeWave(options, longitude) {
|
|
25958
|
+
const { morphologyProfile, animationPhase, timeMs } = options;
|
|
25959
|
+
return ((Math.cos(longitude * OCTOPUS_TENTACLE_COUNT +
|
|
25960
|
+
animationPhase +
|
|
25961
|
+
timeMs / (940 + morphologyProfile.body.lobeCount * 18)) +
|
|
25962
|
+
1) /
|
|
25963
|
+
2);
|
|
25964
|
+
}
|
|
25965
|
+
/**
|
|
25966
|
+
* Resolves one base fill tone for a patch on the continuous octopus mesh.
|
|
25967
|
+
*
|
|
25968
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25969
|
+
*/
|
|
25970
|
+
function resolveBlobbySurfacePatchFillStyle(palette, verticalProgress, forwardness, tentacleCore, lowerLobeWave) {
|
|
25971
|
+
const tonalProgress = clampNumber$1(verticalProgress + lowerLobeWave * 0.1 + tentacleCore * 0.08 - forwardness * 0.08, 0, 1);
|
|
25972
|
+
if (tonalProgress < 0.12) {
|
|
25973
|
+
return palette.highlight;
|
|
25974
|
+
}
|
|
25975
|
+
if (tonalProgress < 0.3) {
|
|
25976
|
+
return palette.secondary;
|
|
25977
|
+
}
|
|
25978
|
+
if (tonalProgress < 0.72) {
|
|
25979
|
+
return forwardness > 0.56 ? palette.secondary : palette.primary;
|
|
25980
|
+
}
|
|
25981
|
+
return tentacleCore > 0.44 ? `${palette.primary}f4` : `${palette.shadow}ee`;
|
|
25982
|
+
}
|
|
25983
|
+
/**
|
|
25984
|
+
* Draws one projected mesh patch with multi-pass shading and a soft edge.
|
|
25985
|
+
*
|
|
25986
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
25987
|
+
*/
|
|
25988
|
+
function drawBlobbyContinuousSurfacePatch(context, surfacePatch) {
|
|
25989
|
+
drawProjectedQuad(context, surfacePatch.corners, surfacePatch.fillStyle);
|
|
25990
|
+
if (surfacePatch.lightIntensity > 0) {
|
|
25991
|
+
drawProjectedQuad(context, surfacePatch.corners, `rgba(255, 255, 255, ${0.2 * surfacePatch.lightIntensity})`);
|
|
25992
|
+
}
|
|
25993
|
+
else if (surfacePatch.lightIntensity < 0) {
|
|
25994
|
+
drawProjectedQuad(context, surfacePatch.corners, `rgba(0, 0, 0, ${0.26 * Math.abs(surfacePatch.lightIntensity)})`);
|
|
25995
|
+
}
|
|
25996
|
+
if (surfacePatch.rimLightIntensity > 0.04) {
|
|
25997
|
+
drawProjectedQuad(context, surfacePatch.corners, `rgba(255, 247, 230, ${0.22 * surfacePatch.rimLightIntensity})`);
|
|
25998
|
+
}
|
|
25999
|
+
context.save();
|
|
26000
|
+
context.beginPath();
|
|
26001
|
+
context.moveTo(surfacePatch.corners[0].x, surfacePatch.corners[0].y);
|
|
26002
|
+
for (let cornerIndex = 1; cornerIndex < surfacePatch.corners.length; cornerIndex++) {
|
|
26003
|
+
context.lineTo(surfacePatch.corners[cornerIndex].x, surfacePatch.corners[cornerIndex].y);
|
|
26004
|
+
}
|
|
26005
|
+
context.closePath();
|
|
26006
|
+
context.strokeStyle = surfacePatch.outlineColor;
|
|
26007
|
+
context.lineWidth = Math.max(0.6, getProjectedQuadPerimeter(surfacePatch.corners) * 0.0026);
|
|
26008
|
+
context.lineJoin = 'round';
|
|
26009
|
+
context.stroke();
|
|
26010
|
+
context.restore();
|
|
26011
|
+
}
|
|
26012
|
+
/**
|
|
26013
|
+
* Draws projected mantle-current lines on the front of the mesh.
|
|
26014
|
+
*
|
|
26015
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
26016
|
+
*/
|
|
26017
|
+
function drawBlobbyContinuousCurrents(options) {
|
|
26018
|
+
const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, morphologyProfile, timeMs, animationPhase, } = options;
|
|
26019
|
+
const currentCount = Math.min(6, morphologyProfile.details.mantleCurrentCount);
|
|
26020
|
+
const centerIndex = (currentCount - 1) / 2;
|
|
26021
|
+
context.save();
|
|
26022
|
+
context.lineCap = 'round';
|
|
26023
|
+
context.lineJoin = 'round';
|
|
26024
|
+
for (let currentIndex = 0; currentIndex < currentCount; currentIndex++) {
|
|
26025
|
+
const baseLongitude = (currentIndex - centerIndex) * 0.15;
|
|
26026
|
+
const projectedPoints = [];
|
|
26027
|
+
for (let sampleIndex = 0; sampleIndex < 9; sampleIndex++) {
|
|
26028
|
+
const progress = sampleIndex / 8;
|
|
26029
|
+
const latitude = -0.48 + progress * 0.78;
|
|
26030
|
+
const longitude = baseLongitude + Math.sin(timeMs / 1140 + animationPhase + currentIndex * 0.7 + progress * 2) * 0.038;
|
|
26031
|
+
const scenePoint = transformScenePoint(sampleBlobbyContinuousSurfacePoint(surfaceOptions, latitude, longitude), center, rotationX, rotationY);
|
|
26032
|
+
if (scenePoint.z > center.z - size * 0.016) {
|
|
26033
|
+
projectedPoints.push(projectScenePoint(scenePoint, size, sceneCenterX, sceneCenterY));
|
|
26034
|
+
}
|
|
26035
|
+
}
|
|
26036
|
+
if (projectedPoints.length < 3) {
|
|
26037
|
+
continue;
|
|
26038
|
+
}
|
|
26039
|
+
context.beginPath();
|
|
26040
|
+
context.moveTo(projectedPoints[0].x, projectedPoints[0].y);
|
|
26041
|
+
for (const projectedPoint of projectedPoints.slice(1)) {
|
|
26042
|
+
context.lineTo(projectedPoint.x, projectedPoint.y);
|
|
26043
|
+
}
|
|
26044
|
+
context.strokeStyle = currentIndex % 2 === 0 ? `${palette.highlight}3d` : `${palette.accent}33`;
|
|
26045
|
+
context.lineWidth = size * (0.0055 + currentIndex * 0.00045);
|
|
26046
|
+
context.stroke();
|
|
26047
|
+
}
|
|
26048
|
+
context.restore();
|
|
26049
|
+
}
|
|
26050
|
+
/**
|
|
26051
|
+
* Draws small projected sucker highlights on the waving lower mesh lobes.
|
|
26052
|
+
*
|
|
26053
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
26054
|
+
*/
|
|
26055
|
+
function drawBlobbyContinuousSuckers(options) {
|
|
26056
|
+
const { surfaceOptions, size } = options;
|
|
26057
|
+
const { timeMs } = surfaceOptions;
|
|
26058
|
+
for (const tentacleProfile of surfaceOptions.tentacleProfiles) {
|
|
26059
|
+
if (Math.cos(tentacleProfile.centerLongitude) < -0.16) {
|
|
26060
|
+
continue;
|
|
26061
|
+
}
|
|
26062
|
+
for (let suckerIndex = 0; suckerIndex < 4; suckerIndex++) {
|
|
26063
|
+
const latitude = 0.5 + suckerIndex * 0.12;
|
|
26064
|
+
const sideOffset = tentacleProfile.suckerSide * (0.038 + suckerIndex * 0.014) * tentacleProfile.widthScale;
|
|
26065
|
+
const waveOffset = Math.sin(timeMs / 880 + tentacleProfile.primaryPhase + suckerIndex * 0.78) * 0.02;
|
|
26066
|
+
drawBlobbyContinuousSurfaceSpot({
|
|
26067
|
+
...options,
|
|
26068
|
+
latitude,
|
|
26069
|
+
longitude: tentacleProfile.centerLongitude + sideOffset + waveOffset,
|
|
26070
|
+
radiusScale: size * (0.0068 - suckerIndex * 0.0006),
|
|
26071
|
+
});
|
|
26072
|
+
}
|
|
26073
|
+
}
|
|
26074
|
+
}
|
|
26075
|
+
/**
|
|
26076
|
+
* Draws seeded pigment spots across the upper mesh for a richer skin texture.
|
|
26077
|
+
*
|
|
26078
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
26079
|
+
*/
|
|
26080
|
+
function drawBlobbySkinSpots(options) {
|
|
26081
|
+
const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, skinSpots, } = options;
|
|
26082
|
+
for (const skinSpot of skinSpots) {
|
|
26083
|
+
const localCenter = sampleBlobbyContinuousSurfacePoint(surfaceOptions, skinSpot.latitude, skinSpot.longitude);
|
|
26084
|
+
const sceneCenterPoint = transformScenePoint(localCenter, center, rotationX, rotationY);
|
|
26085
|
+
if (sceneCenterPoint.z <= center.z - size * 0.01) {
|
|
26086
|
+
continue;
|
|
26087
|
+
}
|
|
26088
|
+
const projectedCenterPoint = projectScenePoint(sceneCenterPoint, size, sceneCenterX, sceneCenterY);
|
|
26089
|
+
const spotRadius = size * skinSpot.radiusScale;
|
|
26090
|
+
context.save();
|
|
26091
|
+
context.beginPath();
|
|
26092
|
+
context.arc(projectedCenterPoint.x, projectedCenterPoint.y, spotRadius, 0, Math.PI * 2);
|
|
26093
|
+
context.fillStyle = `${palette.shadow}${formatAlphaHex(skinSpot.opacity)}`;
|
|
26094
|
+
context.fill();
|
|
26095
|
+
context.restore();
|
|
26096
|
+
}
|
|
26097
|
+
}
|
|
26098
|
+
/**
|
|
26099
|
+
* Draws one tiny projected surface spot by sampling local mesh tangents.
|
|
26100
|
+
*
|
|
26101
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
26102
|
+
*/
|
|
26103
|
+
function drawBlobbyContinuousSurfaceSpot(options) {
|
|
26104
|
+
const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, latitude, longitude, radiusScale, } = options;
|
|
26105
|
+
const localCenter = sampleBlobbyContinuousSurfacePoint(surfaceOptions, latitude, longitude);
|
|
26106
|
+
const localHorizontal = sampleBlobbyContinuousSurfacePoint(surfaceOptions, latitude, longitude + 0.018);
|
|
26107
|
+
const localVertical = sampleBlobbyContinuousSurfacePoint(surfaceOptions, latitude + 0.018, longitude);
|
|
26108
|
+
const sceneCenterPoint = transformScenePoint(localCenter, center, rotationX, rotationY);
|
|
26109
|
+
if (sceneCenterPoint.z <= center.z - size * 0.012) {
|
|
26110
|
+
return;
|
|
26111
|
+
}
|
|
26112
|
+
const projectedCenterPoint = projectScenePoint(sceneCenterPoint, size, sceneCenterX, sceneCenterY);
|
|
26113
|
+
const projectedHorizontalPoint = projectScenePoint(transformScenePoint(localHorizontal, center, rotationX, rotationY), size, sceneCenterX, sceneCenterY);
|
|
26114
|
+
const projectedVerticalPoint = projectScenePoint(transformScenePoint(localVertical, center, rotationX, rotationY), size, sceneCenterX, sceneCenterY);
|
|
26115
|
+
const horizontalRadius = clampNumber$1(Math.hypot(projectedHorizontalPoint.x - projectedCenterPoint.x, projectedHorizontalPoint.y - projectedCenterPoint.y) *
|
|
26116
|
+
radiusScale *
|
|
26117
|
+
0.78, size * 0.003, size * 0.02);
|
|
26118
|
+
const verticalRadius = clampNumber$1(Math.hypot(projectedVerticalPoint.x - projectedCenterPoint.x, projectedVerticalPoint.y - projectedCenterPoint.y) *
|
|
26119
|
+
radiusScale *
|
|
26120
|
+
0.54, size * 0.0024, size * 0.015);
|
|
26121
|
+
const rotation = Math.atan2(projectedHorizontalPoint.y - projectedCenterPoint.y, projectedHorizontalPoint.x - projectedCenterPoint.x);
|
|
26122
|
+
context.save();
|
|
26123
|
+
context.translate(projectedCenterPoint.x, projectedCenterPoint.y);
|
|
26124
|
+
context.rotate(rotation);
|
|
26125
|
+
context.beginPath();
|
|
26126
|
+
context.ellipse(0, 0, horizontalRadius, verticalRadius, 0, 0, Math.PI * 2);
|
|
26127
|
+
context.fillStyle = `${palette.highlight}80`;
|
|
26128
|
+
context.fill();
|
|
26129
|
+
context.strokeStyle = `${palette.highlight}a8`;
|
|
26130
|
+
context.lineWidth = Math.max(0.7, size * 0.0028);
|
|
26131
|
+
context.stroke();
|
|
26132
|
+
context.restore();
|
|
26133
|
+
}
|
|
26134
|
+
/**
|
|
26135
|
+
* Draws a soft, slowly drifting gloss highlight on the front of the mesh.
|
|
26136
|
+
*
|
|
26137
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
26138
|
+
*/
|
|
26139
|
+
function drawBlobbyContinuousGloss(options) {
|
|
26140
|
+
const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size } = options;
|
|
26141
|
+
const { timeMs, animationPhase } = surfaceOptions;
|
|
26142
|
+
const glossLatitude = -0.3 + Math.sin(timeMs / 2700 + animationPhase) * 0.04;
|
|
26143
|
+
const glossLongitude = -0.18 + Math.cos(timeMs / 2300 + animationPhase * 0.8) * 0.05;
|
|
26144
|
+
const localCenter = sampleBlobbyContinuousSurfacePoint(surfaceOptions, glossLatitude, glossLongitude);
|
|
26145
|
+
const sceneCenterPoint = transformScenePoint(localCenter, center, rotationX, rotationY);
|
|
26146
|
+
if (sceneCenterPoint.z <= center.z) {
|
|
26147
|
+
return;
|
|
26148
|
+
}
|
|
26149
|
+
const projectedCenterPoint = projectScenePoint(sceneCenterPoint, size, sceneCenterX, sceneCenterY);
|
|
26150
|
+
const glossRadius = size * 0.058;
|
|
26151
|
+
context.save();
|
|
26152
|
+
const glossGradient = context.createRadialGradient(projectedCenterPoint.x - glossRadius * 0.3, projectedCenterPoint.y - glossRadius * 0.4, glossRadius * 0.04, projectedCenterPoint.x, projectedCenterPoint.y, glossRadius);
|
|
26153
|
+
glossGradient.addColorStop(0, 'rgba(255, 255, 255, 0.34)');
|
|
26154
|
+
glossGradient.addColorStop(0.5, 'rgba(255, 255, 255, 0.1)');
|
|
26155
|
+
glossGradient.addColorStop(1, 'rgba(255, 255, 255, 0)');
|
|
26156
|
+
context.fillStyle = glossGradient;
|
|
26157
|
+
context.beginPath();
|
|
26158
|
+
context.ellipse(projectedCenterPoint.x, projectedCenterPoint.y, glossRadius * 1.05, glossRadius * 0.78, 0, 0, Math.PI * 2);
|
|
26159
|
+
context.fill();
|
|
26160
|
+
context.restore();
|
|
26161
|
+
}
|
|
26162
|
+
/**
|
|
26163
|
+
* Resolves a signed angular distance from the source longitude to the target longitude.
|
|
26164
|
+
*
|
|
26165
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
26166
|
+
*/
|
|
26167
|
+
function resolveSignedAngularDistance(sourceLongitude, targetLongitude) {
|
|
26168
|
+
return Math.atan2(Math.sin(targetLongitude - sourceLongitude), Math.cos(targetLongitude - sourceLongitude));
|
|
26169
|
+
}
|
|
26170
|
+
/**
|
|
26171
|
+
* Smoothly maps a value between two bounds into `[0, 1]`.
|
|
26172
|
+
*
|
|
26173
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
26174
|
+
*/
|
|
26175
|
+
function smoothStep(edgeStart, edgeEnd, value) {
|
|
26176
|
+
const progress = clampNumber$1((value - edgeStart) / (edgeEnd - edgeStart), 0, 1);
|
|
26177
|
+
return progress * progress * (3 - 2 * progress);
|
|
26178
|
+
}
|
|
26179
|
+
/**
|
|
26180
|
+
* Converts an opacity ratio into a two-digit hexadecimal alpha suffix.
|
|
26181
|
+
*
|
|
26182
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
26183
|
+
*/
|
|
26184
|
+
function formatAlphaHex(opacity) {
|
|
26185
|
+
return Math.round(clampNumber$1(opacity, 0, 1) * 255)
|
|
26186
|
+
.toString(16)
|
|
26187
|
+
.padStart(2, '0');
|
|
26188
|
+
}
|
|
26189
|
+
|
|
25435
26190
|
/* eslint-disable no-magic-numbers */
|
|
25436
26191
|
/**
|
|
25437
26192
|
* Octopus avatar visual.
|
|
@@ -26203,6 +26958,7 @@
|
|
|
26203
26958
|
octopus3dAvatarVisual,
|
|
26204
26959
|
octopus3d2AvatarVisual,
|
|
26205
26960
|
octopus3d3AvatarVisual,
|
|
26961
|
+
octopus3d4AvatarVisual,
|
|
26206
26962
|
asciiOctopusAvatarVisual,
|
|
26207
26963
|
minecraftAvatarVisual,
|
|
26208
26964
|
minecraft2AvatarVisual,
|