/**
* Global scope creation and evaluation of scene declaration
* This file also contains the scene api documentation
*/
import * as babel from '@babel/core';
import babelEnvPreset from '@babel/preset-env';
import babelDecoratorPlugin from '@babel/plugin-proposal-decorators';
import babelObjSpreadPlugin from '@babel/plugin-proposal-object-rest-spread';
import babelPipesPlugin from '@babel/plugin-proposal-pipeline-operator';
import SimplexNoise from 'simplex-noise';
import {
vec3 as _vec3,
vec2 as _vec2
} from 'gl-matrix';
import Scene from '../../dtos/scene';
import {createCamera} from '../../camera';
import ObjModel from '../../models/obj-model';
import Plane from '../../models/plane';
import Cube from '../../models/cube';
import Sphere from '../../models/sphere';
import Diamond from '../../models/diamond';
import Volume from '../../models/volume';
import Texture from '../../textures/texture';
import VolumeTexture from '../../textures/volume-texture';
import MetalMaterial from '../../materials/metal';
import LambertMaterial from '../../materials/lambert';
import EmissiveMaterial from '../../materials/emissive';
import DialectricMaterial from '../../materials/dialectric';
import ClearcoatMaterial from '../../materials/clearcoat';
import CoatedEmissiveMaterial from '../../materials/coated-emissive';
import IsotropicVolumeMaterial from '../../materials/isotropic-volume';
import AnisotropicVolumeMaterial from '../../materials/anisotropic-volume';
import SdfSphere from '../../models/sdf-sphere';
import SdfBox from '../../models/sdf-box';
import SdfCylinder from '../../models/sdf-cylinder';
import SdfCapsule from '../../models/sdf-capsule';
import SdfTorus from '../../models/sdf-torus';
import SdfEllipsoid from '../../models/sdf-ellipsoid';
import SdfCone from '../../models/sdf-cone';
import SdfRoundedCone from '../../models/sdf-rounded-cone';
import SdfPyramid from '../../models/sdf-pyramid';
import SdfLine from '../../models/sdf-line';
import SdfXzPlane from '../../models/sdf-xz-plane';
import SdfLink from '../../models/sdf-link';
import {
sdf as _sdf,
sdfOpUnion,
sdfOpUnionRound,
sdfOpUnionChamfer,
sdfOpUnionStairs,
sdfOpUnionColumns,
sdfOpSubtract,
sdfOpSubtractRound,
sdfOpSubtractChamfer,
sdfOpSubtractStairs,
sdfOpSubtractColumns,
sdfOpIntersect,
sdfOpIntersectRound,
sdfOpIntersectStairs,
sdfOpIntersectColumns,
sdfOpIntersectChamfer
} from '../../models/sdf-model';
import {
deterministicRandom as _random,
deterministicRandomInt as _randomInt,
deterministicRandomBool as _randomBool,
deterministicRandomSign as _randomSign,
deterministicMaybe as _maybe,
deterministicPluckRandom as _pluckRandom,
deterministicTakeRandom as _takeRandom,
range as _range,
range2d as _range2d,
range3d as _range3d,
subRange as _subRange,
glslFloat as _glslFloat,
// normedColor as _normedColor,
normedColorStr as _normedColorStr,
degToRad as _degToRad,
radToDeg as _radToDeg,
clamp as _clamp,
lerp as _lerp,
tileSeamless3d as _tileSeamless3d,
} from '../../utils';
// bring everything into local scope - this is
// needed as webpack imported module names are transpiled
// into names like '_xxxxx_js__WEBPACK_IMPORTED_MODULE_5__'
// ...also and add some syntactic sugar :)
/**
* 3d coordinates / 3d vector
*
* @typedef {object} vec3
*
* @property {number} x
* @property {number} y
* @property {number} z
*/
/**
* 2d coordinates / 2d vector
*
* @typedef {object} vec2
*
* @property {number} x
* @property {number} y
*/
/**
* Renderer settings for the scene
*
* @typedef {object} rendererSettings
*
* @property {('raytrace' | 'sdf')} [renderMode='raytrace'] - Render scene using raytracing or a much more performant (but visually crass) "sdf render mode"
* @property {number} [hitDepth=4] - Number of raytraced bounces when raytracing a static image
* @property {number} [realtimeHitDepth=2] - Number of raytraced bounces when raytracing in real time
* @property {number} [tMax=5000] - Maxiumum hit T for the raytracing renderer
* @property {number} [maxSphereTracingStep=255] - Max no. of steps for raymarched geometries
* @property {number} [resolution=0.5] - Renderer resolution before scale up. "0.5" means the resolution will be 50% of browser viewport (and thus will be scaled up 50% in the app)
*/
/**
* Camera settings for the scene. Note that camera position is auto updated
* in real time mode.
*
* @typedef {object} camera
*
* @property {vec3} lookFrom - Camera position
* @property {vec3} lookAt - Position camera is pointing at
* @property {number} vfov - Vertical field of view in degress
* @property {number} aperturre - Apterture
* @property {number} velocity - Camera movement velocity in real time mode
*/
/**
* Default settings for the SDF exporter
*
* @typedef {object} sdfExportSettings
* @todo Make the sdf exporter dialog offer the option to save these to source when exporting.
*
* @property {number} resolution - Marching cubes export resolution
* @property {vec3} minCoords - Min coords of the bounding box
* @property {vec3} maxCoords - Max coords of the bounding box
*/
/**
* Defines a GLSL displacement function
*
* @typedef {object} sdfDisplacement
*
* @property {string} name - The name/id of the displacement function
* @property {string} src - THe GLSL source code for the displacement
* @example
* {
* name: 'sinus-displacement',
* src: `
* float offset = ${glslFloat(random(100))};
* float d = sin(1.*(p.x+offset)) * sin(1.*(p.y+offset)) * sin(1.*(p.z+offset));
* dDist = d;
* `
* }
*/
/**
* The main scene object / declaration
*
* @typedef {object} sceneDescription
*
* @property {rendererSettings} [rendererSettings] - Renderer settings
* @property {camera} [camera] - Camera settings. If no camera is provided a default will be injected (sic) into the source. Note that camera position is auto updated in real time mode.
* @property {(string|string[])} background - A hex color string or an array of length 2 with hex color strings for the scene background
* @property {sdfExportSettings} [sdfExportSettings] - Default sdf export settings.
* @property {Array|Array[]} geometries - The scene geometries. An array of objects created by any of the {@link module:geometries|geometry functions} will do. NOTE: the geometry array can contain nested arrays of geometries. The array is flattened upon parsing.
* @property {Array} materials - The scene materials. An array of objects created by any of the {@link module:materials|material functions} will do.
* @property {Array} textures - The scene textures. Any object created by the {@link module:textures|texture functions} will do.
* @property {sdfDisplacement[]} [displacements] - SDF displacement function definitions
*/
/**
* The main scene call
*
* @static
* @function
* @param {sceneDescription} scene - The scene declaration
*/
const scene = (o) =>
new Scene(o);
const camera = (o) =>
createCamera(o);
/** @module geometries */
/**
* Wavefront .obj model geometry (no MTL support!)
*
* @static
* @function
* @param {object} options - Model options
* @param {string} options.url - Model url
* @param {vec3} options.position - Position
* @param {vec3} [options.rotation] - Rotation per axis in radians.
* @param {vec3|number} [options.scale] - A vec3 representing scale per axis or a single number by which to scale all directions with.
* @param {bool} [options.smoothShading] - If true use smooth shading instead of flat.
* @param {bool} [options.doubleSided] - Shade both sides of each triangle (you probably want this if your model has "holes"). Defaults to false.
* @param {bool} [options.flipNormals] - Flip model normals
* @param {string} options.material - The name of the material to use. If omitted the geometry will default to the first declared material.
* @param {module:textures~geometryTexture} [options.texture] - A texture to apply on the geometry. Textures on SDF geometries are applied using triplanar mapping.
* @param {module:textures~geometryNormalMap} [options.displacementMap] - A texture used as a normal map for the geometry.
*/
const objModel = (o) =>
new ObjModel(o);
/**
* Shortcut for a simple plane .obj model
*
* @static
* @function
* @param {object} options - Model options
* @param {vec3} options.position - Position
* @param {vec3} [options.rotation] - Rotation per axis in radians.
* @param {vec3|number} [options.scale] - A vec3 representing scale per axis or a single number by which to scale all directions with.
* @param {bool} [options.smoothShading] - If true use smooth shading instead of flat. Defaults to true.
* @param {string} options.material - The name of the material to use. If omitted the geometry will default to the first declared material.
* @param {bool} [options.doubleSided] - Shade both sides of each triangle (you probably want this if your model has "holes"). Defaults to false.
* @param {module:textures~geometryTexture} [options.texture] - A texture to apply on the geometry. Textures on SDF geometries are applied using triplanar mapping.
* @param {module:textures~geometryNormalMap} [options.displacementMap] - A texture used as a normal map for the geometry.
*/
const plane = (o) =>
new Plane(o);
/**
* Shortcut for a simple cube .obj model
*
* @static
* @function
* @param {object} options - Model options
* @param {vec3} options.position - Position
* @param {vec3} [options.rotation] - Rotation per axis in radians.
* @param {vec3|number} [options.scale] - A vec3 representing scale per axis or a single number by which to scale all directions with.
* @param {bool} [options.smoothShading] - If true use smooth shading instead of flat. Defaults to true.
* @param {bool} [options.doubleSided] - Shade both sides of each triangle (you probably want this if your model has "holes"). Defaults to false.
* @param {string} options.material - The name of the material to use. If omitted the geometry will default to the first declared material.
* @param {module:textures~geometryTexture} [options.texture] - A texture to apply on the geometry. Textures on SDF geometries are applied using triplanar mapping.
* @param {module:textures~geometryNormalMap} [options.displacementMap] - A texture used as a normal map for the geometry.
*/
const cube = (o) =>
new Cube(o);
/**
* A sphere geometry
*
* @static
* @function
* @param {object} options - Sphere options
* @param {vec3} options.position - Position
* @param {number} options.radius - Sphere radius
* @param {string} options.material - The name of the material to use. If omitted the geometry will default to the first declared material.
* @param {module:textures~geometryTexture} [options.texture] - A texture to apply on the geometry. Textures on SDF geometries are applied using triplanar mapping.
* @param {module:textures~geometryNormalMap} [options.displacementMap] - A texture used as a normal map for the geometry.
*/
const sphere = (o) =>
new Sphere(o);
/**
* Shortcut for a diamond .obj model
*
* @static
* @function
* @param {object} options - Model options
* @param {vec3} options.position - Position
* @param {vec3} [options.rotation] - Rotation per axis in radians.
* @param {vec3|number} [options.scale] - A vec3 representing scale per axis or a single number by which to scale all directions with.
* @param {bool} [options.smoothShading] - If true use smooth shading instead of flat. Defaults to true.
* @param {bool} [options.doubleSided] - Shade both sides of each triangle (you probably want this if your model has "holes"). Defaults to false.
* @param {string} options.material - The name of the material to use. If omitted the geometry will default to the first declared material.
* @param {module:textures~geometryTexture} [options.texture] - A texture to apply on the geometry. Textures on SDF geometries are applied using triplanar mapping.
* @param {module:textures~geometryNormalMap} [options.displacementMap] - A texture used as a normal map for the geometry.
*/
const diamond = (o) =>
new Diamond(o);
/**
* Volume bound geometry.
* Used in conjunction with a volume material
* ({@link module:materials.isotropicVolumeMaterial|isotropicVolumeMaterial}
* or {@link module:materials.anisotropicVolumeMaterial|anisotropicVolumeMaterial}),
* and, optionally, a {@link module:textures.volumeTexture|volume texture}).
*
* @static
* @function
* @param {object} options - Volume options
* @param {string} options.material - The name of a defined {@link module:materials.isotropicVolumeMaterial|isotropicVolumeMaterial}
* or {@link module:materials.anisotropicVolumeMaterial|anisotropicVolumeMaterial}.
* @param {string} [options.texture] - An optional {@link module:textures.volumeTexture|volume texture} to sample from if using an
* {@link module:materials.anisotropicVolumeMaterial|anisotropic material}. If no texture is supplied
* the volume will be automatically sampled from a standard noise function.
* @param {vec3} options.minCoords - Min coords of the bound
* @param {vec3} options.maxCoords - Max coords of the bound
*
* @example
* volume({
* material: 'iso-volume',
* minCoords: {
* x: 0,
* y: 0,
* z: 0
* },
* maxCoords: {
* x: 20,
* y: 20,
* z: 20
* },
* });
*
* @example
* volume({
* material: 'aniso-volume-material',
* texture: 'volume-texture',
* minCoords: {
* x: -155,
* y: 0.0,
* z: -155
* },
* maxCoords: {
* x: 155,
* y: 80.0,
* z: 155
* }
* });
*/
const volume = (o) =>
new Volume(o);
/**
* Represents an SDF domain operation:
* 'repeat', 'repeatBounded', 'bend' or 'twist'
*
* {@link https://iquilezles.org/www/articles/distfunctions/distfunctions.htm|Read more in inigo quilez' 3d sdf article}
*
* @typedef {object} domain
*
* @property {('repeat' | 'repeatBounded' | 'bend' | 'twist')} domainOp - Operation type
* @property {number} size - Size of the side of the cube of space to apply the operation on if repeating. The magnitude of the operation otherwise.
* @property {vec3} [bounds] - NOTE: only required for bounded repetitions. Number of repetitions along each axis. Should be renamed :)
* @property {('x' | 'y' | 'z' | 'xy' | 'xz' | 'yz' | 'xyz')} [axis] - NOTE: only required for repeat operations. Along which axes to repeat.
*/
/**
* SDF geometry wrapper. All sdf geometries & operations need to be wrapped by this function call.
*
* @name sdf
* @static
* @function
* @param {domain} [domain] - Optional domain operation to apply to the whole final geometry
* @param {(sdfGeometry | sdfOperation)} sdfData - An sdf geometry or sdf operation consisting of multiple geometries.
*
* @example
* geometries = [
* sdf(
* sdfSphere({
* material: 'mat1',
* radius: 2,
* position: {x: 0, y: 0, z: 0}
* })
* ),
* // ...
* ];
*
* @example
* geometries = [
* sdf(
* sdfSphere({
* radius: 2,
* material: 'mat1'
* position: {x: 0, y: 0, z: 0}
*
* })
* |> opUnionRound(({radius: 0.5}), #,
* sdfBox({
* dimensions: {
* x: 2,
* y: 3,
* z: 1,
* },
* position: {x: 1, y: 0, z: 4},
* material: 'mat1'
* })
* )
* ),
* // ...
* ];
*
*/
const sdf = _sdf;
/**
* SDF sphere geometry
*
* @static
* @function
* @param {object} options - Sphere options
* @param {number} options.radius - The sphere radius
* @param {vec3} options.position - Position
* @param {string} options.material - The name of the material to use. If omitted the geometry will default to the first declared material.
* @param {module:textures~geometryTexture} [options.texture] - A texture to apply on the geometry. Textures on SDF geometries are applied using triplanar mapping.
* @param {module:geometries~domain} [options.domain] - A domain operation that should be applied on this geometry.
* @param {module:textures~geometryDisplacementMap} [options.displacementMap] - A texture used as a displacement map for the geometry.
* @param {string} [options.displacementFunc] - The name of a in this scene defined displacement func to use as displacement data.
*/
const sdfSphere = (o) =>
new SdfSphere(o).geometryData();
/**
* SDF box geometry
*
* @static
* @function
* @param {object} options - Box options
* @param {vec3} options.position - Position
* @param {vec3} options.dimensions - Box dimensions
* @param {vec3} [options.rotation] - Box rotation per axis in radians.
* @param {string} options.material - The name of the material to use. If omitted the geometry will default to the first declared material.
* @param {module:textures~geometryTexture} [options.texture] - A texture to apply on the geometry. Textures on SDF geometries are applied using triplanar mapping.
* @param {module:geometries~domain} [options.domain] - A domain operation that should be applied on this geometry.
* @param {module:textures~geometryDisplacementMap} [options.displacementMap] - A texture used as a displacement map for the geometry.
* @param {string} [options.displacementFunc] - The name of a in this scene defined displacement func to use as displacement data.
*/
const sdfBox = (o) =>
new SdfBox(o).geometryData();
/**
* SDF cylinder geometry
*
* @static
* @function
* @param {object} options - Cylinder options
* @param {vec3} options.position - Position
* @param {number} options.radius - Cylinder radius
* @param {number} options.height - Cylinder height
* @param {vec3} [options.rotation] - Cylinder rotation per axis in radians.
* @param {string} options.material - The name of the material to use. If omitted the geometry will default to the first declared material.
* @param {module:textures~geometryTexture} [options.texture] - A texture to apply on the geometry. Textures on SDF geometries are applied using triplanar mapping.
* @param {module:geometries~domain} [options.domain] - A domain operation that should be applied on this geometry.
* @param {module:textures~geometryDisplacementMap} [options.displacementMap] - A texture used as a displacement map for the geometry.
* @param {string} [options.displacementFunc] - The name of a in this scene defined displacement func to use as displacement data.
*/
const sdfCylinder = (o) =>
new SdfCylinder(o).geometryData();
/**
* SDF torus geometry
*
* @static
* @function
* @param {object} options - Torus options
* @param {vec3} options.position - Position
* @param {number} options.innerRadius - Torus inner radius
* @param {number} options.outerRadius - Torus outer radius
* @param {vec3} [options.rotation] - Torus rotation per axis in radians.
* @param {string} options.material - The name of the material to use. If omitted the geometry will default to the first declared material.
* @param {module:textures~geometryTexture} [options.texture] - A texture to apply on the geometry. Textures on SDF geometries are applied using triplanar mapping.
* @param {module:geometries~domain} [options.domain] - A domain operation that should be applied on this geometry.
* @param {module:textures~geometryDisplacementMap} [options.displacementMap] - A texture used as a displacement map for the geometry.
* @param {string} [options.displacementFunc] - The name of a in this scene defined displacement func to use as displacement data.
*/
const sdfTorus = (o) =>
new SdfTorus(o).geometryData();
/**
* SDF ellipsoid geometry
*
* @static
* @function
* @param {object} options - Ellipsoid options
* @param {vec3} options.position - Position
* @param {vec3} options.radius - Ellipsoid radii per axis
* @param {vec3} [options.rotation] - Ellipsoid rotation per axis in radians.
* @param {string} options.material - The name of the material to use. If omitted the geometry will default to the first declared material.
* @param {module:textures~geometryTexture} [options.texture] - A texture to apply on the geometry. Textures on SDF geometries are applied using triplanar mapping.
* @param {module:geometries~domain} [options.domain] - A domain operation that should be applied on this geometry.
* @param {module:textures~geometryDisplacementMap} [options.displacementMap] - A texture used as a displacement map for the geometry.
* @param {string} [options.displacementFunc] - The name of a in this scene defined displacement func to use as displacement data.
*/
const sdfEllipsoid = (o) =>
new SdfEllipsoid(o).geometryData();
/**
* SDF cone geometry
*
* @static
* @function
* @param {object} options - Cone options
* @param {vec3} options.position - Position
* @param {number} options.radius - Cone radius
* @param {number} options.height - Cone height
* @param {vec3} [options.rotation] - Cone rotation per axis in radians.
* @param {string} options.material - The name of the material to use. If omitted the geometry will default to the first declared material.
* @param {module:textures~geometryTexture} [options.texture] - A texture to apply on the geometry. Textures on SDF geometries are applied using triplanar mapping.
* @param {module:geometries~domain} [options.domain] - A domain operation that should be applied on this geometry.
* @param {module:textures~geometryDisplacementMap} [options.displacementMap] - A texture used as a displacement map for the geometry.
* @param {string} [options.displacementFunc] - The name of a in this scene defined displacement func to use as displacement data.
*/
const sdfCone = (o) =>
new SdfCone(o).geometryData();
/**
* SDF cone geometry
*
* @static
* @function
* @param {object} options - Cone options
* @param {vec3} options.position - Position
* @param {number} options.bottomRadius - Cone bottom radius
* @param {number} options.topRadius - Cone radius
* @param {number} options.height - Cone height
* @param {vec3} [options.rotation] - Cone rotation per axis in radians.
* @param {string} options.material - The name of the material to use. If omitted the geometry will default to the first declared material.
* @param {module:textures~geometryTexture} [options.texture] - A texture to apply on the geometry. Textures on SDF geometries are applied using triplanar mapping.
* @param {module:geometries~domain} [options.domain] - A domain operation that should be applied on this geometry.
* @param {module:textures~geometryDisplacementMap} [options.displacementMap] - A texture used as a displacement map for the geometry.
* @param {string} [options.displacementFunc] - The name of a in this scene defined displacement func to use as displacement data.
*/
const sdfRoundedCone = (o) =>
new SdfRoundedCone(o).geometryData();
/**
* SDF pyramid geometry
*
* @static
* @function
* @param {object} options - Pyramid options
* @param {vec3} options.position - Position
* @param {number} options.base - Pyramid bottom base dimension
* @param {number} options.height - Pyramid height
* @param {vec3} [options.rotation] - Pyramid rotation per axis in radians.
* @param {string} options.material - The name of the material to use. If omitted the geometry will default to the first declared material.
* @param {module:textures~geometryTexture} [options.texture] - A texture to apply on the geometry. Textures on SDF geometries are applied using triplanar mapping.
* @param {module:geometries~domain} [options.domain] - A domain operation that should be applied on this geometry.
* @param {module:textures~geometryDisplacementMap} [options.displacementMap] - A texture used as a displacement map for the geometry.
* @param {string} [options.displacementFunc] - The name of a in this scene defined displacement func to use as displacement data.
*/
const sdfPyramid = (o) =>
new SdfPyramid(o).geometryData();
/**
* SDF line segment between two points
*
* @static
* @function
* @param {object} options - Line options
* @param {vec3} options.position - Position
* @param {vec3} options.start - Line starting point
* @param {vec3} options.end - Line end point
* @param {number} options.thickness - The thickness of the line
* @param {vec3} [options.rotation] - Pyramid rotation per axis in radians.
* @param {string} options.material - The name of the material to use. If omitted the geometry will default to the first declared material.
* @param {module:textures~geometryTexture} [options.texture] - A texture to apply on the geometry. Textures on SDF geometries are applied using triplanar mapping.
* @param {module:geometries~domain} [options.domain] - A domain operation that should be applied on this geometry.
* @param {module:textures~geometryDisplacementMap} [options.displacementMap] - A texture used as a displacement map for the geometry.
* @param {string} [options.displacementFunc] - The name of a in this scene defined displacement func to use as displacement data.
*/
const sdfLine = (o) =>
new SdfLine(o).geometryData();
const sdfXzPlane = (o) =>
new SdfXzPlane(o).geometryData();
/**
* SDF capsule geometry
*
* @static
* @function
* @param {object} options - Capsule options
* @param {vec3} options.position - Position
* @param {number} options.radius - Capsule radius
* @param {number} options.height - Capsule height
* @param {vec3} [options.rotation] - Capsule rotation per axis in radians.
* @param {string} options.material - The name of the material to use. If omitted the geometry will default to the first declared material.
* @param {module:textures~geometryTexture} [options.texture] - A texture to apply on the geometry. Textures on SDF geometries are applied using triplanar mapping.
* @param {module:geometries~domain} [options.domain] - A domain operation that should be applied on this geometry.
* @param {module:textures~geometryDisplacementMap} [options.displacementMap] - A texture used as a displacement map for the geometry.
* @param {string} [options.displacementFunc] - The name of a in this scene defined displacement func to use as displacement data.
*/
const sdfCapsule = (o) =>
new SdfCapsule(o).geometryData();
/**
* SDF chain link geometry
*
* @static
* @function
* @param {object} options - Chain link options
* @param {vec3} options.position - Position
* @param {number} options.radius - Chain link end radius
* @param {number} options.height - Chain link height
* @param {number} options.thickness - The thickness of the link
* @param {vec3} [options.rotation] - Capsule rotation per axis in radians.
* @param {string} options.material - The name of the material to use. If omitted the geometry will default to the first declared material.
* @param {module:textures~geometryTexture} [options.texture] - A texture to apply on the geometry. Textures on SDF geometries are applied using triplanar mapping.
* @param {module:geometries~domain} [options.domain] - A domain operation that should be applied on this geometry.
* @param {module:textures~geometryDisplacementMap} [options.displacementMap] - A texture used as a displacement map for the geometry.
* @param {string} [options.displacementFunc] - The name of a in this scene defined displacement func to use as displacement data.
*/
const sdfLink = (o) =>
new SdfLink(o).geometryData();
/**
* @module sdf-operations
*/
/**
* SDF union operation. Needs to be wrapped by the sdf() function call.
* See example with sdf() {@link module:geometries.sdf|here}.
*
* NOTE! You shouldn't use this for performance reasons. If you need
* a regular union you should just create another geometry
* and wrap it in a new sdf() call. The engine will handle the union for you
* and each geometry will have its own bounding box for BVH construction (=speedier!).
*
* @static
* @function
* @param {...(sdfGeometry | sdfOperation)} sdfData - An {@link module:geometries.sdf|sdf geometry}
* or sdf operation consisting of multiple geometries.
*/
const opUnion = sdfOpUnion;
/**
* SDF union operation with a round blend. Needs to be wrapped by the sdf() function call.
* See example with sdf() {@link module:geometries.sdf|here}.
*
* @static
* @function
* @param {object} options - Operator options
* @param {number} options.radius - Thickness of the blending
* @param {number} options.colorBlendAmount - Amount of color blend at geometry intersections
* @param {...(sdfGeometry | sdfOperation)} sdfData - An sdf geometry or sdf operation consisting of multiple geometries.
*/
const opUnionRound = sdfOpUnionRound;
/**
* SDF union operation with a chamfer effect. Needs to be wrapped by the sdf() function call.
* See example with sdf() {@link module:geometries.sdf|here}.
*
* @static
* @function
* @param {object} options - Operator options
* @param {number} options.radius - Thickness of the blending
* @param {number} options.colorBlendAmount - Amount of color blend at geometry intersections
* @param {...(sdfGeometry | sdfOperation)} sdfData - An sdf geometry or sdf operation consisting of multiple geometries.
*/
const opUnionChamfer = sdfOpUnionChamfer;
/**
* SDF union operation with a stairs effect. Needs to be wrapped by the sdf() function call.
* See example with sdf() {@link module:geometries.sdf|here}.
*
* @static
* @function
* @param {object} options - Operator options
* @param {number} options.radius - Thickness of the blending
* @param {integer} options.steps - Number of stair steps
* @param {number} options.colorBlendAmount - Amount of color blend at geometry intersections
* @param {...(sdfGeometry | sdfOperation)} sdfData - An sdf geometry or sdf operation consisting of multiple geometries.
*/
const opUnionStairs = sdfOpUnionStairs;
/**
* SDF union operation with columns effect. Needs to be wrapped by the sdf() function call.
* See example with sdf() {@link module:geometries.sdf|here}.
*
* @static
* @function
* @param {object} options - Operator options
* @param {number} options.radius - Thickness of the blending
* @param {integer} options.steps - Number of colymn steps
* @param {number} options.colorBlendAmount - Amount of color blend at geometry intersections
* @param {...(sdfGeometry | sdfOperation)} sdfData - An sdf geometry or sdf operation consisting of multiple geometries.
*/
const opUnionColumns = sdfOpUnionColumns;
/**
* SDF subtraction operation. Needs to be wrapped by the sdf() function call.
* See example with sdf() {@link module:geometries.sdf|here}.
*
* @static
* @function
* @param {(sdfGeometry | sdfOperation)} sdfData - The term to subtract from. An {@link module:geometries.sdf|sdf geometry},
* or sdf operation consisting of multiple geometries.
* @param {...(sdfGeometry | sdfOperation)} sdfData - An {@link module:geometries.sdf|sdf geometry}, or sdf operation consisting
* of multiple geometries, to subtract from the first term.
*/
const opSubtract = sdfOpSubtract;
/**
* SDF subtraction operation with a round blend. Needs to be wrapped by the sdf() function call.
* See example with sdf() {@link module:geometries.sdf|here}.
*
* @static
* @function
* @param {object} options - Operator options
* @param {number} options.radius - Thickness of the blending
* @param {(sdfGeometry | sdfOperation)} sdfData - The term to subtract from. An {@link module:geometries.sdf|sdf geometry},
* or sdf operation consisting of multiple geometries.
* @param {...(sdfGeometry | sdfOperation)} sdfData - An {@link module:geometries.sdf|sdf geometry}, or sdf operation consisting
* of multiple geometries, to subtract from the first term.
*/
const opSubtractRound = sdfOpSubtractRound;
/**
* SDF subtraction operation with a stairs effect. Needs to be wrapped by the sdf() function call.
* See example with sdf() {@link module:geometries.sdf|here}.
*
* @static
* @function
* @param {object} options - Operator options
* @param {number} options.radius - Thickness of the blending
* @param {integer} options.steps - Number of stair steps
* @param {(sdfGeometry | sdfOperation)} sdfData - The term to subtract from. An {@link module:geometries.sdf|sdf geometry},
* or sdf operation consisting of multiple geometries.
* @param {...(sdfGeometry | sdfOperation)} sdfData - An {@link module:geometries.sdf|sdf geometry}, or sdf operation consisting
* of multiple geometries, to subtract from the first term.
*/
const opSubtractStairs = sdfOpSubtractStairs;
/**
* SDF subtraction operation with a chamfer effect. Needs to be wrapped by the sdf() function call.
* See example with sdf() {@link module:geometries.sdf|here}.
*
* @static
* @function
* @param {object} options - Operator options
* @param {number} options.radius - Thickness of the blending
* @param {(sdfGeometry | sdfOperation)} sdfData - The term to subtract from. An {@link module:geometries.sdf|sdf geometry},
* or sdf operation consisting of multiple geometries.
* @param {...(sdfGeometry | sdfOperation)} sdfData - An {@link module:geometries.sdf|sdf geometry}, or sdf operation consisting
* of multiple geometries, to subtract from the first term.
*/
const opSubtractChamfer = sdfOpSubtractChamfer;
/**
* SDF subtraction operation with a columns stairs effect. Needs to be wrapped by the sdf() function call.
* See example with sdf() {@link module:geometries.sdf|here}.
*
* @static
* @function
* @param {object} options - Operator options
* @param {number} options.radius - Thickness of the blending
* @param {integer} options.steps - Number of columns
* @param {(sdfGeometry | sdfOperation)} sdfData - The term to subtract from. An {@link module:geometries.sdf|sdf geometry},
* or sdf operation consisting of multiple geometries.
* @param {...(sdfGeometry | sdfOperation)} sdfData - An {@link module:geometries.sdf|sdf geometry}, or sdf operation consisting
* of multiple geometries, to subtract from the first term.
*/
const opSubtractColumns = sdfOpSubtractColumns;
/**
* SDF intersection operation. Needs to be wrapped by the sdf() function call.
* See example with sdf() {@link module:geometries.sdf|here}.
*
* @static
* @function
* @param {...(sdfGeometry | sdfOperation)} sdfData - An {@link module:geometries.sdf|sdf geometry}
* or sdf operation consisting of multiple geometries.
*/
const opIntersect = sdfOpIntersect;
/**
* SDF intersection operation with a round blend. Needs to be wrapped by the sdf() function call.
* See example with sdf() {@link module:geometries.sdf|here}.
*
* @static
* @function
* @param {object} options - Operator options
* @param {number} options.radius - Thickness of the blending
* @param {...(sdfGeometry | sdfOperation)} sdfData - An sdf geometry or sdf operation consisting of multiple geometries.
*/
const opIntersectRound = sdfOpIntersectRound;
/**
* SDF intersection operation with a chamfer effect. Needs to be wrapped by the sdf() function call.
* See example with sdf() {@link module:geometries.sdf|here}.
*
* @static
* @function
* @param {object} options - Operator options
* @param {number} options.radius - Thickness of the blending
* @param {...(sdfGeometry | sdfOperation)} sdfData - An sdf geometry or sdf operation consisting of multiple geometries.
*/
const opIntersectChamfer = sdfOpIntersectChamfer;
/**
* SDF intersection operation with a stairs effect. Needs to be wrapped by the sdf() function call.
* See example with sdf() {@link module:geometries.sdf|here}.
*
* @static
* @function
* @param {object} options - Operator options
* @param {number} options.radius - Thickness of the blending
* @param {integer} options.steps - Number of stair steps
* @param {number} options.colorBlendAmount - Amount of color blend at geometry intersections
* @param {...(sdfGeometry | sdfOperation)} sdfData - An sdf geometry or sdf operation consisting of multiple geometries.
*/
const opIntersectStairs = sdfOpIntersectStairs;
/**
* SDF intersection operation with a column stairs effect. Needs to be wrapped by the sdf() function call.
* See example with sdf() {@link module:geometries.sdf|here}.
*
* @static
* @function
* @param {object} options - Operator options
* @param {number} options.radius - Thickness of the blending
* @param {integer} options.steps - Number of column steps
* @param {number} options.colorBlendAmount - Amount of color blend at geometry intersections
* @param {...(sdfGeometry | sdfOperation)} sdfData - An sdf geometry or sdf operation consisting of multiple geometries.
*/
const opIntersectColumns = sdfOpIntersectColumns;
/** @module textures */
/**
* Apply texture with name 'name' to a geometry.
*
* @typedef {object} geometryTexture
*
* @property {string} name - The name of the texture as it is defined in the main texture array of the scene.
* @property {number|vec2} uvScale - Texture scale as single number or per axis.
*/
/**
* Apply texture with name 'name' as a displacement map to a SDF geometry.
*
* @typedef {object} geometryDisplacementMap
*
* @property {string} name - The name of the texture as it is defined in the main texture array of the scene.
* @property {number|vec2} uvScale - Texture scale as single number or per axis.
* @property {number} scale - The normal map scale/intensity
*/
/**
* Apply texture with name 'name' as a normal map to a regular geometry.
*
* @typedef {object} geometryNormalMap
*
* @property {string} name - The name of the texture as it is defined in the main texture array of the scene.
* @property {number|vec2} uvScale - Texture scale as single number or per axis.
* @property {number} scale - The normal map scale/intensity
*/
/**
*
* Represents a texture
*
* @static
* @function
* @param {object} settings - Texture settings
* @param {string} settings.name - The name/id of the texture.
* @param {string} [settings.url] - If supplied this url will be used to load an image for the texture.
* @param {strirng} [settings.src] - If supplied this glsl code string will be used to generate a texture dynamically via a fragment shader. See example below.
*
* @example
* // check pattern dynamic texture
* // set tColor to the output rgba value for the current pixel fragment
*
* texture({
* name: 'check',
* src: `
* float s = sin(50.*uv.x)*sin(50.*uv.y);
* if(s < 0.) {
* tColor = vec4(${normedColorStr('#aaaaaa')}, 1.0);
* } else {
* tColor = vec4(0.3, 0.0, 0.0, 1.);
* }
* `
* }),
*
* @example
* // regular texture with image loaded from url
*
* texture({
* name: 'concrete-texture',
* url: 'assets/images/concrete.jpg'
* })
*/
const texture = (o) =>
new Texture(o);
/**
*
* Represents a volume texture (sampling data for an inhomogeneous volume)
*
* @static
* @function
* @param {object} settings - Texture settings
* @param {number} settings.size - Volume size (cubic)
* @param {string} settings.name - The name/id of the texture.
* @param {bool} [settings.cache] - Whether to cache the data (useful if generated on the fly, see example 1)
* @param {number[][][] | Function} data - A 3d array containing sampling data or a function returning a 3d array with data
*
* @example
* // tiled & cached volume generated on the fly
* textures = [
* volumeTexture({
* name: 'volume-texture',
* size: 64,
* cache: true,
* data: range3d(64)
* |> #.map(([x, y, z]) => {
* const scale = 50;
* let f = simplex.noise3D(x*scale, y*scale, z*scale);
* return clamp(f, 0.0, 1.0);
* })
* |> tileSeamless3d(#, 64)
* })
* ];
*/
const volumeTexture = (o) =>
new VolumeTexture(o);
/** @module materials */
/**
*
* Represents a lambert material
*
* @static
* @function
* @param {object} settings - Material settings
* @param {string} settings.name - The name/id of the material.
* @param {string} settings.color - Material color as a hex string: '#ffffff'
* @param {number[]} settings.albedo - Array of three albedo values (one for each rgb channel): [0.8, 0.9, 0.9]
*/
const lambertMaterial = (o) =>
new LambertMaterial(o);
/**
*
* Represents a metal materiall
*
* @static
* @function
* @param {object} settings - Material settings
* @param {string} settings.name - The name/id of the material.
* @param {string} settings.color - Material color as a hex string: '#ffffff'
* @param {number[]} settings.albedo - Array of three albedo values (one for each rgb channel): [0.8, 0.9, 0.9]
* @param {number} settings.fuzz - Metal "fuzziness". A value of '0' signifies perfect reflectance.
*/
const metalMaterial = (o) =>
new MetalMaterial(o);
/**
*
* Represents a dielectric / glass material
*
* @static
* @function
* @param {object} settings - Material settings
* @param {string} settings.name - The name/id of the material.
* @param {string} settings.color - Material color as a hex string: '#ffffff'
* @param {number[]} settings.albedo - Array of three albedo values (one for each rgb channel): [0.8, 0.9, 0.9]
* @param {number} settings.refIdx- Refractive index for the material. Water: ~1.33, window glass: 1.52, diamond: ~2.42
*/
const dialectricMaterial = (o) =>
new DialectricMaterial(o);
/**
*
* Represents a dielectric / glass material
*
* @static
* @function
* @param {object} settings - Material settings
* @param {string} settings.name - The name/id of the material.
* @param {string} settings.color - Material color as a hex string: '#ffffff'
* @param {number[]} settings.albedo - Array of three albedo values (one for each rgb channel): [0.8, 0.9, 0.9]
* @param {number} settings.refIdx- Refractive index for the material. Water: ~1.33, window glass: 1.52, diamond: ~2.42
*/
const clearcoatMaterial = (o) =>
new ClearcoatMaterial(o);
/**
*
* Represents an emissive material ("a light")
*
* @static
* @function
* @param {object} settings - Material settings
* @param {string} settings.name - The name/id of the material.
* @param {string} settings.color - Material color as a hex string: '#ffffff'
* @param {number} settings.intensity - Emissive intensity. Should be larger than 1.
*/
const emissiveMaterial = (o) =>
new EmissiveMaterial(o);
/**
*
* Represents a coated emissive material ("a light with glass over it")
*
* @static
* @function
* @param {object} settings - Material settings
* @param {string} settings.name - The name/id of the material.
* @param {string} settings.color - Material color as a hex string: '#ffffff'
* @param {number} settings.intensity - Emissive intensity. Should be larger than 1.
* @param {number} settings.refIdx- Refractive index for the material. Water: ~1.33, window glass: 1.52, diamond: ~2.42
*/
const coatedEmissiveMaterial = (o) =>
new CoatedEmissiveMaterial(o);
/**
*
* Isotropic (homogeneous) material
*
* @static
* @function
* @param {object} settings - Material settings
* @param {string} settings.name - The name/id of the material.
* @param {string} settings.color - Material color as a hex string: '#ffffff'
* @param {number[]} settings.albedo - Array of three albedo values (one for each rgb channel): [0.8, 0.9, 0.9]
* @param {number} settings.density - Volume density
*/
const isotropicVolumeMaterial = (o) =>
new IsotropicVolumeMaterial(o);
/**
*
* Anisotropic (inhomogeneous) material
*
* @static
* @function
* @param {object} settings - Material settings
* @param {string} settings.name - The name/id of the material.
* @param {string} settings.color - Material color as a hex string: '#ffffff'
* @param {number[]} settings.albedo - Array of three albedo values (one for each rgb channel): [0.8, 0.9, 0.9]
* @param {number} settings.density - Volume density
* @param {number} settings.scale - Sampling scale
* @param {number} settings.sampleOffset - Sample start offset
*/
const anisotropicVolumeMaterial = (o) =>
new AnisotropicVolumeMaterial(o);
/**
* These utils are available in the main scene scope
* @module utils
*/
/**
* vec3 - a standard glMatrix vec3 (constructor) function.
*
* {@link http://glmatrix.net/docs/module-vec3.html|See the glMatrix api reference for details}
*
* @static
* @function
*/
const vec3 = _vec3;
/**
* vec2 - a standard glMatrix vec2 (constructor) function.
*
* {@link http://glmatrix.net/docs/module-vec2.html|See the glMatrix api reference for details}
*
* @static
* @function
*/
const vec2 = _vec2;
/**
* Range helper function
*
* @static
* @function
* @param {...number} limit - A single integer x, giving the range [0, ..., x - 1], or two integers x, y, giving the range [x, ..., y - 1]
* @return {number[]} range - The requested 1d range
*
* @example
* const a = range(10);
* // a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
* const b = range(4, 8);
* // b = [4, 5, 6, 7]
*/
const range = _range;
/**
* 2d range helper function
*
* @static
* @function
* @param {...number} limit - One integer xy, giving the range [[0,0], ..., [xy-1, xy-1]],
* or two integers x, y, giving the range [[0,0], ..., [x-1, y-1]],
* or four integers x, x2, y, y2, giving the range [[x1, y1], ..., [x2-1, y2-1]]
* @return {number[][]} range - The requested 2d range
*
* @example
* const a = range2d(2);
* // a = [[0,0],[0,1],[1,0],[1,1]]
* const b = range2d(3, 4);
* // b = [[0,0],[0,1],[0,2],[0,3],[1,0],[1,1],[1,2],[1,3],[2,0],[2,1],[2,2],[2,3]]
* const c = range2d(2, 4, 0, 2);
* // c = [[2,0],[2,1],[3,0],[3,1]]
*/
const range2d = _range2d;
/**
* 3d range helper function
*
* @static
* @function
* @param {...number} limit - Three integers, x, y, z, giving the range [[0,0,0], ..., [x-1, y-1, z-1]],
* or six integers, x, x2, y, y2, z, z2, giving the range [[x,y,z], ..., [x2-1, y2-1, z2-1]].
* @return {number[][]} range - The requested 3d range
*
* @example
* const a = range2d(2, 2, 2);
* // a = [[0,0,0],[0,0,1],[0,1,0],[0,1,1],[1,0,0],[1,0,1],[1,1,0],[1,1,1]]
* const b = range3d(0, 2, 0, 4, 0, 2);
* // b = [[2,2,0],[2,2,1],[2,3,0],[2,3,1],[3,2,0],[3,2,1],[3,3,0],[3,3,1]]
*/
const range3d = _range3d;
/**
* Subtract on range from another. Note that the ranges used must have the same dimensions.
*
* @static
* @function
* @param {number[]|range[][]|range[][][]} rangeA - The range to subtract from
* @param {number[]|range[][]|range[][][]} rangeB - The range to subtract
*
* @example
* const a = subRange(range2d(5), range2d(2, 3));
* // a = [[0,3],[0,4],[1,3],[1,4],[2,0],[2,1],[2,2],[2,3],[2,4],[3,0],[3,1],[3,2],[3,3],[3,4],[4,0],[4,1],[4,2],[4,3],[4,4]]
*/
const subRange = _subRange;
/**
* Format a float value for inclusion in glsl source code. Useful
* when rendering textures dynamically etc. See example {@link module:textures.texture|here}.
*
* @static
* @function
* @param {number} n - The number to format
*/
const glslFloat = _glslFloat;
// const normedColor = _normedColor;
/**
* Format a color for inclusion in glsl source code
* (i.e. return a normalized triplet of values in the range 0 - 1 encoded as a string: '0.34, 0.1, 0.1').
* Useful when rendering textures dynamically etc.
* See example {@link module:textures.texture|here}.
*
* @static
* @function
* @param {string} color - A hex rgb color value ('#ffffff')
* @return {string} - A normalized triplet of values in the range 0 - 1 as a string, i.e. '0.34, 0.1, 0.1'
*/
const normedColorStr = _normedColorStr;
/**
* Convert degress to radians.
*
* @static
* @function
* @param {number} theta - The angle in degrees
* @return {number} - The angle in radians
*/
const degToRad = _degToRad;
/**
* Convert radians to degrees.
*
* @static
* @function
* @param {number} theta - The angle in radians
* @return {number} - The angle in degrees
*/
const radToDeg = _radToDeg;
/**
* Clamp a number
*
* @static
* @function
* @param {number} value - The value
* @param {number} min - Minimum to clamp to
* @param {number} max - Maximum to clamp to
* @return {number} - The clamped value
*/
const clamp = _clamp;
/**
* Perform a linear interpolation between value1 and value2 using amount to weight between them.
*
* @static
* @function
* @param {number} value1 - First value
* @param {number} value2 - Second value
* @param {number} amount - The weight
*
* @return {number} - The interpolated value
*/
const lerp = _lerp;
/**
* Blend edges in a 3d array (useful for tiling volume textures).
* Read more {@link https://www.gamedev.net/forums/topic/678392-how-do-i-create-tileable-3d-perlinsimplex-noise/|here}.
* See example {@link module:textures.volumeTexture|here}
*
* @static
* @function
* @param {number[][][]} data - The data to tile in 3d
* @param {number} size - The cubic size of the volume to tile
*
* @return {number} - The interpolated value
*/
const tileSeamless3d = _tileSeamless3d;
let cachedSrc = null;
let transpiledSrc = null;
const dynamicScene = async (src, randomSeed) => {
randomSeed = randomSeed || Math.random()*100000;
/**
* @module random
* @description These deterministic random utils made available in the scene cope are primed with the seed value
* set in the retrace editor. Thus, using the same seed while using these functions
* will produce the same "random" result. Perfect for generative work as you can easily recreate
* a generated result.
*
* An instance of {@link https://www.npmjs.com/package/simplex-noise|simplex-noise.js},
* primed with the scene seed, is also available. See example below
*
* @property {Object} simplex - The simplex js object
*
* @example
* simplex.noise3D(4, 6, 1); // 0.10787818930041222
*/
const simplex = new SimplexNoise(randomSeed);
/**
* Return a random float value within a given range.
* Determinstic based on the scenes random seed.
*
* @module random
* @static
* @function
* @param {...number} limit - One argument for a random value in the range [0, limit],
* two arguments for the range [limit1, limit2].
* @return {number} - The random value
* @example
* random(10); // 2.749887186359643
* random(5, 10); // 9.724547372363647
*/
const random = _random(randomSeed);
/**
* Return a random integer value within a given range.
* Determinstic based on the scenes random seed.
*
* @module random
* @static
* @function randomInt
* @param {...number} limit - One argument for a random value in the range [0, limit],
* two arguments for the range [limit1, limit2].
* @return {number} - The random value
* @example
* random(10); // 2
* random(5, 10); // 9
*/
const randomInt = _randomInt(randomSeed);
/**
* Return true or false, randomly.
* Determinstic based on the scenes random seed.
*
* @module random
* @static
* @function randomBool
* @return {bool} - The random boolean
*/
const randomBool = _randomBool(randomSeed);
/**
* Return 1 or -1, randomly.
* Determinstic based on the scenes random seed.
*
* @module random
* @static
* @function randomSign
* @return {integer} - The signed integer
*/
const randomSign = _randomSign(randomSeed);
/**
* Maybe run a function given a probability value.
* Determinstic based on the scenes random seed.
*
* @module random
* @static
* @function maybe
* @param {function} cb - The callback to run
* @param {number} p - Probability that the function will be run (0 - 1).
*/
const maybe = _maybe(randomSeed);
/**
* Pluck a value from a given array, randomly.
* Determinstic based on the scenes random seed.
*
* @module random
* @static
* @function pluckRandom
* @param {array} arr - The array
* @return {any} - The plucked value
*/
const pluckRandom = _pluckRandom(randomSeed);
/**
* Pluck n values from a given array, randomly.
* Determinstic based on the scenes random seed.
*
* @module random
* @static
* @function takeRandom
* @param {array} arr - The array
* @param {number} n - Number of elements
* @return {any} - The plucked value
*/
const takeRandom = _takeRandom(randomSeed);
if(cachedSrc != src) {
transpiledSrc = babel.transformSync(src, {
presets: [babelEnvPreset],
plugins: [
[babelDecoratorPlugin, {legacy: true}],
babelObjSpreadPlugin,
[babelPipesPlugin, {proposal: 'smart'}]
]
});
cachedSrc = src;
}
return eval(transpiledSrc.code);
}
export default dynamicScene;