@tresjs/cientos
Version:
Collection of useful helpers and fully functional, ready-made abstractions for Tres
1,627 lines (1,556 loc) • 375 kB
JavaScript
/**
* name: @tresjs/cientos
* version: v5.7.1
* (c) 2026
* description: Collection of useful helpers and fully functional, ready-made abstractions for Tres
* author: Alvaro Saburido <hola@alvarosaburido.dev> (https://github.com/alvarosabu/)
*/
import { Fragment, Suspense, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, inject, isReactive, isRef, mergeDefaults, mergeProps, nextTick, normalizeProps, onBeforeUnmount, onMounted, onUnmounted, openBlock, provide, reactive, ref, render, renderList, renderSlot, shallowReactive, shallowRef, toRaw, toRefs, toValue, triggerRef, unref, useAttrs, useSlots, watch, watchEffect, withAsyncContext, withCtx } from "vue";
import { buildGraph, createTimer, extend, isObject3D, logError, logWarning, normalizeColor, normalizeVectorFlexibleParam, useLoader, useLoop, useTres, useTresContext } from "@tresjs/core";
import * as THREE from "three";
import { AdditiveBlending, AlwaysStencilFunc, AnimationMixer, Audio, AudioListener, AudioLoader, BackSide, Box2, Box3, BoxGeometry, BufferAttribute, BufferGeometry, Camera, CatmullRomCurve3, ClampToEdgeWrapping, Color, CubeCamera, CubeReflectionMapping, CubeTextureLoader, CubicBezierCurve3, DataTexture, DefaultLoadingManager, DepthTexture, DirectionalLight, DoubleSide, EdgesGeometry, EqualStencilFunc, EquirectangularReflectionMapping, Euler, FloatType, FramebufferTexture, FrontSide, Group, HalfFloatType, IcosahedronGeometry, InstancedMesh, InterleavedBuffer, InterleavedBufferAttribute, KeepStencilOp, LOD, LinearFilter, MOUSE, MathUtils, Matrix4, Mesh, MeshBasicMaterial, MeshDepthMaterial, MeshLambertMaterial, MeshStandardMaterial, NearestFilter, NoBlending, NotEqualStencilFunc, Object3D, OrthographicCamera, PerspectiveCamera, Plane, PlaneGeometry, Points, PointsMaterial, QuadraticBezierCurve3, Quaternion, REVISION, RGBAFormat, RawShaderMaterial, Raycaster, RepeatWrapping, ReplaceStencilOp, Scene, ShaderChunk, ShaderMaterial, ShapeGeometry, SkinnedMesh, Sphere, Spherical, TOUCH, TangentSpaceNormalMap, Texture, TextureLoader, UVMapping, Uniform, UniformsUtils, UnsignedByteType, Vector2, Vector3, Vector4, VideoTexture, WebGLCubeRenderTarget, WebGLRenderTarget, WebGLRenderer } from "three";
import { tryOnScopeDispose, useDebounceFn, useElementSize, useEventListener, useMagicKeys, useMouse, useScroll, useWindowScroll, useWindowSize, watchThrottled, whenever } from "@vueuse/core";
import { DRACOLoader, FBXLoader, FontLoader, GLTFExporter, GLTFLoader, HorizontalBlurShader, Line2, LineGeometry, LineMaterial, MapControls, MarchingCubes, MeshSurfaceSampler, OrbitControls, PointerLockControls, PositionalAudioHelper, RGBELoader, Reflector, RoundedBoxGeometry, SVGLoader, SimplexNoise, Sky, TextGeometry, TransformControls, VerticalBlurShader, Water, toCreasedNormals } from "three-stdlib";
import BaseCameraControls, { default as CameraControls } from "camera-controls";
import CustomShaderMaterial from "three-custom-shader-material/vanilla";
import StatsImpl from "stats.js";
import StatsGlImpl from "stats-gl";
import { BVHHelper, MeshBVH, acceleratedRaycast } from "three-mesh-bvh";
//#region src/core/abstractions/AnimatedSprite/AtlasAnimationDefinitionParser.ts
/**
* Expand an animation definition string into an array of numbers.
* @param definitionStr - A comma-separated string of frame numbers with optional parentheses-surrounded durations.
* @example - expand("0,2") === [0,2]
* @example - expand("2(10)") === [2,2,2,2,2,2,2,2,2,2]
* @example - expand("1-4") === [1,2,3,4]
* @example - expand("10-5(2)") === [10,10,9,9,8,8,7,7,6,6,5,5]
* @example - expand("1-4(3),10(2)") === [1,1,1,2,2,2,3,3,3,4,4,4,10,10]
*/
function expand(definitionStr) {
const parsed = parse(definitionStr);
const result = [];
for (const { startFrame, endFrame, duration } of parsed) if (duration <= 0) continue;
else if (endFrame < 0 || startFrame === endFrame) {
for (let _ = 0; _ < duration; _++) result.push(startFrame);
continue;
} else {
const sign = Math.sign(endFrame - startFrame);
for (let frame = startFrame; frame !== endFrame + sign; frame += sign) for (let _ = 0; _ < duration; _++) result.push(frame);
}
return result;
}
/**
* Parse an animation defintion string into an array of AnimationDefinition.
* @param definitionStr - A comma-separated string of frame numbers with optional parentheses-surrounded durations.
* @example - parse("0,2") === [{startFrame:0, endFrame:0, duration:1}, {startFrame:2, endFrame:2, duration:1}]
* @example - parse("2(10)") === [{startFrame:2, endFrame:2, duration:10}]
* @example - parse("1-4") === [{startFrame:1, endFrame:4, duration:1}]
* @example - parse("10-5(2)") === [{startFrame:10, endFrame:5, duration:2}]
* @example - parse("1-4(3),10(2)") === [{startFrame:1, endFrame:4, duration:3}, {startFrame:10, endFrame:10, duration:2}]
*/
function parse(definitionStr) {
let transition = "START_FRAME_IN";
const result = [];
for (const { name, value, startI } of tokenize(definitionStr)) if (transition === "START_FRAME_IN") if (name === "NUMBER") {
result.push({
startFrame: value,
endFrame: value,
duration: 1
});
transition = "START_FRAME_OUT";
} else logDefinitionSyntaxError("number", name, definitionStr, startI);
else if (transition === "START_FRAME_OUT") if (name === "COMMA") transition = "START_FRAME_IN";
else if (name === "HYPHEN") transition = "END_FRAME_IN";
else if (name === "OPEN_PAREN") transition = "DURATION_IN";
else logDefinitionSyntaxError("\",\", \"-\", \"(\"", name, definitionStr, startI);
else if (transition === "END_FRAME_IN") if (name === "NUMBER") {
result[result.length - 1].endFrame = value;
transition = "END_FRAME_OUT";
} else logDefinitionSyntaxError("number", name, definitionStr, startI);
else if (transition === "END_FRAME_OUT") if (name === "COMMA") transition = "START_FRAME_IN";
else if (name === "OPEN_PAREN") transition = "DURATION_IN";
else logDefinitionSyntaxError("',' or '('", name, definitionStr, startI);
else if (transition === "DURATION_IN") if (name === "NUMBER") {
result[result.length - 1].duration = value;
transition = "DURATION_OUT";
} else logDefinitionSyntaxError("number", name, definitionStr, startI);
else if (transition === "DURATION_OUT") if (name === "CLOSE_PAREN") transition = "NEXT_OR_DONE";
else logDefinitionSyntaxError("\"(\"", name, definitionStr, startI);
else if (transition === "NEXT_OR_DONE") if (name === "COMMA") transition = "START_FRAME_IN";
else logDefinitionSyntaxError("\",\"", name, definitionStr, startI);
return result;
}
function tokenize(definition) {
const result = [];
for (let ii = 0; ii < definition.length; ii++) {
const c = definition[ii];
if ("0123456789".includes(c)) if (result.length && result[result.length - 1].name === "NUMBER") {
result[result.length - 1].value *= 10;
result[result.length - 1].value += Number.parseInt(c);
} else result.push({
name: "NUMBER",
value: Number.parseInt(c),
startI: ii
});
else if (c === " ") continue;
else if (c === ",") result.push({
name: "COMMA",
value: -1,
startI: ii
});
else if (c === "(") result.push({
name: "OPEN_PAREN",
value: -1,
startI: ii
});
else if (c === ")") result.push({
name: "CLOSE_PAREN",
value: -1,
startI: ii
});
else if (c === "-") result.push({
name: "HYPHEN",
value: -1,
startI: ii
});
else logDefinitionBadCharacter("0123456789,-()", c, definition, ii);
}
return result;
}
function logDefinitionBadCharacter(expected, found, definition, index) {
logError(`Cientos AnimationDefinitionParser: Unexpected character while processing animation definition: expected ${expected}, got ${found}.
${definition}
${Array.from({ length: index + 1 }).join(" ")}^`);
}
function logDefinitionSyntaxError(expected, found, definition, index) {
logError(`Cientos AnimationDefinitionParser: Syntax error while processing animation definition: expected ${expected}, got ${found}.
${definition}
${Array.from({ length: index + 1 }).join(" ")}^`);
}
//#endregion
//#region src/core/abstractions/AnimatedSprite/StringOps.ts
const numbersAtEnd = /\d*$/;
const underscoresNumbersAtEnd = /_*\d*$/;
function stripUnderscoresNumbersFromEnd(str) {
return str.replace(underscoresNumbersAtEnd, "");
}
function getNumbersFromEnd(str) {
const matches = str.match(numbersAtEnd);
if (matches) return Number.parseInt(matches[matches.length - 1]);
return null;
}
//#endregion
//#region src/core/abstractions/AnimatedSprite/Atlas.ts
async function getTextureAndAtlasAsync(imagePathOrImageData, atlasPathOrAtlasish) {
const loader = new TextureLoader();
const texturePromise = new Promise((resolve, reject) => {
loader.load(imagePathOrImageData, resolve, void 0, reject);
});
const atlasishPromise = typeof atlasPathOrAtlasish !== "string" ? new Promise((resolve) => resolve(atlasPathOrAtlasish)) : fetch(atlasPathOrAtlasish).then((response) => response.json()).catch((e) => logError(`Cientos Atlas - ${e}`));
return Promise.all([texturePromise, atlasishPromise]).then(([texture$1, atlasish]) => {
return [texture$1, getAtlas(atlasish, texture$1.image.width, texture$1.image.height)];
});
}
function getAtlas(atlasish, textureWidth, textureHeight) {
const frames = typeof atlasish === "number" || Array.isArray(atlasish) ? getAtlasFramesFromNumColsNumRows(atlasish, textureWidth, textureHeight) : getAtlasFramesFromTexturePackerData(atlasish, textureWidth, textureHeight);
return {
frames,
animations: groupAtlasFramesByKey(frames)
};
}
function getAtlasFrames(atlas, animationNameOrFrameNumber, reversed) {
let frames;
if (typeof animationNameOrFrameNumber === "string") frames = getAtlasFramesByAnimationName(atlas, animationNameOrFrameNumber);
else if (typeof animationNameOrFrameNumber === "number") frames = getAtlasFramesByIndices(atlas, animationNameOrFrameNumber, animationNameOrFrameNumber);
else frames = getAtlasFramesByIndices(atlas, animationNameOrFrameNumber[0], animationNameOrFrameNumber[1]);
return reversed ? frames.toReversed() : frames;
}
function getNullAtlasFrame() {
return {
name: "null",
width: 0,
height: 0,
offsetX: 0,
offsetY: 0,
repeatX: 0,
repeatY: 0
};
}
function getAtlasFramesFromTexturePackerData(data, width, height) {
return Array.isArray(data.frames) ? getAtlasFramesFromTexturePackerDataArray(data, width, height) : getAtlasFramesFromTexturePackerDataObject(data, width, height);
}
function getAtlasFramesFromTexturePackerDataArray(data, width, height) {
const invWidth = 1 / width;
const invHeight = 1 / height;
return data.frames.map((d) => ({
name: d.filename,
offsetX: d.frame.x * invWidth,
offsetY: 1 - (d.frame.y + d.frame.h) * invHeight,
repeatX: d.frame.w * invWidth,
repeatY: d.frame.h * invHeight,
width: d.frame.w,
height: d.frame.h
}));
}
function getAtlasFramesFromTexturePackerDataObject(data, width, height) {
const invWidth = 1 / width;
const invHeight = 1 / height;
return Object.entries(data.frames).map(([k, v]) => ({
name: k,
offsetX: v.frame.x * invWidth,
offsetY: 1 - (v.frame.y + v.frame.h) * invHeight,
repeatX: v.frame.w * invWidth,
repeatY: v.frame.h * invHeight,
width: v.frame.w,
height: v.frame.h
}));
}
function getAtlasFramesFromNumColsNumRows(numColsOrNumColsNumRows, width, height, name = "default") {
const [numCols, numRows] = Array.isArray(numColsOrNumColsNumRows) ? numColsOrNumColsNumRows : [numColsOrNumColsNumRows, 1];
const frameWidth = width / numCols;
const frameHeight = height / numRows;
const padAmount = (numCols * numRows).toString().length;
const repeatX = 1 / numCols;
const repeatY = 1 / numRows;
const result = [];
let i = 0;
for (let row = numRows - 1; row >= 0; row--) for (let col = 0; col < numCols; col++) {
i++;
result.push({
name: name + String(i).padStart(padAmount, "0"),
offsetX: col * repeatX,
offsetY: row * repeatY,
repeatX,
repeatY,
width: frameWidth,
height: frameHeight
});
}
return result;
}
function setAtlasDefinitions(atlas, definitions = {}) {
const animations = groupAtlasFramesByKey(atlas.frames);
for (const [animationName, definitionStr] of Object.entries(definitions)) {
const frames = getAtlasFrames(atlas, animationName, false);
const expandedFrameIndices = expand(definitionStr);
for (const frameIndex of expandedFrameIndices) if (frameIndex < 0 || frames.length <= frameIndex) logError(`Cientos Atlas: Attempting to access frame index ${frameIndex} in animation ${animationName}, but it does not exist.`);
animations[animationName] = expandedFrameIndices.map((frameIndex) => frames[frameIndex]);
}
atlas.animations = animations;
}
function getAtlasFramesByAnimationName(atlas, name) {
if (!(name in atlas.animations)) {
logError(`Cientos Atlas: getAtlasFramesByAnimationName
The animation name "${name}" does not exist in this atlas.
Available names:
${Object.keys(atlas.animations).map((n) => `* ${n}\n`).join("")}`);
return [getNullAtlasFrame()];
}
return atlas.animations[name];
}
function getAtlasFramesByIndices(atlas, startI, endI) {
if (startI < 0 || atlas.frames.length <= startI || endI < 0 || atlas.frames.length <= endI) {
logError(`Cientos Atlas: getFramesByIndex – [${startI}, ${endI}] is out of bounds.`);
return [getNullAtlasFrame()];
}
const result = [];
const sign = Math.sign(endI - startI);
if (sign === 0) return [atlas.frames[startI]];
for (let i = startI; i !== endI + sign; i += sign) result.push(atlas.frames[i]);
return result;
}
/**
* @returns An object where all AtlasFrames with the same key are grouped in an ordered array by name in ascending value.
* A key is defined as an alphanumeric string preceding a trailing numeric string.
* E.g.:
* "hero0Idle" has no key as it does not have trailing numeric string.
* "heroIdle0" has the key "heroIdle".
* @example ```
* groupFramesByKey([{name: hero, ...}, {name: heroJump3, ...}, {name: heroJump0, ...}, {name: heroIdle0, ...}, {name: heroIdle1, ...}]) returns
* {
* heroJump: [{name: heroJump0, ...}, {name: heroJump3, ...}],
* heroIdle: [{name: heroIdle0, ...}, {name: heroIdle1, ...}]
* }
* ```
*/
function groupAtlasFramesByKey(frames) {
const result = {};
for (const frame of frames) if (getNumbersFromEnd(frame.name) !== null) {
const key = stripUnderscoresNumbersFromEnd(frame.name);
if (Object.prototype.hasOwnProperty.call(result, key)) result[key].push(frame);
else result[key] = [frame];
}
for (const entry of Object.values(result)) entry.sort((a, b) => a.name.localeCompare(b.name));
return result;
}
//#endregion
//#region src/core/abstractions/AnimatedSprite/component.vue?vue&type=script&setup=true&lang.ts
const _hoisted_1$57 = ["scale", "position"];
const _hoisted_2$27 = ["map", "alphaTest"];
const _hoisted_3$5 = ["scale", "position"];
const _hoisted_4$3 = [
"side",
"map",
"alphaTest",
"depthWrite",
"depthTest"
];
const TEXTURE_PX_TO_WORLD_UNITS = .01;
var component_vue_vue_type_script_setup_true_lang_default$18 = /* @__PURE__ */ defineComponent({
__name: "component",
props: {
image: {
type: String,
required: true
},
atlas: {
type: [
String,
Object,
Array,
Number
],
required: true
},
definitions: {
type: Object,
required: false
},
fps: {
type: Number,
required: false,
default: 30
},
loop: {
type: Boolean,
required: false,
default: true
},
animation: {
type: [
String,
Array,
Number
],
required: false,
default: 0
},
paused: {
type: Boolean,
required: false,
default: false
},
reversed: {
type: Boolean,
required: false,
default: false
},
flipX: {
type: Boolean,
required: false,
default: false
},
resetOnEnd: {
type: Boolean,
required: false,
default: false
},
asSprite: {
type: Boolean,
required: false,
default: true
},
center: {
type: null,
required: false,
default: () => [.5, .5]
},
alphaTest: {
type: Number,
required: false,
default: 0
},
depthTest: {
type: Boolean,
required: false,
default: true
},
depthWrite: {
type: Boolean,
required: false,
default: true
}
},
emits: [
"frame",
"end",
"loop"
],
async setup(__props, { expose: __expose, emit: __emit }) {
let __temp, __restore;
const props = __props;
const emit = __emit;
const { invalidate } = useTres();
watch(props, () => {
invalidate();
});
const positionX = ref(0);
const positionY = ref(0);
const scaleX = ref(0);
const scaleY = ref(0);
const groupRef = shallowRef();
__expose({ instance: groupRef });
const [textureResult, atlas] = ([__temp, __restore] = withAsyncContext(() => getTextureAndAtlasAsync(props.image, props.atlas)), __temp = await __temp, __restore(), __temp);
const texture$1 = Array.isArray(textureResult) ? textureResult[0] : textureResult;
texture$1.matrixAutoUpdate = false;
let animation = getAtlasFrames(atlas, props.animation, props.reversed);
let centerX = .5;
let centerY = .5;
let cooldown = 1;
let frame = getNullAtlasFrame();
let frameNameToEmit = null;
let frameNum = 0;
let frameHeldOnLoopEnd = false;
let dirtyFlag = true;
useLoop().onBeforeRender(({ delta }) => {
if (!props.paused && !frameHeldOnLoopEnd) cooldown -= delta * props.fps;
while (cooldown <= 0) {
cooldown++;
frameNum++;
if (props.loop) {
if (frameNum >= animation.length) emit("loop", animation[animation.length - 1].name);
frameNum %= animation.length;
} else if (frameNum >= animation.length) {
frameHeldOnLoopEnd = true;
frameNum = props.resetOnEnd ? 0 : animation.length - 1;
emit("end", animation[animation.length - 1].name);
}
}
if (animation[frameNum] !== frame) {
frame = animation[frameNum];
frameNameToEmit = frame.name;
render$1();
}
if (dirtyFlag) {
dirtyFlag = false;
texture$1.offset.x = frame.offsetX + (props.flipX ? frame.repeatX : 0);
texture$1.offset.y = frame.offsetY;
texture$1.repeat.x = frame.repeatX * (props.flipX ? -1 : 1);
texture$1.repeat.y = frame.repeatY;
texture$1.updateMatrix();
scaleX.value = frame.width * TEXTURE_PX_TO_WORLD_UNITS;
scaleY.value = frame.height * TEXTURE_PX_TO_WORLD_UNITS;
positionX.value = (.5 - centerX) * frame.width * TEXTURE_PX_TO_WORLD_UNITS;
positionY.value = (.5 - centerY) * frame.height * TEXTURE_PX_TO_WORLD_UNITS;
}
if (frameNameToEmit) {
emit("frame", frameNameToEmit);
frameNameToEmit = null;
}
});
function render$1() {
dirtyFlag = true;
}
watch(() => props.animation, (newValue, oldValue) => {
if (JSON.stringify(newValue) === JSON.stringify(oldValue)) return;
animation = getAtlasFrames(atlas, props.animation, props.reversed);
frameNum = 0;
cooldown = 1;
frameHeldOnLoopEnd = false;
render$1();
}, { immediate: true });
watch(() => props.reversed, () => {
frameNum = (animation.length - frameNum - 1) % animation.length;
animation = getAtlasFrames(atlas, props.animation, props.reversed);
if (frameHeldOnLoopEnd) frameNum = props.resetOnEnd ? 0 : animation.length - 1;
render$1();
});
watch(() => props.paused, () => {
frameHeldOnLoopEnd = false;
});
watch(() => props.loop, () => {
if (frameHeldOnLoopEnd && props.loop) frameHeldOnLoopEnd = false;
});
watch(() => props.resetOnEnd, () => {
if (frameHeldOnLoopEnd) {
frameNum = props.resetOnEnd ? 0 : animation.length - 1;
render$1();
}
});
watch(() => props.flipX, render$1);
watch(() => [props.center], () => {
[centerX, centerY] = normalizeVectorFlexibleParam(props.center);
render$1();
}, { immediate: true });
watch(() => [props.definitions], () => {
setAtlasDefinitions(atlas, props.definitions);
animation = getAtlasFrames(atlas, props.animation, props.reversed);
cooldown = 1;
frameNum = 0;
render$1();
}, { immediate: true });
onUnmounted(() => {
texture$1.dispose();
});
return (_ctx, _cache) => {
return openBlock(), createElementBlock("TresGroup", {
ref_key: "groupRef",
ref: groupRef
}, [props.asSprite ? (openBlock(), createElementBlock("TresSprite", {
key: 0,
scale: [
scaleX.value,
scaleY.value,
1
],
position: [
positionX.value,
positionY.value,
0
]
}, [createElementVNode("TresSpriteMaterial", {
toneMapped: false,
map: unref(texture$1),
transparent: true,
alphaTest: props.alphaTest
}, null, 8, _hoisted_2$27)], 8, _hoisted_1$57)) : (openBlock(), createElementBlock("TresMesh", {
key: 1,
scale: [
scaleX.value,
scaleY.value,
1
],
position: [
positionX.value,
positionY.value,
0
]
}, [_cache[0] || (_cache[0] = createElementVNode("TresPlaneGeometry", { args: [1, 1] }, null, -1)), createElementVNode("TresMeshBasicMaterial", {
toneMapped: false,
side: unref(DoubleSide),
map: unref(texture$1),
transparent: true,
alphaTest: props.alphaTest,
depthWrite: props.depthWrite,
depthTest: props.depthTest
}, null, 8, _hoisted_4$3)], 8, _hoisted_3$5)), renderSlot(_ctx.$slots, "default")], 512);
};
}
});
//#endregion
//#region src/core/abstractions/AnimatedSprite/component.vue
var component_default$1 = component_vue_vue_type_script_setup_true_lang_default$18;
//#endregion
//#region src/core/abstractions/CubeCamera/useCubeCamera.ts
function useCubeCamera(props) {
let { resolution, renderer, scene, envMap, fog, near, far } = props;
renderer = renderer ?? useTres().renderer;
scene = scene ?? useTres().scene;
const updateProps = () => {
resolution = toValue(props.resolution) ?? 255;
near = toValue(props.near) ?? .1;
far = toValue(props.far) ?? 1e3;
envMap = toValue(props.envMap) ?? void 0;
fog = toValue(props.fog) ?? void 0;
renderer = toValue(props.renderer) ?? renderer;
scene = toValue(props.scene) ?? scene;
};
watchEffect(updateProps);
const fbo = computed(() => new WebGLCubeRenderTarget(toValue(resolution)));
fbo.value.texture.type = HalfFloatType;
tryOnScopeDispose(() => {
fbo.value.dispose();
});
const camera = computed(() => new CubeCamera(toValue(near), toValue(far), toValue(fbo)));
const update = () => {
const s = toValue(scene);
const originalFog = s.fog;
const originalBackground = s.background;
s.background = toValue(envMap) || originalBackground;
s.fog = toValue(fog) || originalFog;
camera.value.update(toValue(renderer), s);
s.fog = originalFog;
s.background = originalBackground;
};
watchEffect(update);
return {
fbo,
camera,
update
};
}
//#endregion
//#region src/core/abstractions/CubeCamera/component.vue?vue&type=script&setup=true&lang.ts
const _hoisted_1$56 = ["object"];
var component_vue_vue_type_script_setup_true_lang_default$17 = /* @__PURE__ */ defineComponent({
__name: "component",
props: {
frames: {
type: null,
required: false,
default: Infinity
},
resolution: {
type: null,
required: false
},
near: {
type: null,
required: false
},
far: {
type: null,
required: false
},
envMap: {
type: null,
required: false
},
fog: {
type: null,
required: false
},
renderer: {
type: null,
required: false
},
scene: {
type: null,
required: false
}
},
setup(__props, { expose: __expose }) {
const props = __props;
const groupRef = shallowRef();
const { fbo, camera, update } = useCubeCamera(props);
let count = 0;
useLoop().onBeforeRender(() => {
if (groupRef.value && (props.frames === Infinity || count < toValue(props.frames))) {
groupRef.value.visible = false;
update();
groupRef.value.visible = true;
if (groupRef.value) groupRef.value.traverse((obj) => {
if ("material" in obj && typeof obj.material === "object" && obj.material && "envMap" in obj.material) obj.material.envMap = fbo.value.texture;
});
count++;
}
});
__expose({
instance: groupRef,
fbo,
camera,
update
});
return (_ctx, _cache) => {
return openBlock(), createElementBlock("TresGroup", {
ref_key: "groupRef",
ref: groupRef
}, [createElementVNode("primitive", { object: unref(camera) }, null, 8, _hoisted_1$56), renderSlot(_ctx.$slots, "default")], 512);
};
}
});
//#endregion
//#region src/core/abstractions/CubeCamera/component.vue
var component_default$3 = component_vue_vue_type_script_setup_true_lang_default$17;
//#endregion
//#region src/core/abstractions/Billboard.vue?vue&type=script&setup=true&lang.ts
var Billboard_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
__name: "Billboard",
props: {
autoUpdate: {
type: Boolean,
required: false,
default: true
},
lockX: {
type: Boolean,
required: false,
default: false
},
lockY: {
type: Boolean,
required: false,
default: false
},
lockZ: {
type: Boolean,
required: false,
default: false
}
},
setup(__props, { expose: __expose }) {
const props = __props;
const outerRef = shallowRef(new Group());
const innerRef = shallowRef(new Group());
const q = new Quaternion();
const r = new Euler();
function update(camera) {
if (!outerRef.value) return;
if (!camera) {
const { camera: ctxCamera } = useTresContext();
camera = ctxCamera.activeCamera.value;
if (!camera) return;
}
innerRef.value.rotation.copy(r);
outerRef.value.updateMatrix();
outerRef.value.updateWorldMatrix(false, false);
outerRef.value.getWorldQuaternion(q);
camera.getWorldQuaternion(innerRef.value.quaternion).premultiply(q.invert());
if (props.lockX) innerRef.value.rotation.x = r.x;
if (props.lockY) innerRef.value.rotation.y = r.y;
if (props.lockZ) innerRef.value.rotation.z = r.z;
}
useLoop().onBeforeRender(({ camera }) => {
if (props.autoUpdate) update(camera.value);
});
__expose({
instance: outerRef,
update
});
return (_ctx, _cache) => {
return openBlock(), createElementBlock("TresGroup", {
ref_key: "outerRef",
ref: outerRef
}, [createElementVNode("TresGroup", {
ref_key: "innerRef",
ref: innerRef
}, [renderSlot(_ctx.$slots, "default")], 512)], 512);
};
}
});
//#endregion
//#region src/core/abstractions/Billboard.vue
var Billboard_default = Billboard_vue_vue_type_script_setup_true_lang_default;
//#endregion
//#region src/core/abstractions/GlobalAudio.ts
const GlobalAudio = defineComponent({
name: "GlobalAudio",
props: [
"src",
"loop",
"volume",
"playbackRate",
"playTrigger",
"stopTrigger"
],
async setup(props, { expose, emit }) {
const { camera, renderer } = useTresContext();
const listener = new AudioListener();
camera.activeCamera.value?.add(listener);
const sound = new Audio(listener);
const audioLoader = new AudioLoader();
expose({ instance: sound });
onUnmounted(() => {
if (sound) sound.disconnect();
});
watch(() => [props.playbackRate], () => sound.setPlaybackRate(props.playbackRate ?? 1), { immediate: true });
watch(() => [props.volume], () => sound.setVolume(props.volume ?? .5), { immediate: true });
watch(() => [props.loop], () => sound.setLoop(props.loop ?? false), { immediate: true });
watch(() => [props.src], async () => {
const buffer = await audioLoader.loadAsync(props.src);
sound.setBuffer(buffer);
}, { immediate: true });
useEventListener(document.getElementById(props.playTrigger ?? "") || renderer.instance.domElement, "click", () => {
if (sound.isPlaying) sound.pause();
else sound.play();
emit("isPlaying", sound.isPlaying);
});
const btnStop = document.getElementById(props.stopTrigger ?? "");
if (btnStop) useEventListener(btnStop, "click", () => {
sound.stop();
emit("isPlaying", sound.isPlaying);
});
return null;
}
});
//#endregion
//#region src/core/abstractions/GradientTexture.vue?vue&type=script&setup=true&lang.ts
const _hoisted_1$55 = [
"color-space",
"args",
"attach"
];
var GradientTexture_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
__name: "GradientTexture",
props: {
stops: {
type: Array,
required: true
},
colors: {
type: Array,
required: true
},
attach: {
type: String,
required: false,
default: "map"
},
height: {
type: Number,
required: false,
default: 1024
},
width: {
type: Number,
required: false,
default: 16
},
type: {
type: String,
required: false,
default: "linear"
},
innerCircleRadius: {
type: Number,
required: false,
default: 0
},
outerCircleRadius: {
type: [String, Number],
required: false,
default: "auto"
}
},
setup(__props, { expose: __expose }) {
const props = __props;
const textureRef = shallowRef();
const canvas = document.createElement("canvas");
function update(canvas$1) {
const context = canvas$1.getContext("2d");
canvas$1.width = props.width;
canvas$1.height = props.height;
let gradient;
if (props.type === "linear") gradient = context.createLinearGradient(0, 0, 0, props.height);
else {
const canvasCenterX = canvas$1.width / 2;
const canvasCenterY = canvas$1.height / 2;
const radius = props.outerCircleRadius !== "auto" ? Math.abs(Number(props.outerCircleRadius)) : Math.sqrt(canvasCenterX ** 2 + canvasCenterY ** 2);
gradient = context.createRadialGradient(canvasCenterX, canvasCenterY, Math.abs(props.innerCircleRadius), canvasCenterX, canvasCenterY, radius);
}
const tempColor = new THREE.Color();
let i = props.stops.length;
while (i--) gradient.addColorStop(props.stops[i], tempColor.set(props.colors[i]).getStyle());
context.save();
context.fillStyle = gradient;
context.fillRect(0, 0, props.width, props.height);
context.restore();
if (textureRef.value) textureRef.value.needsUpdate = true;
}
const renderer = useTres().renderer;
watch(() => [
props.colors,
props.stops,
props.height,
props.width,
props.type,
props.innerCircleRadius,
props.outerCircleRadius
], () => {
update(canvas);
}, { immediate: true });
if (isReactive(props.colors)) watch(props.colors, () => update(canvas));
if (isReactive(props.stops)) watch(props.stops, () => update(canvas));
__expose({ instance: textureRef });
return (_ctx, _cache) => {
return openBlock(), createElementBlock("TresCanvasTexture", {
ref_key: "textureRef",
ref: textureRef,
"color-space": unref(renderer).outputColorSpace,
args: [unref(canvas)],
attach: props.attach
}, null, 8, _hoisted_1$55);
};
}
});
//#endregion
//#region src/core/abstractions/GradientTexture.vue
var GradientTexture_default = GradientTexture_vue_vue_type_script_setup_true_lang_default;
//#endregion
//#region src/utils/shaderMaterial.ts
function shaderMaterial(uniforms, vertexShader, fragmentShader, onInit) {
const material = class extends ShaderMaterial {
key = "";
constructor(parameters = {}) {
const entries = Object.entries(uniforms);
super({
uniforms: entries.reduce((acc, [name, value]) => {
const uniform = UniformsUtils.clone({ [name]: { value } });
return {
...acc,
...uniform
};
}, {}),
vertexShader,
fragmentShader
});
entries.forEach(([name]) => Object.defineProperty(this, name, {
get: () => this.uniforms[name].value,
set: (v) => this.uniforms[name].value = v
}));
Object.assign(this, parameters);
if (onInit) onInit(this);
}
};
material.key = MathUtils.generateUUID();
return material;
}
//#endregion
//#region src/core/abstractions/Image/ImageMaterialImpl.ts
/**
* NOTE: Source:
* https://threejs.org/docs/?q=material#api/en/materials/Material.transparent
*/
const ImageMaterialImpl = /* @__PURE__ */ shaderMaterial({
color: /* @__PURE__ */ new Color("white"),
scale: /* @__PURE__ */ new Vector2(1, 1),
imageBounds: /* @__PURE__ */ new Vector2(1, 1),
resolution: 1024,
map: null,
zoom: 1,
radius: 0,
grayscale: 0,
opacity: 1
}, `
varying vec2 vUv;
varying vec2 vPos;
void main() {
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.);
vUv = uv;
vPos = position.xy;
}
`, `
// mostly from https://gist.github.com/statico/df64c5d167362ecf7b34fca0b1459a44
varying vec2 vUv;
varying vec2 vPos;
uniform vec2 scale;
uniform vec2 imageBounds;
uniform float resolution;
uniform vec3 color;
uniform sampler2D map;
uniform float radius;
uniform float zoom;
uniform float grayscale;
uniform float opacity;
const vec3 luma = vec3(.299, 0.587, 0.114);
vec4 toGrayscale(vec4 color, float intensity) {
return vec4(mix(color.rgb, vec3(dot(color.rgb, luma)), intensity), color.a);
}
vec2 aspect(vec2 size) {
return size / min(size.x, size.y);
}
const float PI = 3.14159265;
// from https://iquilezles.org/articles/distfunctions
float udRoundBox( vec2 p, vec2 b, float r ) {
return length(max(abs(p)-b+r,0.0))-r;
}
void main() {
vec2 s = aspect(scale);
vec2 i = aspect(imageBounds);
float rs = s.x / s.y;
float ri = i.x / i.y;
vec2 new = rs < ri ? vec2(i.x * s.y / i.y, s.y) : vec2(s.x, i.y * s.x / i.x);
vec2 offset = (rs < ri ? vec2((new.x - s.x) / 2.0, 0.0) : vec2(0.0, (new.y - s.y) / 2.0)) / new;
vec2 uv = vUv * s / new + offset;
vec2 zUv = (uv - vec2(0.5, 0.5)) / zoom + vec2(0.5, 0.5);
vec2 res = vec2(scale * resolution);
vec2 halfRes = 0.5 * res;
float b = udRoundBox(vUv.xy * res - halfRes, halfRes, resolution * radius);
vec3 a = mix(vec3(1.0,0.0,0.0), vec3(0.0,0.0,0.0), smoothstep(0.0, 1.0, b));
gl_FragColor = toGrayscale(texture2D(map, zUv) * vec4(color, opacity * a), grayscale);
#include <tonemapping_fragment>
#include <colorspace_fragment>
}
`);
//#endregion
//#region src/core/abstractions/Image/ImageMaterial.vue?vue&type=script&setup=true&lang.ts
var ImageMaterial_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
__name: "ImageMaterial",
setup(__props, { expose: __expose }) {
extend({ ImageMaterial: ImageMaterialImpl });
const materialRef = shallowRef();
__expose({ instance: materialRef });
return (_ctx, _cache) => {
return openBlock(), createElementBlock("TresImageMaterial", {
ref_key: "materialRef",
ref: materialRef
}, null, 512);
};
}
});
//#endregion
//#region src/core/abstractions/Image/ImageMaterial.vue
var ImageMaterial_default = ImageMaterial_vue_vue_type_script_setup_true_lang_default;
//#endregion
//#region src/core/loaders/useTexture/index.ts
function useTexture(path) {
return useLoader(TextureLoader, path, { initialValue: new Texture() });
}
//#endregion
//#region src/core/abstractions/Image/component.vue?vue&type=script&setup=true&lang.ts
const _hoisted_1$54 = ["scale"];
const _hoisted_2$26 = ["args"];
var component_vue_vue_type_script_setup_true_lang_default$16 = /* @__PURE__ */ defineComponent({
__name: "component",
props: {
segments: {
type: Number,
required: false,
default: 1
},
scale: {
type: [Number, Array],
required: false,
default: 1
},
color: {
type: null,
required: false,
default: () => new Color("white")
},
zoom: {
type: Number,
required: false,
default: 1
},
radius: {
type: Number,
required: false,
default: 0
},
grayscale: {
type: Number,
required: false,
default: 0
},
toneMapped: {
type: Boolean,
required: false,
default: true
},
transparent: {
type: Boolean,
required: false,
default: false
},
opacity: {
type: Number,
required: false,
default: 1
},
side: {
type: null,
required: false,
default: FrontSide
},
texture: {
type: null,
required: false
},
url: {
type: null,
required: false
}
},
setup(__props, { expose: __expose }) {
const props = __props;
const imageRef = shallowRef();
const texture$1 = shallowRef(props.texture ?? null);
const { sizes: size, renderer } = useTres();
const planeBounds = computed(() => Array.isArray(props.scale) ? [props.scale[0], props.scale[1]] : [props.scale, props.scale]);
const imageBounds = computed(() => [texture$1.value?.image?.width ?? 0, texture$1.value?.image?.height ?? 0]);
const resolution = computed(() => Math.max(size.width.value, size.height.value));
const { state, isLoading } = useTexture(props.url);
watchEffect(() => {
if (props.texture) texture$1.value = props.texture;
if (!isLoading.value) {
texture$1.value = state.value;
texture$1.value.colorSpace = renderer.outputColorSpace;
}
});
const scale = computed(() => Array.isArray(props.scale) ? [...props.scale, 1] : props.scale);
__expose({ instance: imageRef });
return (_ctx, _cache) => {
return openBlock(), createElementBlock("TresMesh", {
ref_key: "imageRef",
ref: imageRef,
scale: scale.value
}, [renderSlot(_ctx.$slots, "default", {}, () => [createElementVNode("TresPlaneGeometry", { args: [
1,
1,
props.segments,
props.segments
] }, null, 8, _hoisted_2$26)]), createVNode(ImageMaterial_default, {
color: props.color,
map: texture$1.value,
zoom: props.zoom,
grayscale: props.grayscale,
opacity: props.opacity,
scale: planeBounds.value,
imageBounds: imageBounds.value,
resolution: resolution.value,
radius: __props.radius,
toneMapped: __props.toneMapped,
transparent: __props.transparent,
side: __props.side
}, null, 8, [
"color",
"map",
"zoom",
"grayscale",
"opacity",
"scale",
"imageBounds",
"resolution",
"radius",
"toneMapped",
"transparent",
"side"
])], 8, _hoisted_1$54);
};
}
});
//#endregion
//#region src/core/abstractions/Image/component.vue
var component_default$9 = component_vue_vue_type_script_setup_true_lang_default$16;
//#endregion
//#region src/core/abstractions/Lensflare/LensflareImpl.ts
var Lensflare = class Lensflare extends Mesh {
static Geometry;
isLensflare = true;
type = "Lensflare";
addElement(_) {}
dispose() {}
constructor() {
super(Lensflare.Geometry, new MeshBasicMaterial({
opacity: 0,
transparent: true
}));
this.frustumCulled = false;
this.renderOrder = Infinity;
const positionScreen = new Vector3();
const positionView = new Vector3();
const tempMap = new FramebufferTexture(16, 16);
const occlusionMap = new FramebufferTexture(16, 16);
let currentType = UnsignedByteType;
const geometry = Lensflare.Geometry;
const material1a = new RawShaderMaterial({
uniforms: {
scale: { value: null },
screenPosition: { value: null }
},
vertexShader: `
precision highp float;
uniform vec3 screenPosition;
uniform vec2 scale;
attribute vec3 position;
void main() {
gl_Position = vec4( position.xy * scale + screenPosition.xy, screenPosition.z, 1.0 );
}`,
fragmentShader: `
precision highp float;
void main() {
gl_FragColor = vec4( 1.0, 0.0, 1.0, 1.0 );
}`,
depthTest: true,
depthWrite: false,
transparent: false
});
const material1b = new RawShaderMaterial({
uniforms: {
map: { value: tempMap },
scale: { value: null },
screenPosition: { value: null }
},
vertexShader: `
precision highp float;
uniform vec3 screenPosition;
uniform vec2 scale;
attribute vec3 position;
attribute vec2 uv;
varying vec2 vUV;
void main() {
vUV = uv;
gl_Position = vec4( position.xy * scale + screenPosition.xy, screenPosition.z, 1.0 );
}`,
fragmentShader: `
precision highp float;
uniform sampler2D map;
varying vec2 vUV;
void main() {
gl_FragColor = texture2D( map, vUV );
}`,
depthTest: false,
depthWrite: false,
transparent: false
});
const mesh1 = new Mesh(geometry, material1a);
const elements = [];
const shader = LensflareElement.Shader;
const material2 = new RawShaderMaterial({
name: shader.name,
uniforms: {
map: { value: null },
occlusionMap: { value: occlusionMap },
color: { value: new Color(16777215) },
scale: { value: new Vector2() },
screenPosition: { value: new Vector3() }
},
vertexShader: shader.vertexShader,
fragmentShader: shader.fragmentShader,
blending: AdditiveBlending,
transparent: true,
depthWrite: false
});
const mesh2 = new Mesh(geometry, material2);
this.addElement = function(element) {
elements.push(element);
};
const scale = new Vector2();
const screenPositionPixels = new Vector2();
const validArea = new Box2();
const viewport = new Vector4();
this.onBeforeRender = function(renderer, _scene, camera) {
renderer.getCurrentViewport(viewport);
const renderTarget = renderer.getRenderTarget();
const type = renderTarget !== null ? renderTarget.texture.type : UnsignedByteType;
if (currentType !== type) {
tempMap.dispose();
occlusionMap.dispose();
tempMap.type = occlusionMap.type = type;
currentType = type;
}
const invAspect = viewport.w / viewport.z;
const halfViewportWidth = viewport.z / 2;
const halfViewportHeight = viewport.w / 2;
let size = 16 / viewport.w;
scale.set(size * invAspect, size);
validArea.min.set(viewport.x, viewport.y);
validArea.max.set(viewport.x + (viewport.z - 16), viewport.y + (viewport.w - 16));
positionView.setFromMatrixPosition(this.matrixWorld);
positionView.applyMatrix4(camera.matrixWorldInverse);
if (positionView.z > 0) return;
positionScreen.copy(positionView).applyMatrix4(camera.projectionMatrix);
screenPositionPixels.x = viewport.x + positionScreen.x * halfViewportWidth + halfViewportWidth - 8;
screenPositionPixels.y = viewport.y + positionScreen.y * halfViewportHeight + halfViewportHeight - 8;
if (validArea.containsPoint(screenPositionPixels)) {
renderer.copyFramebufferToTexture(tempMap, screenPositionPixels);
let uniforms = material1a.uniforms;
uniforms.scale.value = scale;
uniforms.screenPosition.value = positionScreen;
renderer.renderBufferDirect(camera, null, geometry, material1a, mesh1, null);
renderer.copyFramebufferToTexture(occlusionMap, screenPositionPixels);
uniforms = material1b.uniforms;
uniforms.scale.value = scale;
uniforms.screenPosition.value = positionScreen;
renderer.renderBufferDirect(camera, null, geometry, material1b, mesh1, null);
const vecX = -positionScreen.x * 2;
const vecY = -positionScreen.y * 2;
for (let i = 0, l = elements.length; i < l; i++) {
const element = elements[i];
const uniforms$1 = material2.uniforms;
uniforms$1.color.value.copy(element.color);
uniforms$1.map.value = element.texture;
uniforms$1.screenPosition.value.x = positionScreen.x + vecX * element.distance;
uniforms$1.screenPosition.value.y = positionScreen.y + vecY * element.distance;
size = element.size / viewport.w;
const invAspect$1 = viewport.w / viewport.z;
uniforms$1.scale.value.set(size * invAspect$1, size);
material2.uniformsNeedUpdate = true;
renderer.renderBufferDirect(camera, null, geometry, material2, mesh2, null);
}
}
};
this.dispose = function() {
material1a.dispose();
material1b.dispose();
material2.dispose();
tempMap.dispose();
occlusionMap.dispose();
for (let i = 0, l = elements.length; i < l; i++) elements[i].texture.dispose();
};
}
};
var LensflareElement = class {
texture;
size;
distance;
color;
static Shader;
constructor(texture$1, size = 1, distance = 0, color = new Color(16777215)) {
this.texture = texture$1;
this.size = size;
this.distance = distance;
this.color = color;
}
};
LensflareElement.Shader = {
name: "LensflareElementShader",
uniforms: {
map: { value: null },
occlusionMap: { value: null },
color: { value: null },
scale: { value: null },
screenPosition: { value: null }
},
vertexShader: `
precision highp float;
uniform vec3 screenPosition;
uniform vec2 scale;
uniform sampler2D occlusionMap;
attribute vec3 position;
attribute vec2 uv;
varying vec2 vUV;
varying float vVisibility;
void main() {
vUV = uv;
vec2 pos = position.xy;
vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );
visibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );
visibility += texture2D( occlusionMap, vec2( 0.9, 0.1 ) );
visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) );
visibility += texture2D( occlusionMap, vec2( 0.9, 0.9 ) );
visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) );
visibility += texture2D( occlusionMap, vec2( 0.1, 0.9 ) );
visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) );
visibility += texture2D( occlusionMap, vec2( 0.5, 0.5 ) );
vVisibility = visibility.r / 9.0;
vVisibility *= 1.0 - visibility.g / 9.0;
vVisibility *= visibility.b / 9.0;
gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );
}`,
fragmentShader: `
precision highp float;
uniform sampler2D map;
uniform vec3 color;
varying vec2 vUV;
varying float vVisibility;
void main() {
vec4 texture = texture2D( map, vUV );
texture.a *= vVisibility;
gl_FragColor = texture;
gl_FragColor.rgb *= color;
}`
};
Lensflare.Geometry = (function() {
const geometry = new BufferGeometry();
const interleavedBuffer = new InterleavedBuffer(new Float32Array([
-1,
-1,
0,
0,
0,
1,
-1,
0,
1,
0,
1,
1,
0,
1,
1,
-1,
1,
0,
0,
1
]), 5);
geometry.setIndex([
0,
1,
2,
0,
2,
3
]);
geometry.setAttribute("position", new InterleavedBufferAttribute(interleavedBuffer, 3, 0, false));
geometry.setAttribute("uv", new InterleavedBufferAttribute(interleavedBuffer, 2, 3, false));
return geometry;
})();
//#endregion
//#region src/utils/easing.ts
function linear(x) {
return x;
}
function easeInCubic(x) {
return x * x * x;
}
function easeInOutCubic(x) {
return x < .5 ? 4 * x * x * x : 1 - (-2 * x + 2) ** 3 / 2;
}
function easeInQuart(x) {
return x * x * x * x;
}
function easeOutBounce(x) {
const n1 = 7.5625;
const d1 = 2.75;
if (x < 1 / d1) return n1 * x * x;
else if (x < 2 / d1) return n1 * (x -= 1.5 / d1) * x + .75;
else if (x < 2.5 / d1) return n1 * (x -= 2.25 / d1) * x + .9375;
else return n1 * (x -= 2.625 / d1) * x + .984375;
}
//#endregion
//#region src/core/abstractions/Lensflare/constants.ts
const TEXTURE_PATH = "https://raw.githubusercontent.com/Tresjs/assets/93976c7d63ac83d4a254a41a10b2362bc17e90c9/textures/lensflare/";
const circle = `${TEXTURE_PATH}circle.png`;
const circleBlur = `${TEXTURE_PATH}circleBlur.png`;
const circleRainbow = `${TEXTURE_PATH}circleRainbow.png`;
const line = `${TEXTURE_PATH}line.png`;
const poly6 = `${TEXTURE_PATH}poly6.png`;
const polyStroke6 = `${TEXTURE_PATH}polyStroke6.png`;
const rays = `${TEXTURE_PATH}rays.png`;
const ring = `${TEXTURE_PATH}ring.png`;
const starThin6 = `${TEXTURE_PATH}starThin6.png`;
const oversize = {
texture: [line, ring],
color: ["white"],
distance: [0, 0],
size: [750, 1024],
length: [0, 2]
};
const bodyRequired0 = {
texture: [circleBlur],
color: ["white"],
distance: [0, 0],
size: [180, 512],
length: [1, 1]
};
const bodyRequired1 = {
texture: [rays],
color: ["white"],
distance: [0, 0],
size: [180, 512],
length: [1, 1]
};
const bodyOptional = {
texture: [
circle,
circleRainbow,
ring,
starThin6
],
color: ["white"],
distance: [0, 0],
size: [180, 512],
length: [2, 3]
};
const [darkPurple, darkBlue] = [3679071, 132442];
const front = {
texture: [
circleBlur,
circle,
ring,
poly6,
polyStroke6
],
color: [
"dimgray",
"gray",
"darkgray",
darkPurple,
darkBlue
],
distance: [.5, 2.5],
size: [20, 180],
length: [5, 21]
};
const back = {
texture: [
circleBlur,
circle,
ring,
poly6,
polyStroke6
],
color: [
"dimgray",
"gray",
"darkgray",
darkPurple,
darkBlue
],
distance: [-.6, -.1],
size: [180, 360],
length: [0, 5]
};
const defaultSeedProps = [
oversize,
bodyRequired0,
bodyRequired1,
bodyOptional,
front,
back
];
const defaultLensflareElementProps = {
color: "white",
distance: 0,
size: 512,
texture: circleBlur
};
//#endregion
//#region src/core/abstractions/Lensflare/RandUtils.ts
const clamp = MathUtils.clamp;
/**
* Seedable pseudorandom number tools
*/
var RandUtils = class {
_getNext;
_getGenerator;
/**
* Create a new seeded pseudorandom number generator.
* @param [seed] - the seed for the generator
* @param [getSeededRandomGenerator] - a function that returns a pseudorandom number generator
* @constructor
*/
constructor(seed = 0, getSeededRandomGenerator) {
this._getGenerator = getSeededRandomGenerator ?? this.getMulberry32;
this._getNext = this._getGenerator(seed);
}
/**
* Reseed the pseudorandom number generator
*/
seed(s) {
this._getNext = this._getGenerator(s);
}
/**
* Return the next pseudorandom number in the interval [0, 1]
*/
rand() {
return this._getNext();
}
/**
* Random float from <low, high> interval
* @param low - Low value of the interval
* @param high - High value of the interval
*/
float(low, high) {
return low + this._getNext() * (high - low);
}
/**
* Random float from <-range/2, range/2> interval
* @param range - Interval range
*/
floatSpread(range) {
return this.float(-.5 * range, .5 * range);
}
/**
* Random integer from <low, high> interval
* @param low Low value of the interval
* @param high High value of the interval
*/
int(low, high) {
return low + Math.floor(this._getNext() * (high - low + 1));
}
/**
* Choose an element from an array.
* @param array The array to choose from
* @returns An element from the array or null if the array is empty
*/
choice(array) {
if (!array.length) return null;
return array[Math.floor(this._getNext() * array.length)];
}
/**
* Choose an element from an array or return defaultValue if array is empty.
* @para