@promptbook/components 0.112.0-102 → 0.112.0-104
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 +519 -8
- package/esm/index.es.js.map +1 -1
- package/esm/src/avatars/types/AvatarVisualDefinition.d.ts +1 -1
- package/esm/src/avatars/visuals/octopus3d3AvatarVisual.d.ts +7 -0
- package/esm/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/umd/index.umd.js +519 -8
- package/umd/index.umd.js.map +1 -1
- package/umd/src/avatars/types/AvatarVisualDefinition.d.ts +1 -1
- package/umd/src/avatars/visuals/octopus3d3AvatarVisual.d.ts +7 -0
- package/umd/src/version.d.ts +1 -1
|
@@ -7,7 +7,7 @@ import type { AvatarDefinition } from './AvatarDefinition';
|
|
|
7
7
|
*
|
|
8
8
|
* @private shared contract for the avatar rendering system
|
|
9
9
|
*/
|
|
10
|
-
export type AvatarVisualId = 'pixel-art' | 'octopus' | 'octopus2' | 'octopus3' | 'octopus3d' | 'octopus3d2' | 'ascii-octopus' | 'minecraft' | 'minecraft2' | 'fractal' | 'orb';
|
|
10
|
+
export type AvatarVisualId = 'pixel-art' | 'octopus' | 'octopus2' | 'octopus3' | 'octopus3d' | 'octopus3d2' | 'octopus3d3' | 'ascii-octopus' | 'minecraft' | 'minecraft2' | 'fractal' | 'orb';
|
|
11
11
|
/**
|
|
12
12
|
* Derived color palette used by avatar visuals.
|
|
13
13
|
*
|
package/esm/src/version.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export declare const BOOK_LANGUAGE_VERSION: string_semantic_version;
|
|
|
15
15
|
export declare const PROMPTBOOK_ENGINE_VERSION: string_promptbook_version;
|
|
16
16
|
/**
|
|
17
17
|
* Represents the version string of the Promptbook engine.
|
|
18
|
-
* It follows semantic versioning (e.g., `0.112.0-
|
|
18
|
+
* It follows semantic versioning (e.g., `0.112.0-102`).
|
|
19
19
|
*
|
|
20
20
|
* @generated
|
|
21
21
|
*/
|
package/package.json
CHANGED
package/umd/index.umd.js
CHANGED
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
* @generated
|
|
32
32
|
* @see https://github.com/webgptorg/promptbook
|
|
33
33
|
*/
|
|
34
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.112.0-
|
|
34
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.112.0-104';
|
|
35
35
|
/**
|
|
36
36
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
37
37
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -3243,7 +3243,7 @@
|
|
|
3243
3243
|
*
|
|
3244
3244
|
* @private helper of `minecraft2AvatarVisual`
|
|
3245
3245
|
*/
|
|
3246
|
-
const LIGHT_DIRECTION$
|
|
3246
|
+
const LIGHT_DIRECTION$3 = normalizeVector3({
|
|
3247
3247
|
x: 0.4,
|
|
3248
3248
|
y: -0.65,
|
|
3249
3249
|
z: 0.92,
|
|
@@ -3455,7 +3455,7 @@
|
|
|
3455
3455
|
corners: projectedCorners,
|
|
3456
3456
|
texture: faceDefinition.texture,
|
|
3457
3457
|
averageDepth: transformedCorners.reduce((depthSum, corner) => depthSum + corner.z, 0) / transformedCorners.length,
|
|
3458
|
-
lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION$
|
|
3458
|
+
lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION$3), -1, 1),
|
|
3459
3459
|
outlineColor: cuboid.outlineColor,
|
|
3460
3460
|
};
|
|
3461
3461
|
});
|
|
@@ -4646,7 +4646,7 @@
|
|
|
4646
4646
|
*
|
|
4647
4647
|
* @private helper of `octopus3dAvatarVisual`
|
|
4648
4648
|
*/
|
|
4649
|
-
const LIGHT_DIRECTION$
|
|
4649
|
+
const LIGHT_DIRECTION$2 = normalizeVector3({
|
|
4650
4650
|
x: 0.48,
|
|
4651
4651
|
y: -0.62,
|
|
4652
4652
|
z: 0.94,
|
|
@@ -4856,7 +4856,7 @@
|
|
|
4856
4856
|
corners: projectedCorners,
|
|
4857
4857
|
averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
|
|
4858
4858
|
transformedCorners.length,
|
|
4859
|
-
lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$
|
|
4859
|
+
lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$2), -1, 1),
|
|
4860
4860
|
fillStyle: resolveSurfacePatchFillStyle(palette, verticalProgress + verticalColorBias),
|
|
4861
4861
|
outlineColor,
|
|
4862
4862
|
});
|
|
@@ -5045,7 +5045,7 @@
|
|
|
5045
5045
|
*
|
|
5046
5046
|
* @private helper of `octopus3d2AvatarVisual`
|
|
5047
5047
|
*/
|
|
5048
|
-
const LIGHT_DIRECTION = normalizeVector3({
|
|
5048
|
+
const LIGHT_DIRECTION$1 = normalizeVector3({
|
|
5049
5049
|
x: 0.38,
|
|
5050
5050
|
y: -0.6,
|
|
5051
5051
|
z: 0.98,
|
|
@@ -5194,7 +5194,7 @@
|
|
|
5194
5194
|
corners: projectedCorners,
|
|
5195
5195
|
averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
|
|
5196
5196
|
transformedCorners.length,
|
|
5197
|
-
lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION), -1, 1),
|
|
5197
|
+
lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$1), -1, 1),
|
|
5198
5198
|
fillStyle: resolveBlobbySurfacePatchFillStyle(palette, verticalProgress, Math.max(0, Math.cos(centerLongitude)), resolveLowerLobeWave(centerLongitude, morphologyProfile, animationPhase, timeMs)),
|
|
5199
5199
|
outlineColor: verticalProgress < 0.58 ? `${palette.highlight}73` : `${palette.shadow}8a`,
|
|
5200
5200
|
});
|
|
@@ -5294,6 +5294,516 @@
|
|
|
5294
5294
|
context.restore();
|
|
5295
5295
|
}
|
|
5296
5296
|
|
|
5297
|
+
/* eslint-disable no-magic-numbers */
|
|
5298
|
+
/**
|
|
5299
|
+
* Light direction used by the continuous octopus mesh shading.
|
|
5300
|
+
*
|
|
5301
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
5302
|
+
*/
|
|
5303
|
+
const LIGHT_DIRECTION = normalizeVector3({
|
|
5304
|
+
x: 0.34,
|
|
5305
|
+
y: -0.62,
|
|
5306
|
+
z: 1,
|
|
5307
|
+
});
|
|
5308
|
+
/**
|
|
5309
|
+
* Real-octopus tentacle count used by the continuous lower mesh.
|
|
5310
|
+
*
|
|
5311
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
5312
|
+
*/
|
|
5313
|
+
const OCTOPUS_TENTACLE_COUNT = 8;
|
|
5314
|
+
/**
|
|
5315
|
+
* Octopus 3D 3 avatar visual.
|
|
5316
|
+
*
|
|
5317
|
+
* @private built-in avatar visual
|
|
5318
|
+
*/
|
|
5319
|
+
const octopus3d3AvatarVisual = {
|
|
5320
|
+
id: 'octopus3d3',
|
|
5321
|
+
title: 'Octopus 3D 3',
|
|
5322
|
+
description: 'Cute continuous 3D octopus with a blobby single mesh, waving tentacle lobes, rich shading, and cursor-aware eyes.',
|
|
5323
|
+
isAnimated: true,
|
|
5324
|
+
supportsPointerTracking: true,
|
|
5325
|
+
render({ context, size, palette, createRandom, timeMs, interaction }) {
|
|
5326
|
+
const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
|
|
5327
|
+
const animationRandom = createRandom('octopus3d3-animation-profile');
|
|
5328
|
+
const eyeRandom = createRandom('octopus3d3-eye-profile');
|
|
5329
|
+
const animationPhase = animationRandom() * Math.PI * 2;
|
|
5330
|
+
const tentacleProfiles = createContinuousTentacleProfiles(createRandom, morphologyProfile);
|
|
5331
|
+
const sceneCenterX = size * 0.5;
|
|
5332
|
+
const sceneCenterY = size * 0.535;
|
|
5333
|
+
const bob = Math.sin(timeMs / 960 + animationPhase) * size * 0.012;
|
|
5334
|
+
const meshCenter = {
|
|
5335
|
+
x: interaction.bodyOffsetX * size * 0.048 + size * morphologyProfile.body.centerXJitterRatio * 0.44,
|
|
5336
|
+
y: -size * 0.07 + interaction.bodyOffsetY * size * 0.026 + bob,
|
|
5337
|
+
z: interaction.intensity * size * 0.018,
|
|
5338
|
+
};
|
|
5339
|
+
const rotationY = -0.1 +
|
|
5340
|
+
Math.sin(timeMs / 2700 + animationPhase) * 0.035 +
|
|
5341
|
+
interaction.bodyOffsetX * 0.22 +
|
|
5342
|
+
interaction.gazeX * 0.88;
|
|
5343
|
+
const rotationX = -0.07 +
|
|
5344
|
+
Math.cos(timeMs / 3100 + animationPhase * 0.7) * 0.018 -
|
|
5345
|
+
interaction.bodyOffsetY * 0.08 -
|
|
5346
|
+
interaction.gazeY * 0.38;
|
|
5347
|
+
const surfaceOptions = {
|
|
5348
|
+
radiusX: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.horizontalStretch * 1.1,
|
|
5349
|
+
radiusY: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.verticalStretch * 1.08,
|
|
5350
|
+
radiusZ: size *
|
|
5351
|
+
morphologyProfile.body.bodyRadiusRatio *
|
|
5352
|
+
(1.02 + (morphologyProfile.body.horizontalStretch - 1) * 0.18),
|
|
5353
|
+
morphologyProfile,
|
|
5354
|
+
timeMs,
|
|
5355
|
+
animationPhase,
|
|
5356
|
+
tentacleProfiles,
|
|
5357
|
+
};
|
|
5358
|
+
const surfacePatches = resolveVisibleContinuousOctopusPatches({
|
|
5359
|
+
...surfaceOptions,
|
|
5360
|
+
center: meshCenter,
|
|
5361
|
+
rotationX,
|
|
5362
|
+
rotationY,
|
|
5363
|
+
sceneCenterX,
|
|
5364
|
+
sceneCenterY,
|
|
5365
|
+
size,
|
|
5366
|
+
palette,
|
|
5367
|
+
});
|
|
5368
|
+
const eyeLatitude = clampNumber$1(morphologyProfile.face.eyeCenterYOffsetRatio * 4.2 - 0.03, -0.22, 0.08);
|
|
5369
|
+
const eyeLongitude = clampNumber$1(morphologyProfile.face.eyeSpacingRatio * 3.1, 0.18, 0.32);
|
|
5370
|
+
const mouthLatitude = clampNumber$1(eyeLatitude + 0.2 + morphologyProfile.face.mouthYOffsetRatio, 0.08, 0.34);
|
|
5371
|
+
const mouthCenterLongitude = clampNumber$1(morphologyProfile.face.mouthCenterOffsetRatio * 5.6, -0.08, 0.08);
|
|
5372
|
+
const mouthHalfLongitude = clampNumber$1(eyeLongitude * 0.78, 0.15, 0.28);
|
|
5373
|
+
const mouthCurveLatitude = clampNumber$1(mouthLatitude + morphologyProfile.face.mouthCurveDepthRatio * 0.78, mouthLatitude + 0.03, 0.42);
|
|
5374
|
+
const eyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.76;
|
|
5375
|
+
const eyeRadiusY = eyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.9;
|
|
5376
|
+
drawAvatarFrame(context, size, palette);
|
|
5377
|
+
drawContinuousOctopusAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs);
|
|
5378
|
+
drawContinuousOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile);
|
|
5379
|
+
for (const surfacePatch of surfacePatches.sort((firstSurfacePatch, secondSurfacePatch) => firstSurfacePatch.averageDepth - secondSurfacePatch.averageDepth)) {
|
|
5380
|
+
drawContinuousSurfacePatch(context, surfacePatch);
|
|
5381
|
+
}
|
|
5382
|
+
drawProjectedSurfaceCurrents({
|
|
5383
|
+
context,
|
|
5384
|
+
surfaceOptions,
|
|
5385
|
+
center: meshCenter,
|
|
5386
|
+
rotationX,
|
|
5387
|
+
rotationY,
|
|
5388
|
+
sceneCenterX,
|
|
5389
|
+
sceneCenterY,
|
|
5390
|
+
size,
|
|
5391
|
+
palette,
|
|
5392
|
+
morphologyProfile,
|
|
5393
|
+
timeMs,
|
|
5394
|
+
animationPhase,
|
|
5395
|
+
});
|
|
5396
|
+
drawProjectedTentacleSuckers({
|
|
5397
|
+
context,
|
|
5398
|
+
surfaceOptions,
|
|
5399
|
+
center: meshCenter,
|
|
5400
|
+
rotationX,
|
|
5401
|
+
rotationY,
|
|
5402
|
+
sceneCenterX,
|
|
5403
|
+
sceneCenterY,
|
|
5404
|
+
size,
|
|
5405
|
+
palette,
|
|
5406
|
+
});
|
|
5407
|
+
drawProjectedOrganicEye(context, sampleContinuousOctopusSurfacePoint(surfaceOptions, eyeLatitude, -eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + eyeRandom() * 0.7, interaction, morphologyProfile.face.eyeStyle);
|
|
5408
|
+
drawProjectedOrganicEye(context, sampleContinuousOctopusSurfacePoint(surfaceOptions, eyeLatitude, eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.85 + eyeRandom() * 0.7, interaction, morphologyProfile.face.eyeStyle);
|
|
5409
|
+
drawProjectedOrganicMouth(context, [
|
|
5410
|
+
sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude - mouthHalfLongitude),
|
|
5411
|
+
sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthCurveLatitude, mouthCenterLongitude),
|
|
5412
|
+
sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude + mouthHalfLongitude),
|
|
5413
|
+
], meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, palette, size);
|
|
5414
|
+
},
|
|
5415
|
+
};
|
|
5416
|
+
/**
|
|
5417
|
+
* Creates seeded tentacle-lobe profiles around the visible lower octopus body.
|
|
5418
|
+
*
|
|
5419
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
5420
|
+
*/
|
|
5421
|
+
function createContinuousTentacleProfiles(createRandom, morphologyProfile) {
|
|
5422
|
+
return Array.from({ length: OCTOPUS_TENTACLE_COUNT }, (_, tentacleIndex) => {
|
|
5423
|
+
const tentacleRandom = createRandom(`octopus3d3-tentacle-${tentacleIndex}`);
|
|
5424
|
+
const progress = tentacleIndex / (OCTOPUS_TENTACLE_COUNT - 1);
|
|
5425
|
+
return {
|
|
5426
|
+
centerLongitude: -Math.PI * 0.86 +
|
|
5427
|
+
progress * Math.PI * 1.72 +
|
|
5428
|
+
(tentacleRandom() - 0.5) * (0.08 + morphologyProfile.tentacles.rootSpreadScale * 0.03),
|
|
5429
|
+
widthScale: 0.86 + tentacleRandom() * 0.34 + (morphologyProfile.tentacles.baseWidthScale - 1) * 0.16,
|
|
5430
|
+
lengthScale: 0.86 + tentacleRandom() * 0.36 + (morphologyProfile.tentacles.flowLengthScale - 1) * 0.22,
|
|
5431
|
+
swayScale: 0.82 + tentacleRandom() * 0.38 + (morphologyProfile.tentacles.swayScale - 1) * 0.2,
|
|
5432
|
+
depthScale: 0.86 + tentacleRandom() * 0.32 + (morphologyProfile.tentacles.tipReachScale - 1) * 0.2,
|
|
5433
|
+
phase: tentacleRandom() * Math.PI * 2,
|
|
5434
|
+
suckerSide: tentacleRandom() > 0.5 ? 1 : -1,
|
|
5435
|
+
};
|
|
5436
|
+
});
|
|
5437
|
+
}
|
|
5438
|
+
/**
|
|
5439
|
+
* Draws the soft underwater atmosphere behind the continuous octopus mesh.
|
|
5440
|
+
*
|
|
5441
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
5442
|
+
*/
|
|
5443
|
+
function drawContinuousOctopusAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs) {
|
|
5444
|
+
const glowGradient = context.createRadialGradient(sceneCenterX + interaction.gazeX * size * 0.11, sceneCenterY - size * 0.17 + interaction.gazeY * size * 0.05, size * 0.04, sceneCenterX, sceneCenterY, size * (0.66 + interaction.intensity * 0.02));
|
|
5445
|
+
glowGradient.addColorStop(0, `${palette.highlight}66`);
|
|
5446
|
+
glowGradient.addColorStop(0.34, `${palette.accent}2e`);
|
|
5447
|
+
glowGradient.addColorStop(1, `${palette.highlight}00`);
|
|
5448
|
+
context.fillStyle = glowGradient;
|
|
5449
|
+
context.fillRect(0, 0, size, size);
|
|
5450
|
+
const lowerGradient = context.createRadialGradient(sceneCenterX + Math.sin(timeMs / 1550) * size * 0.05, sceneCenterY + size * 0.29, size * 0.06, sceneCenterX, sceneCenterY + size * 0.3, size * 0.54);
|
|
5451
|
+
lowerGradient.addColorStop(0, `${palette.secondary}25`);
|
|
5452
|
+
lowerGradient.addColorStop(1, `${palette.secondary}00`);
|
|
5453
|
+
context.fillStyle = lowerGradient;
|
|
5454
|
+
context.fillRect(0, 0, size, size);
|
|
5455
|
+
}
|
|
5456
|
+
/**
|
|
5457
|
+
* Draws the soft lower shadow that anchors the octopus in the avatar frame.
|
|
5458
|
+
*
|
|
5459
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
5460
|
+
*/
|
|
5461
|
+
function drawContinuousOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile) {
|
|
5462
|
+
context.save();
|
|
5463
|
+
context.fillStyle = `${palette.shadow}66`;
|
|
5464
|
+
context.filter = `blur(${size * 0.025}px)`;
|
|
5465
|
+
context.beginPath();
|
|
5466
|
+
context.ellipse(size * 0.5 + interaction.gazeX * size * 0.045, size * 0.9 + Math.sin(timeMs / 980) * size * 0.007, size * (0.19 + morphologyProfile.tentacles.rootSpreadScale * 0.022 + interaction.intensity * 0.02), size * 0.06, 0, 0, Math.PI * 2);
|
|
5467
|
+
context.fill();
|
|
5468
|
+
context.restore();
|
|
5469
|
+
}
|
|
5470
|
+
/**
|
|
5471
|
+
* Resolves visible projected patches for the continuous octopus mesh.
|
|
5472
|
+
*
|
|
5473
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
5474
|
+
*/
|
|
5475
|
+
function resolveVisibleContinuousOctopusPatches(options) {
|
|
5476
|
+
const { center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette } = options;
|
|
5477
|
+
const latitudePatchCount = 16;
|
|
5478
|
+
const longitudePatchCount = 40;
|
|
5479
|
+
const surfacePatches = [];
|
|
5480
|
+
for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
|
|
5481
|
+
const startLatitude = -Math.PI / 2 + (latitudeIndex / latitudePatchCount) * Math.PI;
|
|
5482
|
+
const endLatitude = -Math.PI / 2 + ((latitudeIndex + 1) / latitudePatchCount) * Math.PI;
|
|
5483
|
+
const centerLatitude = (startLatitude + endLatitude) / 2;
|
|
5484
|
+
const verticalProgress = (Math.sin(centerLatitude) + 1) / 2;
|
|
5485
|
+
for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
|
|
5486
|
+
const startLongitude = -Math.PI + (longitudeIndex / longitudePatchCount) * Math.PI * 2;
|
|
5487
|
+
const endLongitude = -Math.PI + ((longitudeIndex + 1) / longitudePatchCount) * Math.PI * 2;
|
|
5488
|
+
const centerLongitude = (startLongitude + endLongitude) / 2;
|
|
5489
|
+
const localCorners = [
|
|
5490
|
+
sampleContinuousOctopusSurfacePoint(options, startLatitude, startLongitude),
|
|
5491
|
+
sampleContinuousOctopusSurfacePoint(options, startLatitude, endLongitude),
|
|
5492
|
+
sampleContinuousOctopusSurfacePoint(options, endLatitude, endLongitude),
|
|
5493
|
+
sampleContinuousOctopusSurfacePoint(options, endLatitude, startLongitude),
|
|
5494
|
+
];
|
|
5495
|
+
const transformedCorners = localCorners.map((localCorner) => transformScenePoint(localCorner, center, rotationX, rotationY));
|
|
5496
|
+
const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
|
|
5497
|
+
if (surfaceNormal.z <= 0.008) {
|
|
5498
|
+
continue;
|
|
5499
|
+
}
|
|
5500
|
+
const projectedCorners = transformedCorners.map((transformedCorner) => projectScenePoint(transformedCorner, size, sceneCenterX, sceneCenterY));
|
|
5501
|
+
const tentacleInfluence = resolveContinuousTentacleInfluence(options, centerLongitude);
|
|
5502
|
+
const lowerLobeWave = resolveContinuousLobeWave(options, centerLongitude);
|
|
5503
|
+
surfacePatches.push({
|
|
5504
|
+
corners: projectedCorners,
|
|
5505
|
+
averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
|
|
5506
|
+
transformedCorners.length,
|
|
5507
|
+
lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION), -1, 1),
|
|
5508
|
+
fillStyle: resolveContinuousSurfacePatchFillStyle(palette, verticalProgress, Math.max(0, Math.cos(centerLongitude)), tentacleInfluence.core, lowerLobeWave),
|
|
5509
|
+
outlineColor: verticalProgress < 0.54 ? `${palette.highlight}69` : `${palette.shadow}78`,
|
|
5510
|
+
});
|
|
5511
|
+
}
|
|
5512
|
+
}
|
|
5513
|
+
return surfacePatches;
|
|
5514
|
+
}
|
|
5515
|
+
/**
|
|
5516
|
+
* Samples one point on the continuous Octopus 3D 3 surface.
|
|
5517
|
+
*
|
|
5518
|
+
* The lower hemisphere is pulled into eight seeded waving lobes, so the portrait reads as
|
|
5519
|
+
* tentacled while still being rendered as one connected blobby mesh.
|
|
5520
|
+
*
|
|
5521
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
5522
|
+
*/
|
|
5523
|
+
function sampleContinuousOctopusSurfacePoint(options, latitude, longitude) {
|
|
5524
|
+
const { radiusX, radiusY, radiusZ, morphologyProfile, timeMs, animationPhase } = options;
|
|
5525
|
+
const cosineLatitude = Math.max(0, Math.cos(latitude));
|
|
5526
|
+
const verticalProgress = (Math.sin(latitude) + 1) / 2;
|
|
5527
|
+
const upperBlend = Math.pow(1 - verticalProgress, 1.28);
|
|
5528
|
+
const lowerBlend = smoothStep(0.38, 1, verticalProgress);
|
|
5529
|
+
const tipBlend = smoothStep(0.68, 1, verticalProgress);
|
|
5530
|
+
const tentacleInfluence = resolveContinuousTentacleInfluence(options, longitude);
|
|
5531
|
+
const centerPull = resolveSignedAngularDistance(longitude, tentacleInfluence.centerLongitude);
|
|
5532
|
+
const effectiveLongitude = longitude + centerPull * lowerBlend * tentacleInfluence.core * (0.24 + tipBlend * 0.2);
|
|
5533
|
+
const lowerLobeWave = resolveContinuousLobeWave(options, longitude);
|
|
5534
|
+
const mantleRipple = Math.sin(longitude * morphologyProfile.body.lobeCount +
|
|
5535
|
+
animationPhase * 0.6 +
|
|
5536
|
+
timeMs / (1750 + morphologyProfile.body.lobeCount * 30)) *
|
|
5537
|
+
(0.018 + morphologyProfile.body.wobbleAmplitudeRatio * 0.8) *
|
|
5538
|
+
(0.3 + lowerBlend * 0.7);
|
|
5539
|
+
const tentacleWave = Math.sin(timeMs / 760 + tentacleInfluence.phase + verticalProgress * 2.4) *
|
|
5540
|
+
lowerBlend *
|
|
5541
|
+
tentacleInfluence.core *
|
|
5542
|
+
tentacleInfluence.swayScale;
|
|
5543
|
+
const horizontalScale = 1.04 +
|
|
5544
|
+
mantleRipple +
|
|
5545
|
+
lowerBlend * (0.16 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.1) +
|
|
5546
|
+
lowerBlend * tentacleInfluence.core * (0.2 + lowerLobeWave * 0.12) -
|
|
5547
|
+
upperBlend * 0.08;
|
|
5548
|
+
const depthScale = 1.06 +
|
|
5549
|
+
upperBlend * 0.16 +
|
|
5550
|
+
Math.max(0, Math.cos(effectiveLongitude)) * 0.1 +
|
|
5551
|
+
lowerBlend * tentacleInfluence.core * (0.1 + tentacleInfluence.depthScale * 0.06) -
|
|
5552
|
+
Math.max(0, -Math.cos(effectiveLongitude)) * 0.05;
|
|
5553
|
+
const tentacleTubeRadius = lowerBlend *
|
|
5554
|
+
tentacleInfluence.core *
|
|
5555
|
+
(0.11 + tipBlend * 0.06 + tentacleInfluence.widthScale * 0.025) *
|
|
5556
|
+
radiusX;
|
|
5557
|
+
const planarRadiusX = cosineLatitude * radiusX * horizontalScale + tentacleTubeRadius;
|
|
5558
|
+
const planarRadiusZ = cosineLatitude * radiusZ * depthScale + tentacleTubeRadius * 0.72;
|
|
5559
|
+
const lowerDrop = lowerBlend *
|
|
5560
|
+
radiusY *
|
|
5561
|
+
(0.18 +
|
|
5562
|
+
tentacleInfluence.core *
|
|
5563
|
+
(0.38 +
|
|
5564
|
+
tentacleInfluence.lengthScale * 0.22 +
|
|
5565
|
+
(morphologyProfile.tentacles.flowLengthScale - 1) * 0.08));
|
|
5566
|
+
return {
|
|
5567
|
+
x: Math.sin(effectiveLongitude) * planarRadiusX +
|
|
5568
|
+
tentacleWave * radiusX * (0.052 + tipBlend * 0.05),
|
|
5569
|
+
y: Math.sin(latitude) * radiusY * (1 + upperBlend * 0.12) -
|
|
5570
|
+
upperBlend * radiusY * 0.1 +
|
|
5571
|
+
lowerDrop +
|
|
5572
|
+
Math.sin(timeMs / 1420 + animationPhase + latitude * 1.6) * lowerBlend * radiusY * 0.018 +
|
|
5573
|
+
Math.cos(timeMs / 880 + tentacleInfluence.phase) *
|
|
5574
|
+
lowerBlend *
|
|
5575
|
+
tipBlend *
|
|
5576
|
+
tentacleInfluence.core *
|
|
5577
|
+
radiusY *
|
|
5578
|
+
0.034,
|
|
5579
|
+
z: Math.cos(effectiveLongitude) * planarRadiusZ +
|
|
5580
|
+
Math.cos(timeMs / 980 + tentacleInfluence.phase + verticalProgress) *
|
|
5581
|
+
lowerBlend *
|
|
5582
|
+
tentacleInfluence.core *
|
|
5583
|
+
radiusZ *
|
|
5584
|
+
0.04,
|
|
5585
|
+
};
|
|
5586
|
+
}
|
|
5587
|
+
/**
|
|
5588
|
+
* Blends nearby seeded tentacle profiles at one mesh longitude.
|
|
5589
|
+
*
|
|
5590
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
5591
|
+
*/
|
|
5592
|
+
function resolveContinuousTentacleInfluence(options, longitude) {
|
|
5593
|
+
let totalWeight = 0;
|
|
5594
|
+
let weightedSin = 0;
|
|
5595
|
+
let weightedCos = 0;
|
|
5596
|
+
let weightedWidthScale = 0;
|
|
5597
|
+
let weightedLengthScale = 0;
|
|
5598
|
+
let weightedSwayScale = 0;
|
|
5599
|
+
let weightedDepthScale = 0;
|
|
5600
|
+
let weightedPhase = 0;
|
|
5601
|
+
for (const tentacleProfile of options.tentacleProfiles) {
|
|
5602
|
+
const distance = Math.abs(resolveSignedAngularDistance(longitude, tentacleProfile.centerLongitude));
|
|
5603
|
+
const width = 0.2 * tentacleProfile.widthScale;
|
|
5604
|
+
const weight = Math.exp(-(distance * distance) / (width * width));
|
|
5605
|
+
totalWeight += weight;
|
|
5606
|
+
weightedSin += Math.sin(tentacleProfile.centerLongitude) * weight;
|
|
5607
|
+
weightedCos += Math.cos(tentacleProfile.centerLongitude) * weight;
|
|
5608
|
+
weightedWidthScale += tentacleProfile.widthScale * weight;
|
|
5609
|
+
weightedLengthScale += tentacleProfile.lengthScale * weight;
|
|
5610
|
+
weightedSwayScale += tentacleProfile.swayScale * weight;
|
|
5611
|
+
weightedDepthScale += tentacleProfile.depthScale * weight;
|
|
5612
|
+
weightedPhase += tentacleProfile.phase * weight;
|
|
5613
|
+
}
|
|
5614
|
+
if (totalWeight < 0.0001) {
|
|
5615
|
+
return {
|
|
5616
|
+
core: 0,
|
|
5617
|
+
centerLongitude: longitude,
|
|
5618
|
+
widthScale: 1,
|
|
5619
|
+
lengthScale: 1,
|
|
5620
|
+
swayScale: 1,
|
|
5621
|
+
depthScale: 1,
|
|
5622
|
+
phase: 0,
|
|
5623
|
+
};
|
|
5624
|
+
}
|
|
5625
|
+
return {
|
|
5626
|
+
core: clampNumber$1(totalWeight, 0, 1),
|
|
5627
|
+
centerLongitude: Math.atan2(weightedSin / totalWeight, weightedCos / totalWeight),
|
|
5628
|
+
widthScale: weightedWidthScale / totalWeight,
|
|
5629
|
+
lengthScale: weightedLengthScale / totalWeight,
|
|
5630
|
+
swayScale: weightedSwayScale / totalWeight,
|
|
5631
|
+
depthScale: weightedDepthScale / totalWeight,
|
|
5632
|
+
phase: weightedPhase / totalWeight,
|
|
5633
|
+
};
|
|
5634
|
+
}
|
|
5635
|
+
/**
|
|
5636
|
+
* Resolves the soft lower wave that makes the continuous mesh read as a set of tentacles.
|
|
5637
|
+
*
|
|
5638
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
5639
|
+
*/
|
|
5640
|
+
function resolveContinuousLobeWave(options, longitude) {
|
|
5641
|
+
const { morphologyProfile, animationPhase, timeMs } = options;
|
|
5642
|
+
return (Math.cos(longitude * OCTOPUS_TENTACLE_COUNT + animationPhase + timeMs / (980 + morphologyProfile.body.lobeCount * 18)) +
|
|
5643
|
+
1) / 2;
|
|
5644
|
+
}
|
|
5645
|
+
/**
|
|
5646
|
+
* Resolves one base fill tone for a patch on the continuous octopus mesh.
|
|
5647
|
+
*
|
|
5648
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
5649
|
+
*/
|
|
5650
|
+
function resolveContinuousSurfacePatchFillStyle(palette, verticalProgress, forwardness, tentacleCore, lowerLobeWave) {
|
|
5651
|
+
const tonalProgress = clampNumber$1(verticalProgress + lowerLobeWave * 0.1 + tentacleCore * 0.08 - forwardness * 0.08, 0, 1);
|
|
5652
|
+
if (tonalProgress < 0.14) {
|
|
5653
|
+
return palette.highlight;
|
|
5654
|
+
}
|
|
5655
|
+
if (tonalProgress < 0.32) {
|
|
5656
|
+
return palette.secondary;
|
|
5657
|
+
}
|
|
5658
|
+
if (tonalProgress < 0.72) {
|
|
5659
|
+
return forwardness > 0.55 ? palette.secondary : palette.primary;
|
|
5660
|
+
}
|
|
5661
|
+
return tentacleCore > 0.44 ? `${palette.primary}f4` : `${palette.shadow}ee`;
|
|
5662
|
+
}
|
|
5663
|
+
/**
|
|
5664
|
+
* Draws one projected mesh patch with soft shading and a subtle edge.
|
|
5665
|
+
*
|
|
5666
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
5667
|
+
*/
|
|
5668
|
+
function drawContinuousSurfacePatch(context, surfacePatch) {
|
|
5669
|
+
drawProjectedQuad(context, surfacePatch.corners, surfacePatch.fillStyle);
|
|
5670
|
+
if (surfacePatch.lightIntensity > 0) {
|
|
5671
|
+
drawProjectedQuad(context, surfacePatch.corners, `rgba(255, 255, 255, ${0.18 * surfacePatch.lightIntensity})`);
|
|
5672
|
+
}
|
|
5673
|
+
else if (surfacePatch.lightIntensity < 0) {
|
|
5674
|
+
drawProjectedQuad(context, surfacePatch.corners, `rgba(0, 0, 0, ${0.25 * Math.abs(surfacePatch.lightIntensity)})`);
|
|
5675
|
+
}
|
|
5676
|
+
context.save();
|
|
5677
|
+
context.beginPath();
|
|
5678
|
+
context.moveTo(surfacePatch.corners[0].x, surfacePatch.corners[0].y);
|
|
5679
|
+
for (let cornerIndex = 1; cornerIndex < surfacePatch.corners.length; cornerIndex++) {
|
|
5680
|
+
context.lineTo(surfacePatch.corners[cornerIndex].x, surfacePatch.corners[cornerIndex].y);
|
|
5681
|
+
}
|
|
5682
|
+
context.closePath();
|
|
5683
|
+
context.strokeStyle = surfacePatch.outlineColor;
|
|
5684
|
+
context.lineWidth = Math.max(0.7, getProjectedQuadPerimeter(surfacePatch.corners) * 0.0032);
|
|
5685
|
+
context.lineJoin = 'round';
|
|
5686
|
+
context.stroke();
|
|
5687
|
+
context.restore();
|
|
5688
|
+
}
|
|
5689
|
+
/**
|
|
5690
|
+
* Draws projected mantle-current lines on the front of the mesh.
|
|
5691
|
+
*
|
|
5692
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
5693
|
+
*/
|
|
5694
|
+
function drawProjectedSurfaceCurrents(options) {
|
|
5695
|
+
const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, morphologyProfile, timeMs, animationPhase, } = options;
|
|
5696
|
+
const currentCount = Math.min(6, morphologyProfile.details.mantleCurrentCount);
|
|
5697
|
+
const centerIndex = (currentCount - 1) / 2;
|
|
5698
|
+
context.save();
|
|
5699
|
+
context.lineCap = 'round';
|
|
5700
|
+
context.lineJoin = 'round';
|
|
5701
|
+
for (let currentIndex = 0; currentIndex < currentCount; currentIndex++) {
|
|
5702
|
+
const baseLongitude = (currentIndex - centerIndex) * 0.15;
|
|
5703
|
+
const projectedPoints = [];
|
|
5704
|
+
for (let sampleIndex = 0; sampleIndex < 8; sampleIndex++) {
|
|
5705
|
+
const progress = sampleIndex / 7;
|
|
5706
|
+
const latitude = -0.46 + progress * 0.74;
|
|
5707
|
+
const longitude = baseLongitude +
|
|
5708
|
+
Math.sin(timeMs / 1160 + animationPhase + currentIndex * 0.7 + progress * 2) * 0.035;
|
|
5709
|
+
const scenePoint = transformScenePoint(sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude, longitude), center, rotationX, rotationY);
|
|
5710
|
+
if (scenePoint.z > center.z - size * 0.016) {
|
|
5711
|
+
projectedPoints.push(projectScenePoint(scenePoint, size, sceneCenterX, sceneCenterY));
|
|
5712
|
+
}
|
|
5713
|
+
}
|
|
5714
|
+
if (projectedPoints.length < 3) {
|
|
5715
|
+
continue;
|
|
5716
|
+
}
|
|
5717
|
+
context.beginPath();
|
|
5718
|
+
context.moveTo(projectedPoints[0].x, projectedPoints[0].y);
|
|
5719
|
+
for (const projectedPoint of projectedPoints.slice(1)) {
|
|
5720
|
+
context.lineTo(projectedPoint.x, projectedPoint.y);
|
|
5721
|
+
}
|
|
5722
|
+
context.strokeStyle = currentIndex % 2 === 0 ? `${palette.highlight}3d` : `${palette.accent}33`;
|
|
5723
|
+
context.lineWidth = size * (0.0055 + currentIndex * 0.00045);
|
|
5724
|
+
context.stroke();
|
|
5725
|
+
}
|
|
5726
|
+
context.restore();
|
|
5727
|
+
}
|
|
5728
|
+
/**
|
|
5729
|
+
* Draws small projected sucker highlights on the waving lower mesh lobes.
|
|
5730
|
+
*
|
|
5731
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
5732
|
+
*/
|
|
5733
|
+
function drawProjectedTentacleSuckers(options) {
|
|
5734
|
+
const { surfaceOptions, size } = options;
|
|
5735
|
+
const { timeMs } = surfaceOptions;
|
|
5736
|
+
for (const tentacleProfile of surfaceOptions.tentacleProfiles) {
|
|
5737
|
+
if (Math.cos(tentacleProfile.centerLongitude) < -0.12) {
|
|
5738
|
+
continue;
|
|
5739
|
+
}
|
|
5740
|
+
for (let suckerIndex = 0; suckerIndex < 3; suckerIndex++) {
|
|
5741
|
+
const latitude = 0.52 + suckerIndex * 0.14;
|
|
5742
|
+
const sideOffset = tentacleProfile.suckerSide * (0.035 + suckerIndex * 0.012) * tentacleProfile.widthScale;
|
|
5743
|
+
const waveOffset = Math.sin(timeMs / 900 + tentacleProfile.phase + suckerIndex * 0.8) * 0.018;
|
|
5744
|
+
drawProjectedSurfaceSpot({
|
|
5745
|
+
...options,
|
|
5746
|
+
latitude,
|
|
5747
|
+
longitude: tentacleProfile.centerLongitude + sideOffset + waveOffset,
|
|
5748
|
+
radiusScale: size * (0.0065 - suckerIndex * 0.0007),
|
|
5749
|
+
});
|
|
5750
|
+
}
|
|
5751
|
+
}
|
|
5752
|
+
}
|
|
5753
|
+
/**
|
|
5754
|
+
* Draws one tiny projected surface spot by sampling local mesh tangents.
|
|
5755
|
+
*
|
|
5756
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
5757
|
+
*/
|
|
5758
|
+
function drawProjectedSurfaceSpot(options) {
|
|
5759
|
+
const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, latitude, longitude, radiusScale, } = options;
|
|
5760
|
+
const localCenter = sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude, longitude);
|
|
5761
|
+
const localHorizontal = sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude, longitude + 0.018);
|
|
5762
|
+
const localVertical = sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude + 0.018, longitude);
|
|
5763
|
+
const sceneCenterPoint = transformScenePoint(localCenter, center, rotationX, rotationY);
|
|
5764
|
+
if (sceneCenterPoint.z <= center.z - size * 0.012) {
|
|
5765
|
+
return;
|
|
5766
|
+
}
|
|
5767
|
+
const projectedCenterPoint = projectScenePoint(sceneCenterPoint, size, sceneCenterX, sceneCenterY);
|
|
5768
|
+
const projectedHorizontalPoint = projectScenePoint(transformScenePoint(localHorizontal, center, rotationX, rotationY), size, sceneCenterX, sceneCenterY);
|
|
5769
|
+
const projectedVerticalPoint = projectScenePoint(transformScenePoint(localVertical, center, rotationX, rotationY), size, sceneCenterX, sceneCenterY);
|
|
5770
|
+
const horizontalRadius = clampNumber$1(Math.hypot(projectedHorizontalPoint.x - projectedCenterPoint.x, projectedHorizontalPoint.y - projectedCenterPoint.y) *
|
|
5771
|
+
radiusScale *
|
|
5772
|
+
0.74, size * 0.003, size * 0.018);
|
|
5773
|
+
const verticalRadius = clampNumber$1(Math.hypot(projectedVerticalPoint.x - projectedCenterPoint.x, projectedVerticalPoint.y - projectedCenterPoint.y) *
|
|
5774
|
+
radiusScale *
|
|
5775
|
+
0.52, size * 0.0024, size * 0.014);
|
|
5776
|
+
const rotation = Math.atan2(projectedHorizontalPoint.y - projectedCenterPoint.y, projectedHorizontalPoint.x - projectedCenterPoint.x);
|
|
5777
|
+
context.save();
|
|
5778
|
+
context.translate(projectedCenterPoint.x, projectedCenterPoint.y);
|
|
5779
|
+
context.rotate(rotation);
|
|
5780
|
+
context.beginPath();
|
|
5781
|
+
context.ellipse(0, 0, horizontalRadius, verticalRadius, 0, 0, Math.PI * 2);
|
|
5782
|
+
context.fillStyle = `${palette.highlight}73`;
|
|
5783
|
+
context.fill();
|
|
5784
|
+
context.strokeStyle = `${palette.highlight}99`;
|
|
5785
|
+
context.lineWidth = Math.max(0.7, size * 0.0028);
|
|
5786
|
+
context.stroke();
|
|
5787
|
+
context.restore();
|
|
5788
|
+
}
|
|
5789
|
+
/**
|
|
5790
|
+
* Resolves a signed angular distance from the source longitude to the target longitude.
|
|
5791
|
+
*
|
|
5792
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
5793
|
+
*/
|
|
5794
|
+
function resolveSignedAngularDistance(sourceLongitude, targetLongitude) {
|
|
5795
|
+
return Math.atan2(Math.sin(targetLongitude - sourceLongitude), Math.cos(targetLongitude - sourceLongitude));
|
|
5796
|
+
}
|
|
5797
|
+
/**
|
|
5798
|
+
* Smoothly maps a value between two bounds into `[0, 1]`.
|
|
5799
|
+
*
|
|
5800
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
5801
|
+
*/
|
|
5802
|
+
function smoothStep(edgeStart, edgeEnd, value) {
|
|
5803
|
+
const progress = clampNumber$1((value - edgeStart) / (edgeEnd - edgeStart), 0, 1);
|
|
5804
|
+
return progress * progress * (3 - 2 * progress);
|
|
5805
|
+
}
|
|
5806
|
+
|
|
5297
5807
|
/* eslint-disable no-magic-numbers */
|
|
5298
5808
|
/**
|
|
5299
5809
|
* Octopus avatar visual.
|
|
@@ -6064,6 +6574,7 @@
|
|
|
6064
6574
|
octopus3AvatarVisual,
|
|
6065
6575
|
octopus3dAvatarVisual,
|
|
6066
6576
|
octopus3d2AvatarVisual,
|
|
6577
|
+
octopus3d3AvatarVisual,
|
|
6067
6578
|
asciiOctopusAvatarVisual,
|
|
6068
6579
|
minecraftAvatarVisual,
|
|
6069
6580
|
minecraft2AvatarVisual,
|
|
@@ -6145,7 +6656,7 @@
|
|
|
6145
6656
|
*
|
|
6146
6657
|
* @private shared avatar contract
|
|
6147
6658
|
*/
|
|
6148
|
-
const DEFAULT_AGENT_AVATAR_VISUAL_ID = '
|
|
6659
|
+
const DEFAULT_AGENT_AVATAR_VISUAL_ID = 'octopus3d3';
|
|
6149
6660
|
/**
|
|
6150
6661
|
* Resolves the avatar visual preferred by an agent, then falls back to a federated server default
|
|
6151
6662
|
* and finally to the caller/server default.
|