pex-renderer
Version:
Physically Based Renderer (PBR) and scene graph designed as ECS for PEX: define entities to be rendered as collections of components with their update orchestrated by systems.
155 lines (133 loc) • 4.65 kB
JavaScript
import * as systems from "./systems/index.js";
import createRenderGraph from "./render-graph.js";
import createResourceCache from "./resource-cache.js";
import { getDefaultViewport } from "./utils.js";
export default ({ ctx, debug = false }) => {
const renderGraph = createRenderGraph(ctx);
const resourceCache = createResourceCache(ctx);
const options = { ctx, resourceCache, renderGraph };
const animationSystem = systems.animation();
const skinSystem = systems.skin();
const geometrySystem = systems.geometry(options);
const morphSystem = systems.morph();
const transformSystem = systems.transform();
const layerSystem = systems.layer();
const skyboxSystem = systems.skybox(options);
const cameraSystem = systems.camera();
const reflectionProbeSystem = systems.reflectionProbe(options);
const lightSystem = systems.light();
const renderPipelineSystem = systems.renderPipeline(options);
const standardRendererSystem = systems.renderer.standard(options);
const lineRendererSystem = systems.renderer.line(options);
const skyboxRendererSystem = systems.renderer.skybox(options);
const helperRendererSystem = systems.renderer.helper(options);
const renderEngine = {
// debugMode,
debug(enabled) {
for (let i = 0; i < this.systems.length; i++) {
this.systems[i].debug = enabled;
}
for (let i = 0; i < this.renderers.length; i++) {
this.renderers[i].debug = enabled;
}
this.debugMode = enabled;
},
time: 0,
deltaTime: 0,
_prevTime: performance.now(),
renderGraph,
resourceCache,
systems: [
animationSystem,
skinSystem,
geometrySystem,
morphSystem,
transformSystem,
layerSystem,
skyboxSystem,
cameraSystem,
reflectionProbeSystem,
lightSystem,
renderPipelineSystem,
],
renderers: [
standardRendererSystem,
lineRendererSystem,
skyboxRendererSystem,
helperRendererSystem,
],
update(entities, deltaTime) {
const now = performance.now();
this.deltaTime = deltaTime || (now - this._prevTime) / 1000;
this._prevTime = now;
this.time += this.deltaTime;
animationSystem.update(entities, this);
skinSystem.update(entities);
geometrySystem.update(entities);
morphSystem.update(entities);
transformSystem.update(entities);
layerSystem.update(entities);
skyboxSystem.update(entities);
cameraSystem.update(entities);
for (let i = 0; i < this.renderers.length; i++) {
this.renderers[i].update(entities, this);
}
},
render(entities, cameraEntities, options = {}) {
resourceCache.beginFrame();
renderGraph.beginFrame();
if (!Array.isArray(cameraEntities)) cameraEntities = [cameraEntities];
const framebufferTexturesPerCamera = cameraEntities.map(
(cameraEntity) => {
// Set render view
const viewport =
cameraEntity.camera.viewport || getDefaultViewport(ctx);
const aspect = viewport[2] / viewport[3];
if (aspect !== cameraEntity.camera.aspect) {
cameraEntity.camera.aspect = aspect;
cameraEntity.camera.dirty = true;
}
const renderView = {
camera: cameraEntity.camera,
cameraEntity,
viewport,
};
const entitiesForCamera = cameraEntity.layer
? entities.filter(
(entity) => !entity.layer || entity.layer == cameraEntity.layer,
)
: entities;
// Update camera dependent systems
reflectionProbeSystem.update(entitiesForCamera, {
renderers: [skyboxRendererSystem],
});
lightSystem.update(entitiesForCamera);
const framebufferTextures = renderPipelineSystem.update(
entitiesForCamera,
{
time: options.time ?? this.time,
renderers: options.renderers || this.renderers,
renderView,
drawToScreen: options.drawToScreen,
},
);
return framebufferTextures;
},
);
renderGraph.endFrame();
resourceCache.endFrame();
return framebufferTexturesPerCamera;
},
dispose(entities) {
for (let i = 0; i < this.systems.length; i++) {
this.systems[i].dispose?.(entities);
}
for (let i = 0; i < this.renderers.length; i++) {
this.renderers[i].dispose?.(entities);
}
resourceCache.dispose();
},
};
renderEngine.debug(debug);
return renderEngine;
};