incanto 0.3.3 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/3d.d.ts +68 -7
- package/dist/3d.js +4 -4
- package/dist/{create-game-DKdR28SI.js → create-game-DlTZUCSj.js} +2 -2
- package/dist/index.js +1 -1
- package/dist/{physics-3d-DXODbpY1.js → physics-3d-Yvle9Clx.js} +1 -1
- package/dist/react.js +1 -1
- package/dist/{register-CljkZG08.js → register-9mSsSUS1.js} +119 -20
- package/dist/test.js +3 -3
- package/editor/assets/{agent8-CXBTOrkj.js → agent8-B-6dWaK9.js} +1 -1
- package/editor/assets/{index-DMghTAy4.js → index-BN0hHg3U.js} +36 -36
- package/editor/index.html +1 -1
- package/package.json +1 -1
- package/schemas/scene.schema.json +94 -0
- package/skills/incanto-building-3d-games.md +49 -1
- package/skills/incanto-environment.md +1 -1
- package/skills/incanto-node-reference.md +11 -0
package/dist/3d.d.ts
CHANGED
|
@@ -543,6 +543,51 @@ declare class Environment3D {
|
|
|
543
543
|
dispose(): void;
|
|
544
544
|
}
|
|
545
545
|
//#endregion
|
|
546
|
+
//#region src/3d/nodes/billboard-3d.d.ts
|
|
547
|
+
/** Valid `Billboard3D.mode` values — drives the prop options AND validateJson. */
|
|
548
|
+
declare const BILLBOARD_GROUP_MODES: readonly ["screen", "y", "none"];
|
|
549
|
+
type BillboardGroupMode = (typeof BILLBOARD_GROUP_MODES)[number];
|
|
550
|
+
/**
|
|
551
|
+
* Billboard3D — a transform GROUP that orients itself toward the camera every
|
|
552
|
+
* rendered frame; children inherit the rotation through the scene graph. Hang
|
|
553
|
+
* world-space UI off a character — HP bars, markers, icons — without
|
|
554
|
+
* hand-rolling camera math in game code.
|
|
555
|
+
*
|
|
556
|
+
* - `mode: "screen"` (default) copies the camera's orientation wholesale, so a
|
|
557
|
+
* child rectangle projects as an upright rectangle ANYWHERE on screen. This is
|
|
558
|
+
* what flat UI wants — a yaw-only look-at visibly tilts off-center shapes when
|
|
559
|
+
* the camera pitches. (Deliberately NOT the same as Sprite3D's `"full"`, which
|
|
560
|
+
* faces the camera POSITION and keeps that perspective tilt.)
|
|
561
|
+
* - `mode: "y"` yaws the group's +Z upright toward the camera (like Sprite3D's
|
|
562
|
+
* `"y"`) — for standing content that must not pitch back as the camera rises.
|
|
563
|
+
* - `mode: "none"` billboarding off — the authored transform applies again on
|
|
564
|
+
* the next frame (`_syncObject3D` re-stamps it), so toggling is lossless.
|
|
565
|
+
*
|
|
566
|
+
* The node's own `rotation` prop is overwritten while billboarding ("none"
|
|
567
|
+
* restores it — also handy in the editor to pose children with the gizmo).
|
|
568
|
+
* The group keeps orienting while `visible: false` ON PURPOSE: a hidden HP bar
|
|
569
|
+
* revealed later is already correct — no one-frame orientation pop. Headless
|
|
570
|
+
* runs never invoke the render hook — the group keeps its authored transform.
|
|
571
|
+
*/
|
|
572
|
+
declare class Billboard3D extends Node3D {
|
|
573
|
+
static override readonly typeName: string;
|
|
574
|
+
static override readonly props: PropSchema;
|
|
575
|
+
/** `"screen"` copy the camera orientation (screen-aligned) · `"y"` upright
|
|
576
|
+
* yaw-to-camera · `"none"` billboarding off (authored rotation applies). */
|
|
577
|
+
mode: BillboardGroupMode;
|
|
578
|
+
/** Last successfully billboarded WORLD orientation — reapplied on the frames
|
|
579
|
+
* where `"y"` has no answer (camera dead overhead), because `_syncObject3D`
|
|
580
|
+
* re-stamps the authored rotation every frame and a bare early-return would
|
|
581
|
+
* visibly snap the group to it. Lazily created; `null` until first success. */
|
|
582
|
+
private _lastDesired;
|
|
583
|
+
/** Scene-load validation: a typo'd mode must fail HARD, not silently behave
|
|
584
|
+
* like one of the real modes (agents self-correct on load errors). */
|
|
585
|
+
static validateJson(node: Node): void;
|
|
586
|
+
/** @internal Orient the BACKING object each rendered frame — unlike Sprite3D
|
|
587
|
+
* (which spins only its inner quad) the whole group turns, so children follow. */
|
|
588
|
+
_onRender3D(ctx: RenderContext3D): void;
|
|
589
|
+
}
|
|
590
|
+
//#endregion
|
|
546
591
|
//#region src/3d/nodes/camera-3d.d.ts
|
|
547
592
|
/**
|
|
548
593
|
* A perspective camera. Mark exactly one camera `current: true`; with none
|
|
@@ -740,7 +785,11 @@ type FoliageStyle = "mesh" | "tufts" | "blades" | "simple";
|
|
|
740
785
|
* (`colorA`→`colorB`, clump-coherent, dry patches) and the ported sin·cos
|
|
741
786
|
* tuft sway. Dozens of overlapping textured fans read as a DENSE meadow
|
|
742
787
|
* (잔디밭) at a fraction of the instances — the look that per-blade
|
|
743
|
-
* geometry never reaches at sane budgets.
|
|
788
|
+
* geometry never reaches at sane budgets. NOTE: tuft cards are big — drawn
|
|
789
|
+
* ~1.5× the `height` prop and wider than tall — and opaque (depth-writing),
|
|
790
|
+
* so over a field of SMALL gameplay sprites (mobs, items) they legitimately
|
|
791
|
+
* occlude actors shorter than the grass. For such fields prefer `'blades'`
|
|
792
|
+
* (thin) or keep `height` low so actors read above the grass line.
|
|
744
793
|
*
|
|
745
794
|
* - `'mesh'` (default, kind 'grass' only): the per-blade path — ONE
|
|
746
795
|
* InstancedMesh where every instance is a REAL tapered blade (7-vertex
|
|
@@ -786,7 +835,9 @@ declare class Foliage3D extends Node3D implements SunConsumer3D {
|
|
|
786
835
|
/** Instance cap (hard ceiling 200_000). */
|
|
787
836
|
maxInstances: number;
|
|
788
837
|
/** Base blade height in meters — mesh style additionally scales it 0.55–1.7×
|
|
789
|
-
* regionally (low-frequency noise: tall patches and short worn patches)
|
|
838
|
+
* regionally (low-frequency noise: tall patches and short worn patches), and
|
|
839
|
+
* `tufts` cards render ~1.5× this value (so 0.4 ≈ 0.6 m tall). Keep it low under
|
|
840
|
+
* small gameplay sprites or they hide behind the grass — see the `style` doc. */
|
|
790
841
|
height: number;
|
|
791
842
|
/** colorA = bottom/lerp-start, colorB = top/lerp-end. Refinement pass 3:
|
|
792
843
|
* lifted toward the reference's warm sunlit green (the old olives read
|
|
@@ -962,6 +1013,14 @@ interface MeshMaterialProps {
|
|
|
962
1013
|
flatShading?: boolean;
|
|
963
1014
|
/** 0..1 surface opacity; below 1 turns on transparency (glassy gems, etc.). */
|
|
964
1015
|
opacity?: number;
|
|
1016
|
+
/** Depth-test against the scene (default true). Set false so the mesh ALWAYS
|
|
1017
|
+
* draws on top of geometry behind it — the standard trick for flat ground
|
|
1018
|
+
* decals (AoE telegraphs, selection rings) that must never z-fight with or be
|
|
1019
|
+
* occluded by bumpy terrain. Pair with a high `renderOrder`. */
|
|
1020
|
+
depthTest?: boolean;
|
|
1021
|
+
/** Write to the depth buffer (default true). Set false for additive/overlay
|
|
1022
|
+
* decals so they don't occlude effects drawn after them. */
|
|
1023
|
+
depthWrite?: boolean;
|
|
965
1024
|
/** Color texture URL (sampled sRGB, tinted by `color`). */
|
|
966
1025
|
map?: string;
|
|
967
1026
|
/** Tangent-space normal map URL (sampled linear). */
|
|
@@ -978,10 +1037,12 @@ interface MeshMaterialProps {
|
|
|
978
1037
|
* - `size [x,y,z]`: box extents; sphere/gem radius = x; capsule radius = x,
|
|
979
1038
|
* height = y; plane = x·z ground plane (laid flat); cylinder radius = x, height = y
|
|
980
1039
|
* - `material`: `{color, metalness, roughness, wireframe, flatShading, opacity,
|
|
981
|
-
* emissive, emissiveIntensity, map, normalMap, repeat}` —
|
|
982
|
-
* texture URLs loaded lazily (headless-safe: geometry and
|
|
983
|
-
* wait on them), tiled by `repeat: [u, v]`. `gem` + `flatShading:
|
|
984
|
-
* `roughness` reads as a sparkling faceted jewel (spin it for the
|
|
1040
|
+
* depthTest, depthWrite, emissive, emissiveIntensity, map, normalMap, repeat}` —
|
|
1041
|
+
* `map`/`normalMap` are texture URLs loaded lazily (headless-safe: geometry and
|
|
1042
|
+
* physics never wait on them), tiled by `repeat: [u, v]`. `gem` + `flatShading:
|
|
1043
|
+
* true` + a low `roughness` reads as a sparkling faceted jewel (spin it for the
|
|
1044
|
+
* sparkle). `depthTest: false` makes a flat `plane`/`cylinder` an always-on-top
|
|
1045
|
+
* ground decal (AoE telegraph, selection ring) — no z-fighting with terrain.
|
|
985
1046
|
*/
|
|
986
1047
|
declare class MeshInstance3D extends Node3D {
|
|
987
1048
|
static override readonly typeName: string;
|
|
@@ -1860,4 +1921,4 @@ interface WalkState {
|
|
|
1860
1921
|
alpha: number;
|
|
1861
1922
|
}
|
|
1862
1923
|
//#endregion
|
|
1863
|
-
export { Area3D, AssetStore3D, Camera3D, CharacterBody3D, CharacterController3D, type CreateGame3DOptions, DEFAULT_TERRAIN_TEXTURE_BASE, DirectionalLight3D, Environment3D, type Environment3DConfig, DENSITY_PRESETS as FLOWER_DENSITY_PRESETS, FLOWER_VARIETIES, type FlowerVariety, Flowers3D, type FogEnvironment, Foliage3D, type FoliageKind, type FoliageStyle, type Game3D, type Heightmap, type HeightmapOptions, MeshInstance3D, type MeshKind, type MeshMaterialProps, type ModelEntry, ModelInstance3D, Node3D, OmniLight3D, Particles3D, Physics3D, type Physics3DOptions, PhysicsBody3D, QUARTER_PITCH, type RenderContext3D, type RenderHook3D, Renderer3D, type Renderer3DOptions, type RigView, RigidBody3D, type Ripple, type ShadowsEnvironment, type SkyEnvironment, StaticBody3D, type SunConsumer3D, type SyncOptions, type SyncResult, TERRAIN_THEMES, Terrain3D, type TerrainLayer, type TerrainTheme, Tree3D, type TreeTier, type TreeType, VOXEL_PALETTE, type VoxelBlock, VoxelGrid3D, WATER_MAX_RIPPLES, Water3D, buildHeightmap, cameraRelative, createGame3D, enablePhysics3D, horizonColorFromSky, keyboardIntensity, movementState, parseEnvironment3D, registerNodes3D, resolveFlowerDensity, rigPose, splatWeights, sunDirectionFromElevationAzimuth, sunDirectionFromSky, syncTree, terrainThemeLayers };
|
|
1924
|
+
export { Area3D, AssetStore3D, Billboard3D, type BillboardGroupMode, Camera3D, CharacterBody3D, CharacterController3D, type CreateGame3DOptions, DEFAULT_TERRAIN_TEXTURE_BASE, DirectionalLight3D, Environment3D, type Environment3DConfig, DENSITY_PRESETS as FLOWER_DENSITY_PRESETS, FLOWER_VARIETIES, type FlowerVariety, Flowers3D, type FogEnvironment, Foliage3D, type FoliageKind, type FoliageStyle, type Game3D, type Heightmap, type HeightmapOptions, MeshInstance3D, type MeshKind, type MeshMaterialProps, type ModelEntry, ModelInstance3D, Node3D, OmniLight3D, Particles3D, Physics3D, type Physics3DOptions, PhysicsBody3D, QUARTER_PITCH, type RenderContext3D, type RenderHook3D, Renderer3D, type Renderer3DOptions, type RigView, RigidBody3D, type Ripple, type ShadowsEnvironment, type SkyEnvironment, StaticBody3D, type SunConsumer3D, type SyncOptions, type SyncResult, TERRAIN_THEMES, Terrain3D, type TerrainLayer, type TerrainTheme, Tree3D, type TreeTier, type TreeType, VOXEL_PALETTE, type VoxelBlock, VoxelGrid3D, WATER_MAX_RIPPLES, Water3D, buildHeightmap, cameraRelative, createGame3D, enablePhysics3D, horizonColorFromSky, keyboardIntensity, movementState, parseEnvironment3D, registerNodes3D, resolveFlowerDensity, rigPose, splatWeights, sunDirectionFromElevationAzimuth, sunDirectionFromSky, syncTree, terrainThemeLayers };
|
package/dist/3d.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as Environment3D, c as sunDirectionFromElevationAzimuth, i as syncTree, l as sunDirectionFromSky, o as horizonColorFromSky, r as Renderer3D, s as parseEnvironment3D, t as createGame3D, u as AssetStore3D } from "./create-game-
|
|
2
|
-
import {
|
|
1
|
+
import { a as Environment3D, c as sunDirectionFromElevationAzimuth, i as syncTree, l as sunDirectionFromSky, o as horizonColorFromSky, r as Renderer3D, s as parseEnvironment3D, t as createGame3D, u as AssetStore3D } from "./create-game-DlTZUCSj.js";
|
|
2
|
+
import { C as terrainThemeLayers, D as RigidBody3D, E as PhysicsBody3D, F as rigPose, M as cameraRelative, N as keyboardIntensity, O as StaticBody3D, P as movementState, S as TERRAIN_THEMES, T as CharacterBody3D, _ as CharacterController3D, a as VoxelGrid3D, b as Terrain3D, c as ModelInstance3D, d as OmniLight3D, f as Foliage3D, g as FLOWER_VARIETIES, h as resolveFlowerDensity, i as VOXEL_PALETTE, j as QUARTER_PITCH, k as Node3D, l as MeshInstance3D, m as Flowers3D, n as Water3D, o as Tree3D, p as DENSITY_PRESETS, r as WATER_MAX_RIPPLES, s as Particles3D, t as registerNodes3D, u as DirectionalLight3D, v as Camera3D, w as Area3D, x as DEFAULT_TERRAIN_TEXTURE_BASE, y as Billboard3D } from "./register-9mSsSUS1.js";
|
|
3
3
|
import { n as splatWeights, t as buildHeightmap } from "./heightmap-CroQPEER.js";
|
|
4
|
-
import { n as enablePhysics3D, t as Physics3D } from "./physics-3d-
|
|
5
|
-
export { Area3D, AssetStore3D, Camera3D, CharacterBody3D, CharacterController3D, DEFAULT_TERRAIN_TEXTURE_BASE, DirectionalLight3D, Environment3D, DENSITY_PRESETS as FLOWER_DENSITY_PRESETS, FLOWER_VARIETIES, Flowers3D, Foliage3D, MeshInstance3D, ModelInstance3D, Node3D, OmniLight3D, Particles3D, Physics3D, PhysicsBody3D, QUARTER_PITCH, Renderer3D, RigidBody3D, StaticBody3D, TERRAIN_THEMES, Terrain3D, Tree3D, VOXEL_PALETTE, VoxelGrid3D, WATER_MAX_RIPPLES, Water3D, buildHeightmap, cameraRelative, createGame3D, enablePhysics3D, horizonColorFromSky, keyboardIntensity, movementState, parseEnvironment3D, registerNodes3D, resolveFlowerDensity, rigPose, splatWeights, sunDirectionFromElevationAzimuth, sunDirectionFromSky, syncTree, terrainThemeLayers };
|
|
4
|
+
import { n as enablePhysics3D, t as Physics3D } from "./physics-3d-Yvle9Clx.js";
|
|
5
|
+
export { Area3D, AssetStore3D, Billboard3D, Camera3D, CharacterBody3D, CharacterController3D, DEFAULT_TERRAIN_TEXTURE_BASE, DirectionalLight3D, Environment3D, DENSITY_PRESETS as FLOWER_DENSITY_PRESETS, FLOWER_VARIETIES, Flowers3D, Foliage3D, MeshInstance3D, ModelInstance3D, Node3D, OmniLight3D, Particles3D, Physics3D, PhysicsBody3D, QUARTER_PITCH, Renderer3D, RigidBody3D, StaticBody3D, TERRAIN_THEMES, Terrain3D, Tree3D, VOXEL_PALETTE, VoxelGrid3D, WATER_MAX_RIPPLES, Water3D, buildHeightmap, cameraRelative, createGame3D, enablePhysics3D, horizonColorFromSky, keyboardIntensity, movementState, parseEnvironment3D, registerNodes3D, resolveFlowerDensity, rigPose, splatWeights, sunDirectionFromElevationAzimuth, sunDirectionFromSky, syncTree, terrainThemeLayers };
|
|
@@ -6,8 +6,8 @@ import { r as AudioPlayer } from "./register-BuUV1_KB.js";
|
|
|
6
6
|
import { i as resolveRendering, n as attachTouchControls } from "./touch-031PxtCR.js";
|
|
7
7
|
import { n as registerGameplayBehaviors } from "./gameplay-Ccruc3Wd.js";
|
|
8
8
|
import { t as debugSources } from "./debug-draw-CZmOYjL2.js";
|
|
9
|
-
import {
|
|
10
|
-
import { n as enablePhysics3D } from "./physics-3d-
|
|
9
|
+
import { E as PhysicsBody3D, c as ModelInstance3D, k as Node3D, t as registerNodes3D, u as DirectionalLight3D, v as Camera3D } from "./register-9mSsSUS1.js";
|
|
10
|
+
import { n as enablePhysics3D } from "./physics-3d-Yvle9Clx.js";
|
|
11
11
|
import { ACESFilmicToneMapping, AmbientLight, BufferAttribute, BufferGeometry, Color, DepthTexture, EquirectangularReflectionMapping, FloatType, Fog, LineBasicMaterial, LineSegments, Matrix4, Mesh, PCFShadowMap, PMREMGenerator, PerspectiveCamera, PlaneGeometry, Quaternion, Raycaster, Scene, ShaderMaterial, Vector2, Vector3, WebGLRenderTarget, WebGLRenderer } from "three";
|
|
12
12
|
import { VRMLoaderPlugin, VRMUtils } from "@pixiv/three-vrm";
|
|
13
13
|
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
|
package/dist/index.js
CHANGED
|
@@ -57,6 +57,6 @@ function newUid() {
|
|
|
57
57
|
//#endregion
|
|
58
58
|
//#region src/index.ts
|
|
59
59
|
/** Engine version. Kept in sync with package.json by the release pipeline. */
|
|
60
|
-
const VERSION = "0.
|
|
60
|
+
const VERSION = "0.4.0";
|
|
61
61
|
//#endregion
|
|
62
62
|
export { AudioBuses, AudioPlayer, Behavior, CONST_REF_KEY, Engine, IncantoError, InputMap, LogManager, MusicManager, Node, PARTICLE_PRESETS, PARTICLE_PRESET_NAMES, ParticleSim, ROLLOFF_MODELS, Rng, SCENE_FORMAT, SFX_PRESETS, SFX_PRESET_NAMES, Scene, SceneTree, SfxEngine, Signal, Timer, TouchControls, VERSION, WebAudioMusicBackend, applyParticlePreset, assetUrls, attachTouchControls, clearBehaviors, clearRegistry, computeViewport, createNode, createNoise2D, crossfadeGains, duplicateNode, fadeGain, getBehavior, getNodeSchema, getNodeSignals, getNodeType, isAudioContextAvailable, isConstRef, joystickVector, jsonClone, jsonEquals, jsonKind, loadScene, mergeStaticSignals, newUid, parseNodePath, preloadUrls, registerBehavior, registerCoreNodes, registerNode, registeredBehaviors, registeredTypes, resolveConstants, resolveRendering, resolveViewport, serializeNode, spatialGain, spatialPan, synthSfx };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
2
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
3
3
|
import { n as registerDebugSource } from "./debug-draw-CZmOYjL2.js";
|
|
4
|
-
import {
|
|
4
|
+
import { A as validateCollider3D, D as RigidBody3D, E as PhysicsBody3D, T as CharacterBody3D, b as Terrain3D, k as Node3D, w as Area3D } from "./register-9mSsSUS1.js";
|
|
5
5
|
import { Euler, Quaternion } from "three";
|
|
6
6
|
//#region src/3d/physics/physics-3d.ts
|
|
7
7
|
var physics_3d_exports = /* @__PURE__ */ __exportAll({
|
package/dist/react.js
CHANGED
|
@@ -156,7 +156,7 @@ function IncantoCanvas(props) {
|
|
|
156
156
|
pointer: latest.pointer,
|
|
157
157
|
...keyboard !== void 0 ? { keyboard } : {}
|
|
158
158
|
};
|
|
159
|
-
const next = await (_gameFactory ?? (mode === "3d" ? async (o) => (await import("./create-game-
|
|
159
|
+
const next = await (_gameFactory ?? (mode === "3d" ? async (o) => (await import("./create-game-DlTZUCSj.js").then((n) => n.n)).createGame3D(o) : async (o) => (await import("./create-game-CZHROKcT.js").then((n) => n.n)).createGame2D(o)))(opts);
|
|
160
160
|
if (disposed) {
|
|
161
161
|
next.dispose();
|
|
162
162
|
return;
|
|
@@ -1339,9 +1339,9 @@ function blobTexture() {
|
|
|
1339
1339
|
_blobTex = new CanvasTexture(c);
|
|
1340
1340
|
return _blobTex;
|
|
1341
1341
|
}
|
|
1342
|
-
const Z_AXIS = new Vector3(0, 0, 1);
|
|
1342
|
+
const Z_AXIS$1 = new Vector3(0, 0, 1);
|
|
1343
1343
|
const _spritePos = new Vector3();
|
|
1344
|
-
const _dir = new Vector3();
|
|
1344
|
+
const _dir$1 = new Vector3();
|
|
1345
1345
|
const _target = new Quaternion();
|
|
1346
1346
|
const _parent = new Quaternion();
|
|
1347
1347
|
const _sunLocal = new Vector3();
|
|
@@ -1550,22 +1550,22 @@ var Sprite3D = class extends Node3D {
|
|
|
1550
1550
|
/** @internal Aim the quad's +Z at a world point, honoring billboard mode. */
|
|
1551
1551
|
_faceToward(quad, targetWorld) {
|
|
1552
1552
|
quad.getWorldPosition(_spritePos);
|
|
1553
|
-
_dir.copy(targetWorld).sub(_spritePos);
|
|
1554
|
-
if (this.billboard === "y") _dir.y = 0;
|
|
1555
|
-
if (_dir.lengthSq() < 1e-12) return;
|
|
1556
|
-
_dir.normalize();
|
|
1557
|
-
_target.setFromUnitVectors(Z_AXIS, _dir);
|
|
1553
|
+
_dir$1.copy(targetWorld).sub(_spritePos);
|
|
1554
|
+
if (this.billboard === "y") _dir$1.y = 0;
|
|
1555
|
+
if (_dir$1.lengthSq() < 1e-12) return;
|
|
1556
|
+
_dir$1.normalize();
|
|
1557
|
+
_target.setFromUnitVectors(Z_AXIS$1, _dir$1);
|
|
1558
1558
|
quad.parent?.getWorldQuaternion(_parent);
|
|
1559
1559
|
quad.quaternion.copy(_parent.invert()).multiply(_target);
|
|
1560
1560
|
}
|
|
1561
1561
|
/** @internal Aim a mesh's +Z along a world DIRECTION (vs a point), honoring
|
|
1562
1562
|
* billboard mode — keeps the silhouette shadow caster broad to the light. */
|
|
1563
1563
|
_faceDir(mesh, dir) {
|
|
1564
|
-
_dir.copy(dir);
|
|
1565
|
-
if (this.billboard === "y") _dir.y = 0;
|
|
1566
|
-
if (_dir.lengthSq() < 1e-12) return;
|
|
1567
|
-
_dir.normalize();
|
|
1568
|
-
_target.setFromUnitVectors(Z_AXIS, _dir);
|
|
1564
|
+
_dir$1.copy(dir);
|
|
1565
|
+
if (this.billboard === "y") _dir$1.y = 0;
|
|
1566
|
+
if (_dir$1.lengthSq() < 1e-12) return;
|
|
1567
|
+
_dir$1.normalize();
|
|
1568
|
+
_target.setFromUnitVectors(Z_AXIS$1, _dir$1);
|
|
1569
1569
|
mesh.parent?.getWorldQuaternion(_parent);
|
|
1570
1570
|
mesh.quaternion.copy(_parent.invert()).multiply(_target);
|
|
1571
1571
|
}
|
|
@@ -1694,6 +1694,87 @@ function resolveFrames(frames, name) {
|
|
|
1694
1694
|
return [...frames];
|
|
1695
1695
|
}
|
|
1696
1696
|
//#endregion
|
|
1697
|
+
//#region src/3d/nodes/billboard-3d.ts
|
|
1698
|
+
const _parentQ = new Quaternion();
|
|
1699
|
+
const _rotQ = new Quaternion();
|
|
1700
|
+
const _pos = new Vector3();
|
|
1701
|
+
const _camPos = new Vector3();
|
|
1702
|
+
const _dir = new Vector3();
|
|
1703
|
+
const Z_AXIS = new Vector3(0, 0, 1);
|
|
1704
|
+
/** Valid `Billboard3D.mode` values — drives the prop options AND validateJson. */
|
|
1705
|
+
const BILLBOARD_GROUP_MODES = [
|
|
1706
|
+
"screen",
|
|
1707
|
+
"y",
|
|
1708
|
+
"none"
|
|
1709
|
+
];
|
|
1710
|
+
/**
|
|
1711
|
+
* Billboard3D — a transform GROUP that orients itself toward the camera every
|
|
1712
|
+
* rendered frame; children inherit the rotation through the scene graph. Hang
|
|
1713
|
+
* world-space UI off a character — HP bars, markers, icons — without
|
|
1714
|
+
* hand-rolling camera math in game code.
|
|
1715
|
+
*
|
|
1716
|
+
* - `mode: "screen"` (default) copies the camera's orientation wholesale, so a
|
|
1717
|
+
* child rectangle projects as an upright rectangle ANYWHERE on screen. This is
|
|
1718
|
+
* what flat UI wants — a yaw-only look-at visibly tilts off-center shapes when
|
|
1719
|
+
* the camera pitches. (Deliberately NOT the same as Sprite3D's `"full"`, which
|
|
1720
|
+
* faces the camera POSITION and keeps that perspective tilt.)
|
|
1721
|
+
* - `mode: "y"` yaws the group's +Z upright toward the camera (like Sprite3D's
|
|
1722
|
+
* `"y"`) — for standing content that must not pitch back as the camera rises.
|
|
1723
|
+
* - `mode: "none"` billboarding off — the authored transform applies again on
|
|
1724
|
+
* the next frame (`_syncObject3D` re-stamps it), so toggling is lossless.
|
|
1725
|
+
*
|
|
1726
|
+
* The node's own `rotation` prop is overwritten while billboarding ("none"
|
|
1727
|
+
* restores it — also handy in the editor to pose children with the gizmo).
|
|
1728
|
+
* The group keeps orienting while `visible: false` ON PURPOSE: a hidden HP bar
|
|
1729
|
+
* revealed later is already correct — no one-frame orientation pop. Headless
|
|
1730
|
+
* runs never invoke the render hook — the group keeps its authored transform.
|
|
1731
|
+
*/
|
|
1732
|
+
var Billboard3D = class extends Node3D {
|
|
1733
|
+
static typeName = "Billboard3D";
|
|
1734
|
+
static props = { mode: {
|
|
1735
|
+
default: "screen",
|
|
1736
|
+
options: BILLBOARD_GROUP_MODES
|
|
1737
|
+
} };
|
|
1738
|
+
/** `"screen"` copy the camera orientation (screen-aligned) · `"y"` upright
|
|
1739
|
+
* yaw-to-camera · `"none"` billboarding off (authored rotation applies). */
|
|
1740
|
+
mode = "screen";
|
|
1741
|
+
/** Last successfully billboarded WORLD orientation — reapplied on the frames
|
|
1742
|
+
* where `"y"` has no answer (camera dead overhead), because `_syncObject3D`
|
|
1743
|
+
* re-stamps the authored rotation every frame and a bare early-return would
|
|
1744
|
+
* visibly snap the group to it. Lazily created; `null` until first success. */
|
|
1745
|
+
_lastDesired = null;
|
|
1746
|
+
/** Scene-load validation: a typo'd mode must fail HARD, not silently behave
|
|
1747
|
+
* like one of the real modes (agents self-correct on load errors). */
|
|
1748
|
+
static validateJson(node) {
|
|
1749
|
+
const b = node;
|
|
1750
|
+
if (!BILLBOARD_GROUP_MODES.includes(b.mode)) throw new IncantoError("BAD_FORMAT", `Billboard3D '${node.name}' mode must be one of [${BILLBOARD_GROUP_MODES.join(", ")}], got '${b.mode}'.`, {
|
|
1751
|
+
prop: "mode",
|
|
1752
|
+
validOptions: BILLBOARD_GROUP_MODES
|
|
1753
|
+
});
|
|
1754
|
+
}
|
|
1755
|
+
/** @internal Orient the BACKING object each rendered frame — unlike Sprite3D
|
|
1756
|
+
* (which spins only its inner quad) the whole group turns, so children follow. */
|
|
1757
|
+
_onRender3D(ctx) {
|
|
1758
|
+
if (this.mode === "none") return;
|
|
1759
|
+
const o = this._ensureObject3D();
|
|
1760
|
+
if (this.mode === "screen") ctx.camera.getWorldQuaternion(_rotQ);
|
|
1761
|
+
else {
|
|
1762
|
+
o.getWorldPosition(_pos);
|
|
1763
|
+
ctx.camera.getWorldPosition(_camPos);
|
|
1764
|
+
_dir.copy(_camPos).sub(_pos);
|
|
1765
|
+
_dir.y = 0;
|
|
1766
|
+
if (_dir.lengthSq() >= 1e-12) _rotQ.setFromUnitVectors(Z_AXIS, _dir.normalize());
|
|
1767
|
+
else if (this._lastDesired) _rotQ.copy(this._lastDesired);
|
|
1768
|
+
else return;
|
|
1769
|
+
}
|
|
1770
|
+
if (!this._lastDesired) this._lastDesired = new Quaternion();
|
|
1771
|
+
this._lastDesired.copy(_rotQ);
|
|
1772
|
+
o.parent?.getWorldQuaternion(_parentQ);
|
|
1773
|
+
if (o.parent) o.quaternion.copy(_parentQ.invert()).multiply(_rotQ);
|
|
1774
|
+
else o.quaternion.copy(_rotQ);
|
|
1775
|
+
}
|
|
1776
|
+
};
|
|
1777
|
+
//#endregion
|
|
1697
1778
|
//#region src/3d/nodes/camera-3d.ts
|
|
1698
1779
|
/**
|
|
1699
1780
|
* A perspective camera. Mark exactly one camera `current: true`; with none
|
|
@@ -3981,7 +4062,11 @@ const BEND_AIR_GAP = .5;
|
|
|
3981
4062
|
* (`colorA`→`colorB`, clump-coherent, dry patches) and the ported sin·cos
|
|
3982
4063
|
* tuft sway. Dozens of overlapping textured fans read as a DENSE meadow
|
|
3983
4064
|
* (잔디밭) at a fraction of the instances — the look that per-blade
|
|
3984
|
-
* geometry never reaches at sane budgets.
|
|
4065
|
+
* geometry never reaches at sane budgets. NOTE: tuft cards are big — drawn
|
|
4066
|
+
* ~1.5× the `height` prop and wider than tall — and opaque (depth-writing),
|
|
4067
|
+
* so over a field of SMALL gameplay sprites (mobs, items) they legitimately
|
|
4068
|
+
* occlude actors shorter than the grass. For such fields prefer `'blades'`
|
|
4069
|
+
* (thin) or keep `height` low so actors read above the grass line.
|
|
3985
4070
|
*
|
|
3986
4071
|
* - `'mesh'` (default, kind 'grass' only): the per-blade path — ONE
|
|
3987
4072
|
* InstancedMesh where every instance is a REAL tapered blade (7-vertex
|
|
@@ -4062,7 +4147,9 @@ var Foliage3D = class Foliage3D extends Node3D {
|
|
|
4062
4147
|
/** Instance cap (hard ceiling 200_000). */
|
|
4063
4148
|
maxInstances = 5e4;
|
|
4064
4149
|
/** Base blade height in meters — mesh style additionally scales it 0.55–1.7×
|
|
4065
|
-
* regionally (low-frequency noise: tall patches and short worn patches)
|
|
4150
|
+
* regionally (low-frequency noise: tall patches and short worn patches), and
|
|
4151
|
+
* `tufts` cards render ~1.5× this value (so 0.4 ≈ 0.6 m tall). Keep it low under
|
|
4152
|
+
* small gameplay sprites or they hide behind the grass — see the `style` doc. */
|
|
4066
4153
|
height = .25;
|
|
4067
4154
|
/** colorA = bottom/lerp-start, colorB = top/lerp-end. Refinement pass 3:
|
|
4068
4155
|
* lifted toward the reference's warm sunlit green (the old olives read
|
|
@@ -4797,6 +4884,8 @@ const MATERIAL_KEYS = [
|
|
|
4797
4884
|
"wireframe",
|
|
4798
4885
|
"flatShading",
|
|
4799
4886
|
"opacity",
|
|
4887
|
+
"depthTest",
|
|
4888
|
+
"depthWrite",
|
|
4800
4889
|
"map",
|
|
4801
4890
|
"normalMap",
|
|
4802
4891
|
"repeat"
|
|
@@ -4807,10 +4896,12 @@ const MATERIAL_KEYS = [
|
|
|
4807
4896
|
* - `size [x,y,z]`: box extents; sphere/gem radius = x; capsule radius = x,
|
|
4808
4897
|
* height = y; plane = x·z ground plane (laid flat); cylinder radius = x, height = y
|
|
4809
4898
|
* - `material`: `{color, metalness, roughness, wireframe, flatShading, opacity,
|
|
4810
|
-
* emissive, emissiveIntensity, map, normalMap, repeat}` —
|
|
4811
|
-
* texture URLs loaded lazily (headless-safe: geometry and
|
|
4812
|
-
* wait on them), tiled by `repeat: [u, v]`. `gem` + `flatShading:
|
|
4813
|
-
* `roughness` reads as a sparkling faceted jewel (spin it for the
|
|
4899
|
+
* depthTest, depthWrite, emissive, emissiveIntensity, map, normalMap, repeat}` —
|
|
4900
|
+
* `map`/`normalMap` are texture URLs loaded lazily (headless-safe: geometry and
|
|
4901
|
+
* physics never wait on them), tiled by `repeat: [u, v]`. `gem` + `flatShading:
|
|
4902
|
+
* true` + a low `roughness` reads as a sparkling faceted jewel (spin it for the
|
|
4903
|
+
* sparkle). `depthTest: false` makes a flat `plane`/`cylinder` an always-on-top
|
|
4904
|
+
* ground decal (AoE telegraph, selection ring) — no z-fighting with terrain.
|
|
4814
4905
|
*/
|
|
4815
4906
|
var MeshInstance3D = class extends Node3D {
|
|
4816
4907
|
static typeName = "MeshInstance3D";
|
|
@@ -4873,6 +4964,8 @@ var MeshInstance3D = class extends Node3D {
|
|
|
4873
4964
|
const opacity = this.material.opacity ?? 1;
|
|
4874
4965
|
mat.opacity = opacity;
|
|
4875
4966
|
mat.transparent = opacity < 1;
|
|
4967
|
+
mat.depthTest = this.material.depthTest ?? true;
|
|
4968
|
+
mat.depthWrite = this.material.depthWrite ?? true;
|
|
4876
4969
|
const flat = this.material.flatShading ?? false;
|
|
4877
4970
|
if (mat.flatShading !== flat) {
|
|
4878
4971
|
mat.flatShading = flat;
|
|
@@ -4948,7 +5041,12 @@ function validateMaterial(material, name) {
|
|
|
4948
5041
|
const value = material[key];
|
|
4949
5042
|
if (value !== void 0 && !(typeof value === "number" && value >= 0 && value <= 1)) fail(`material.${key} must be a number in 0..1, got ${JSON.stringify(value)}.`);
|
|
4950
5043
|
}
|
|
4951
|
-
for (const key of [
|
|
5044
|
+
for (const key of [
|
|
5045
|
+
"wireframe",
|
|
5046
|
+
"flatShading",
|
|
5047
|
+
"depthTest",
|
|
5048
|
+
"depthWrite"
|
|
5049
|
+
]) {
|
|
4952
5050
|
const value = material[key];
|
|
4953
5051
|
if (value !== void 0 && typeof value !== "boolean") fail(`material.${key} must be a boolean, got ${JSON.stringify(value)}.`);
|
|
4954
5052
|
}
|
|
@@ -10993,6 +11091,7 @@ function registerNodes3D() {
|
|
|
10993
11091
|
registerNode(MeshInstance3D);
|
|
10994
11092
|
registerNode(Sprite3D);
|
|
10995
11093
|
registerNode(AnimatedSprite3D);
|
|
11094
|
+
registerNode(Billboard3D);
|
|
10996
11095
|
registerNode(CharacterController3D);
|
|
10997
11096
|
registerNode(VoxelGrid3D);
|
|
10998
11097
|
registerNode(Terrain3D);
|
|
@@ -11010,4 +11109,4 @@ function registerNodes3D() {
|
|
|
11010
11109
|
registerNode(CharacterBody3D);
|
|
11011
11110
|
}
|
|
11012
11111
|
//#endregion
|
|
11013
|
-
export {
|
|
11112
|
+
export { validateCollider3D as A, terrainThemeLayers as C, RigidBody3D as D, PhysicsBody3D as E, rigPose as F, cameraRelative as M, keyboardIntensity as N, StaticBody3D as O, movementState as P, TERRAIN_THEMES as S, CharacterBody3D as T, CharacterController3D as _, VoxelGrid3D as a, Terrain3D as b, ModelInstance3D as c, OmniLight3D as d, Foliage3D as f, FLOWER_VARIETIES as g, resolveFlowerDensity as h, VOXEL_PALETTE as i, QUARTER_PITCH as j, Node3D as k, MeshInstance3D as l, Flowers3D as m, Water3D as n, Tree3D as o, DENSITY_PRESETS as p, WATER_MAX_RIPPLES as r, Particles3D as s, registerNodes3D as t, DirectionalLight3D as u, Camera3D as v, Area3D as w, DEFAULT_TERRAIN_TEXTURE_BASE as x, Billboard3D as y };
|
package/dist/test.js
CHANGED
|
@@ -5,7 +5,7 @@ import { n as jsonEquals, t as jsonClone } from "./json-BLk7H2Qa.js";
|
|
|
5
5
|
import { i as getNodeSchema, s as mergeStaticProps } from "./registry-BVJ2HbCn.js";
|
|
6
6
|
import { n as registerGameplayBehaviors } from "./gameplay-Ccruc3Wd.js";
|
|
7
7
|
import { t as registerNodes2D } from "./register-DPEV9_9t.js";
|
|
8
|
-
import { t as registerNodes3D } from "./register-
|
|
8
|
+
import { t as registerNodes3D } from "./register-9mSsSUS1.js";
|
|
9
9
|
import { t as registerNodesNet } from "./register-Dasmnurl.js";
|
|
10
10
|
//#region src/test/index.ts
|
|
11
11
|
/**
|
|
@@ -132,7 +132,7 @@ async function runScript(json, opts) {
|
|
|
132
132
|
const { enablePhysics2D } = await import("./physics-2d-KuMWPTf6.js").then((n) => n.r);
|
|
133
133
|
await enablePhysics2D(engine);
|
|
134
134
|
} else if (physics === "3d" || physics === "auto" && scene.dimension === "3d") {
|
|
135
|
-
const { enablePhysics3D } = await import("./physics-3d-
|
|
135
|
+
const { enablePhysics3D } = await import("./physics-3d-Yvle9Clx.js").then((n) => n.r);
|
|
136
136
|
await enablePhysics3D(engine);
|
|
137
137
|
}
|
|
138
138
|
const failures = [];
|
|
@@ -237,7 +237,7 @@ async function createPlaySession(json, opts = {}) {
|
|
|
237
237
|
const { enablePhysics2D } = await import("./physics-2d-KuMWPTf6.js").then((n) => n.r);
|
|
238
238
|
await enablePhysics2D(engine);
|
|
239
239
|
} else if (physics === "3d" || physics === "auto" && scene.dimension === "3d") {
|
|
240
|
-
const { enablePhysics3D } = await import("./physics-3d-
|
|
240
|
+
const { enablePhysics3D } = await import("./physics-3d-Yvle9Clx.js").then((n) => n.r);
|
|
241
241
|
await enablePhysics3D(engine);
|
|
242
242
|
}
|
|
243
243
|
const stepMs = 1e3 / (opts.fixedHz ?? 60);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{t as e}from"./index-
|
|
1
|
+
import{t as e}from"./index-BN0hHg3U.js";async function t(t){return new n((await e(()=>import(`./GameServer-C56iOUgF.js`),[],import.meta.url)).GameServer,t)}var n=class{raw;active=new Map;constructor(e,t){this.raw=new e({...t})}get account(){return this.raw.account}get connected(){return this.raw.connected}connect(){return this.rawConnect()}rawConnect(){return this.raw.connect({onDisconnect:()=>void this.reconnect()})}disconnect(){for(let e of this.active.values())e.off();return this.active.clear(),this.raw.disconnect()}remoteFunction(e,t,n){return this.raw.remoteFunction(e,t,n)}track(e){let t=Symbol(`sub`),n={make:e,off:e()};return this.active.set(t,n),()=>{n.off(),this.active.delete(t)}}async reconnect(){await this.rawConnect();for(let e of this.active.values())e.off(),e.off=e.make()}subscribeRoomState(e,t){return this.track(()=>this.raw.subscribeRoomState(e,t))}subscribeRoomMyState(e,t){return this.track(()=>this.raw.subscribeRoomMyState(e,t))}subscribeRoomAllUserStates(e,t){return this.track(()=>this.raw.subscribeRoomAllUserStates(e,e=>{let n={};for(let t of e??[]){if(!t||typeof t.account!=`string`||t.__leaved)continue;let{account:e,__updated:r,__leaved:i,...a}=t;n[e]=a}t(n)}))}subscribeRoomCollection(e,t,n){return this.track(()=>this.raw.subscribeRoomCollection(e,t,({items:e})=>{let t={};for(let n of e??[])n&&typeof n.__id==`string`&&(t[n.__id]=n);n(t)}))}onRoomMessage(e,t,n){return this.track(()=>this.raw.onRoomMessage(e,t,n))}onRoomUserJoin(e,t){return this.track(()=>this.raw.onRoomUserJoin(e,t))}onRoomUserLeave(e,t){return this.track(()=>this.raw.onRoomUserLeave(e,t))}subscribeGlobalState(e){return this.track(()=>this.raw.subscribeGlobalState(e))}subscribeGlobalMyState(e){return this.track(()=>this.raw.subscribeGlobalMyState(e))}subscribeGlobalUserState(e,t){return this.track(()=>this.raw.subscribeGlobalUserState(e,t))}subscribeGlobalCollection(e,t){return this.track(()=>this.raw.subscribeGlobalCollection(e,({items:e})=>{let n={};for(let t of e??[])t&&typeof t.__id==`string`&&(n[t.__id]=t);t(n)}))}subscribeAsset(e,t){return this.track(()=>this.raw.subscribeAsset(e,t))}onGlobalMessage(e,t){return this.track(()=>this.raw.onGlobalMessage(e,t))}};export{t as createAgent8Server};
|