Add main function for animations
This commit is contained in:
parent
e5423212c3
commit
a15cb9e19c
3 changed files with 105 additions and 45 deletions
|
|
@ -10,13 +10,18 @@ The motivation behind this library and more in-depth information about the rende
|
||||||
- [More complex example](https://github.com/schmelczerandras/sdf-2d-more-complex-example)
|
- [More complex example](https://github.com/schmelczerandras/sdf-2d-more-complex-example)
|
||||||
- [Source code of the demo](https://github.com/schmelczerandras/sdf-2d-demo)
|
- [Source code of the demo](https://github.com/schmelczerandras/sdf-2d-demo)
|
||||||
|
|
||||||
## Usage
|
## Usage (1st option)
|
||||||
|
|
||||||
- To start using cutting-edge 2D graphics, first you have get a renderer instance. This is possible by calling the [compile function](globals.html#compile).
|
- To start using cutting-edge 2D graphics, first you have get a renderer instance. This is possible by calling the [compile function](globals.html#compile).
|
||||||
- For this, some [DrawableDescriptors](interfaces/drawabledescriptor.html) has to be provided.
|
- For this, some [DrawableDescriptors](interfaces/drawabledescriptor.html) has to be provided.
|
||||||
- Optionally, default compile settings can overridden using [StartupSettings](interfaces/startupsettings.html).
|
- Optionally, default compile settings can overridden using [StartupSettings](interfaces/startupsettings.html).
|
||||||
- After acquiring a renderer, the drawing of objects can be started through the [Renderer](interfaces/renderer.html) interface.
|
- After acquiring a renderer, the drawing of objects can be started through the [Renderer](interfaces/renderer.html) interface.
|
||||||
|
|
||||||
|
## Usage (2nd option)
|
||||||
|
|
||||||
|
If you're planning on creating animated content, use the [runAnimation function](globals.html#run-animation) function to spare yourself from writing boilerplate code.
|
||||||
|
Further documentation on its usage is available in its [documentation](globals.html#run-animation).
|
||||||
|
|
||||||
## Extending drawables
|
## Extending drawables
|
||||||
|
|
||||||
> Iñigo Quilez has some great [2D SDF-s](https://iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm)
|
> Iñigo Quilez has some great [2D SDF-s](https://iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm)
|
||||||
|
|
|
||||||
50
src/main.ts
50
src/main.ts
|
|
@ -6,18 +6,12 @@
|
||||||
* @packageDocumentation
|
* @packageDocumentation
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { DrawableDescriptor } from './drawables/drawable-descriptor';
|
|
||||||
import { Insights } from './graphics/rendering/insights';
|
|
||||||
import { ContextAwareRenderer } from './graphics/rendering/renderer/context-aware-renderer';
|
|
||||||
import { Renderer } from './graphics/rendering/renderer/renderer';
|
|
||||||
import { StartupSettings } from './graphics/rendering/settings/startup-settings';
|
|
||||||
import { applyArrayPlugins } from './helper/array';
|
import { applyArrayPlugins } from './helper/array';
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Array<T> {
|
interface Array<T> {
|
||||||
x: number;
|
x: T;
|
||||||
y: number;
|
y: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Float32Array {
|
interface Float32Array {
|
||||||
|
|
@ -28,47 +22,13 @@ declare global {
|
||||||
|
|
||||||
applyArrayPlugins();
|
applyArrayPlugins();
|
||||||
|
|
||||||
/**
|
export * from './compile';
|
||||||
* Compiles a new renderer instance. There can multiple renderers on a single page.
|
|
||||||
* > Asynchronous behaviour is required for parallel shader compiling.
|
|
||||||
* > Trying to draw before the returned promise resolves, results in no action taken.
|
|
||||||
* > Settings can be set before promise resolution and they will be applied later.
|
|
||||||
*
|
|
||||||
* The descriptors of every to-be-drawn objects are required before creating the renderer,
|
|
||||||
* allowing the compiler to only create the shaders that will actually be used.
|
|
||||||
*
|
|
||||||
* Example usage:
|
|
||||||
*
|
|
||||||
* ```js
|
|
||||||
* import { compile, hsl, CircleFactory, CircleLight } from 'sdf-2d';
|
|
||||||
*
|
|
||||||
* const canvas = document.querySelector('canvas');
|
|
||||||
* const Circle = CircleFactory(hsl(30, 66, 50));
|
|
||||||
* const renderer = await compile(canvas, [Circle.descriptor, CircleLight.descriptor]);
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* @param canvas The returned renderer will only be able to draw to this canvas.
|
|
||||||
* @param descriptors The descriptor of every single object (and light) that
|
|
||||||
* ever needs to be drawn by this renderer has to be given before compiling.
|
|
||||||
* @param settingsOverrides Sensible defaults are provided, but these can be overridden.
|
|
||||||
*/
|
|
||||||
export async function compile(
|
|
||||||
canvas: HTMLCanvasElement,
|
|
||||||
descriptors: Array<DrawableDescriptor>,
|
|
||||||
settingsOverrides: Partial<StartupSettings> = {}
|
|
||||||
): Promise<Renderer> {
|
|
||||||
return Insights.measureFunction('startup', async () => {
|
|
||||||
const renderer = new ContextAwareRenderer(canvas, descriptors, settingsOverrides);
|
|
||||||
await renderer.initializedPromise;
|
|
||||||
return renderer;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export * from './drawables/drawable';
|
export * from './drawables/drawable';
|
||||||
export * from './drawables/drawable-descriptor';
|
export * from './drawables/drawable-descriptor';
|
||||||
export * from './drawables/lights/circle-light';
|
export * from './drawables/lights/circle-light';
|
||||||
export * from './drawables/lights/flashlight';
|
export * from './drawables/lights/flashlight';
|
||||||
export * from './drawables/shapes/circle-factory';
|
export * from './drawables/shapes/circle-factory';
|
||||||
|
export * from './drawables/shapes/colorful-circle';
|
||||||
export * from './drawables/shapes/droplet-factory';
|
export * from './drawables/shapes/droplet-factory';
|
||||||
export * from './drawables/shapes/inverted-tunnel-factory';
|
export * from './drawables/shapes/inverted-tunnel-factory';
|
||||||
export * from './drawables/shapes/meta-circle-factory';
|
export * from './drawables/shapes/meta-circle-factory';
|
||||||
|
|
@ -84,3 +44,5 @@ export * from './helper/colors/rgb';
|
||||||
export * from './helper/colors/rgb255';
|
export * from './helper/colors/rgb255';
|
||||||
export * from './helper/colors/rgba';
|
export * from './helper/colors/rgba';
|
||||||
export * from './helper/colors/rgba255';
|
export * from './helper/colors/rgba255';
|
||||||
|
export * from './helper/delta-time-calculator';
|
||||||
|
export * from './run-animation';
|
||||||
|
|
|
||||||
93
src/run-animation.ts
Normal file
93
src/run-animation.ts
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
import { FpsQualityAutoscaler } from './graphics/rendering/fps-quality-autoscaler';
|
||||||
|
import { ContextAwareRenderer } from './graphics/rendering/renderer/context-aware-renderer';
|
||||||
|
import { RuntimeSettings } from './graphics/rendering/settings/runtime-settings';
|
||||||
|
import { StartupSettings } from './graphics/rendering/settings/startup-settings';
|
||||||
|
import { DeltaTimeCalculator } from './helper/delta-time-calculator';
|
||||||
|
import { DrawableDescriptor, Renderer } from './main';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements the boilerplate code required to run real-time animations
|
||||||
|
* in the browser. An FPS based autoscaler is also used. This creates an additional `fps`
|
||||||
|
* key in the renderers `insights` property.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <canvas id="main" style="width: 300px; height: 150px"></canvas>
|
||||||
|
* ```
|
||||||
|
* > The canvas needs to have a fixed size specified by CSS.
|
||||||
|
|
||||||
|
* ```js
|
||||||
|
* import { CircleFactory, CircleLight, hsl, runAnimation } from 'sdf-2d';
|
||||||
|
*
|
||||||
|
* const canvas = document.querySelector('canvas');
|
||||||
|
* const Circle = CircleFactory(hsl(180, 100, 40));
|
||||||
|
*
|
||||||
|
* runAnimation(canvas, [Circle.descriptor, CircleLight.descriptor], (renderer, time) => {
|
||||||
|
* renderer.addDrawable(
|
||||||
|
* new Circle([150 + 50 * Math.cos(time / 1000), 75 + 50 * Math.sin(time / 1000)], 25)
|
||||||
|
* );
|
||||||
|
* renderer.addDrawable(new CircleLight([150, 75], hsl(270, 100, 40), 0.1));
|
||||||
|
* return true;
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param canvas The returned renderer will only be able to draw to this canvas.
|
||||||
|
* @param descriptors The descriptor of every single object (and light) that
|
||||||
|
* ever needs to be drawn by this renderer has to be given before compiling.
|
||||||
|
* @param animate This function will be called before rendering each frame.
|
||||||
|
* `renderDrawables` must not be called by the animate function. It should return `true`
|
||||||
|
* if the animation should be continued. To break out of the animation loop, a `false` (falsy)
|
||||||
|
* return value must be given.
|
||||||
|
* @param startupSettingOverrides Sensible defaults are provided, but these can be overridden.
|
||||||
|
* @param initialRuntimeSettingOverrides Sensible defaults are provided, but these can be overridden.
|
||||||
|
*/
|
||||||
|
export async function runAnimation(
|
||||||
|
canvas: HTMLCanvasElement,
|
||||||
|
descriptors: Array<DrawableDescriptor>,
|
||||||
|
animate: (
|
||||||
|
renderer: Renderer,
|
||||||
|
currentTimeInMilliseconds: DOMHighResTimeStamp,
|
||||||
|
deltaTimeInMilliseconds: DOMHighResTimeStamp
|
||||||
|
) => boolean,
|
||||||
|
startupSettingOverrides: Partial<StartupSettings> = {},
|
||||||
|
initialRuntimeSettingOverrides: Partial<RuntimeSettings> = {}
|
||||||
|
): Promise<void> {
|
||||||
|
const renderer = new ContextAwareRenderer(canvas, descriptors, startupSettingOverrides);
|
||||||
|
|
||||||
|
const deltaTimeCalculator = new DeltaTimeCalculator();
|
||||||
|
let triggerIsOver: () => void;
|
||||||
|
const isOver = new Promise((resolve) => (triggerIsOver = resolve));
|
||||||
|
renderer.setRuntimeSettings(initialRuntimeSettingOverrides);
|
||||||
|
const autoscaler = new FpsQualityAutoscaler(renderer);
|
||||||
|
|
||||||
|
await renderer.initializedPromise;
|
||||||
|
|
||||||
|
let startTime: DOMHighResTimeStamp | null = null;
|
||||||
|
const handleFrame = (currentTime: DOMHighResTimeStamp) => {
|
||||||
|
if (startTime === null) {
|
||||||
|
startTime = currentTime;
|
||||||
|
}
|
||||||
|
const deltaTime = deltaTimeCalculator.getNextDeltaTime(currentTime);
|
||||||
|
autoscaler.addDeltaTime(deltaTime);
|
||||||
|
|
||||||
|
if (renderer.insights) {
|
||||||
|
renderer.insights.fps = autoscaler.FPS;
|
||||||
|
}
|
||||||
|
|
||||||
|
const shouldStop = animate(renderer, currentTime - startTime, deltaTime);
|
||||||
|
renderer.renderDrawables();
|
||||||
|
|
||||||
|
if (!shouldStop) {
|
||||||
|
triggerIsOver();
|
||||||
|
} else {
|
||||||
|
requestAnimationFrame(handleFrame);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
requestAnimationFrame(handleFrame);
|
||||||
|
|
||||||
|
await isOver;
|
||||||
|
deltaTimeCalculator.destroy();
|
||||||
|
renderer.destroy();
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue