oxygen-core
Version:
Oxygen game engine (Xenon Core for browsers)
1,333 lines (1,175 loc) • 80.3 kB
HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<base data-ice="baseUrl" href="../../../">
<title data-ice="title">src/systems/RenderSystem.js | oxygen-core</title>
<link type="text/css" rel="stylesheet" href="css/style.css">
<link type="text/css" rel="stylesheet" href="css/prettify-tomorrow.css">
<script src="script/prettify/prettify.js"></script>
<script src="script/manual.js"></script>
<meta name="description" content="Oxygen game engine (Xenon Core for browsers)"><meta property="twitter:card" content="summary"><meta property="twitter:title" content="oxygen-core"><meta property="twitter:description" content="Oxygen game engine (Xenon Core for browsers)"></head>
<body class="layout-container" data-ice="rootContainer">
<header>
<a href="./">Home</a>
<a href="identifiers.html">Reference</a>
<a href="source.html">Source</a>
<div class="search-box">
<span>
<img src="./image/search.png">
<span class="search-input-edge"></span><input class="search-input"><span class="search-input-edge"></span>
</span>
<ul class="search-result"></ul>
</div>
<a style="position:relative; top:3px;" href="https://github.com/PsichiX/Oxygen"><img width="20px" src="./image/github.png"></a></header>
<nav class="navigation" data-ice="nav"><div>
<ul>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/index.js~EventsController.html">EventsController</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-function">F</span><span data-ice="name"><span><a href="function/index.html#static-function-lazyInitialization">lazyInitialization</a></span></span></li>
<li data-ice="doc"><a data-ice="dirPath" class="nav-dir-path" href="identifiers.html#asset-loaders">asset-loaders</a><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/asset-loaders/AssemblyAsset.js~AssemblyAsset.html">AssemblyAsset</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/asset-loaders/AtlasAsset.js~AtlasAsset.html">AtlasAsset</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/asset-loaders/BinaryAsset.js~BinaryAsset.html">BinaryAsset</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/asset-loaders/FontAsset.js~FontAsset.html">FontAsset</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/asset-loaders/ImageAsset.js~ImageAsset.html">ImageAsset</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/asset-loaders/JSONAsset.js~JSONAsset.html">JSONAsset</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/asset-loaders/MusicAsset.js~MusicAsset.html">MusicAsset</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/asset-loaders/PackAsset.js~PackAsset.html">PackAsset</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/asset-loaders/ParticleSystemAsset.js~ParticleSystemAsset.html">ParticleSystemAsset</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/asset-loaders/PostprocessRackEffectAsset.js~PostprocessRackEffectAsset.html">PostprocessRackEffectAsset</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/asset-loaders/SVGAsset.js~SVGAsset.html">SVGAsset</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/asset-loaders/SceneAsset.js~SceneAsset.html">SceneAsset</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/asset-loaders/SetAsset.js~SetAsset.html">SetAsset</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/asset-loaders/ShaderAsset.js~ShaderAsset.html">ShaderAsset</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/asset-loaders/SkeletonAsset.js~SkeletonAsset.html">SkeletonAsset</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/asset-loaders/SoundAsset.js~SoundAsset.html">SoundAsset</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/asset-loaders/TextAsset.js~TextAsset.html">TextAsset</a></span></span></li>
<li data-ice="doc"><a data-ice="dirPath" class="nav-dir-path" href="identifiers.html#components">components</a><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/components/AtlasSprite.js~AtlasSprite.html">AtlasSprite</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/components/Camera.js~Camera.html">Camera</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/components/Camera.js~PostprocessPass.html">PostprocessPass</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/components/Camera2D.js~Camera2D.html">Camera2D</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/components/CameraDirector2D.js~CameraDirector2D.html">CameraDirector2D</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/components/CircleShape.js~CircleShape.html">CircleShape</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/components/Container.js~Container.html">Container</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/components/DeferredRenderer.js~DeferredPipeline.html">DeferredPipeline</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/components/DeferredRenderer.js~DeferredRenderer.html">DeferredRenderer</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/components/GestureListener.js~GestureListener.html">GestureListener</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/components/InputHandler.js~InputHandler.html">InputHandler</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/components/MultipassRenderer.js~MultipassPipeline.html">MultipassPipeline</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/components/MultipassRenderer.js~MultipassRenderer.html">MultipassRenderer</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/components/Particles.js~Particles.html">Particles</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/components/PhysicsBody.js~PhysicsBody.html">PhysicsBody</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/components/PhysicsWorld.js~PhysicsWorld.html">PhysicsWorld</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/components/PolygonShape.js~PolygonShape.html">PolygonShape</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/components/PostprocessRack.js~PostprocessRack.html">PostprocessRack</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/components/PostprocessRack.js~PostprocessRackPass.html">PostprocessRackPass</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/components/PostprocessRack.js~PostprocessRackRawEffectPass.html">PostprocessRackRawEffectPass</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/components/PrefabInstance.js~PrefabInstance.html">PrefabInstance</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/components/RectangleRenderer.js~RectangleRenderer.html">RectangleRenderer</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/components/RectangleShape.js~RectangleShape.html">RectangleShape</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/components/Script.js~Script.html">Script</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/components/Shape.js~Shape.html">Shape</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/components/Skeleton.js~Skeleton.html">Skeleton</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/components/SortedActions.js~SortedActions.html">SortedActions</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/components/Sprite.js~Sprite.html">Sprite</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/components/TextRenderer.js~TextRenderer.html">TextRenderer</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/components/UiLayout.js~UiLayout.html">UiLayout</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/components/UiSprite.js~UiSprite.html">UiSprite</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/components/VerticesRenderer.js~VerticesRenderer.html">VerticesRenderer</a></span></span></li>
<li data-ice="doc"><a data-ice="dirPath" class="nav-dir-path" href="identifiers.html#systems">systems</a><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/systems/AssemblySystem.js~AssemblySystem.html">AssemblySystem</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/systems/AudioSystem.js~AudioSystem.html">AudioSystem</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/systems/InputSystem.js~InputSystem.html">InputSystem</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/systems/RenderSystem.js~Command.html">Command</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/systems/RenderSystem.js~Pipeline.html">Pipeline</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/systems/RenderSystem.js~RenderFullscreenCommand.html">RenderFullscreenCommand</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/systems/RenderSystem.js~RenderSystem.html">RenderSystem</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/systems/RenderSystem.js~RenderTargetWrapper.html">RenderTargetWrapper</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/systems/StorageSystem.js~StorageSystem.html">StorageSystem</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/systems/System.js~System.html">System</a></span></span></li>
<li data-ice="doc"><a data-ice="dirPath" class="nav-dir-path" href="identifiers.html#systems-assetsystem">systems/AssetSystem</a><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/systems/AssetSystem/Asset.js~Asset.html">Asset</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/systems/AssetSystem/index.js~AssetSystem.html">AssetSystem</a></span></span></li>
<li data-ice="doc"><a data-ice="dirPath" class="nav-dir-path" href="identifiers.html#systems-entitysystem">systems/EntitySystem</a><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/systems/EntitySystem/Component.js~Component.html">Component</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/systems/EntitySystem/Entity.js~Entity.html">Entity</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/systems/EntitySystem/index.js~EntitySystem.html">EntitySystem</a></span></span></li>
<li data-ice="doc"><a data-ice="dirPath" class="nav-dir-path" href="identifiers.html#utils">utils</a><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/src/utils/Events.js~Events.html">Events</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-function">F</span><span data-ice="name"><span><a href="function/index.html#static-function-angleDifference">angleDifference</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-function">F</span><span data-ice="name"><span><a href="function/index.html#static-function-bezierCubic">bezierCubic</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-function">F</span><span data-ice="name"><span><a href="function/index.html#static-function-convertGlobalPointToLocalPoint">convertGlobalPointToLocalPoint</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-function">F</span><span data-ice="name"><span><a href="function/index.html#static-function-convertLocalPointToGlobalPoint">convertLocalPointToGlobalPoint</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-function">F</span><span data-ice="name"><span><a href="function/index.html#static-function-findMapKeyOfValue">findMapKeyOfValue</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-function">F</span><span data-ice="name"><span><a href="function/index.html#static-function-getMipmapScale">getMipmapScale</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-function">F</span><span data-ice="name"><span><a href="function/index.html#static-function-getPOT">getPOT</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-function">F</span><span data-ice="name"><span><a href="function/index.html#static-function-isGlobalPointInGlobalBoundingBox">isGlobalPointInGlobalBoundingBox</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-function">F</span><span data-ice="name"><span><a href="function/index.html#static-function-isLocalPointInLocalBoundingBox">isLocalPointInLocalBoundingBox</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-function">F</span><span data-ice="name"><span><a href="function/index.html#static-function-isPOT">isPOT</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-function">F</span><span data-ice="name"><span><a href="function/index.html#static-function-propsEnumStringify">propsEnumStringify</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-function">F</span><span data-ice="name"><span><a href="function/index.html#static-function-stringToRGBA">stringToRGBA</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-function">F</span><span data-ice="name"><span><a href="function/index.html#static-function-waitForSeconds">waitForSeconds</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-variable">V</span><span data-ice="name"><span><a href="variable/index.html#static-variable-Box2D">Box2D</a></span></span></li>
</ul>
</div>
</nav>
<div class="content" data-ice="content"><h1 data-ice="title">src/systems/RenderSystem.js</h1>
<pre class="source-code line-number raw-source-code"><code class="prettyprint linenums" data-ice="content">import System from './System';
import Events from '../utils/Events';
import { vec2, vec3, vec4, mat2, mat3, mat4 } from '../utils/gl-matrix';
import { isPOT, getPOT, getMipmapScale } from '../utils';
import funcParser from '../utils/funcParser';
let rtwUidGenerator = 0;
const versions = [
// TODO: change order to provide fallback to previous versions if requested is not supported.
[1, 'webgl'],
[2, 'webgl2'],
];
const vertices = new Float32Array([
-1, -1, 0, 0,
1, -1, 1, 0,
1, 1, 1, 1,
-1, 1, 0, 1
]);
const indices = new Uint16Array([
0, 1, 2,
2, 3, 0
]);
const extensions = {
instanced_arrays: [
1, 'ANGLE_instanced_arrays', null,
2, null, null
],
blend_minmax: [
1, 'EXT_blend_minmax', null,
2, null, null
],
color_buffer_float: [
1, 'WEBGL_color_buffer_float', null,
2, 'EXT_color_buffer_float', null
],
color_buffer_half_float: [
1, 'WEBGL_color_buffer_half_float', null,
2, 'EXT_color_buffer_half_float', null
],
disjoint_timer_query: [
1, 'EXT_disjoint_timer_query', null,
2, 'EXT_disjoint_timer_query_webgl2', null
],
frag_depth: [
1, 'EXT_frag_depth', null,
2, null, null
],
sRGB: [
1, 'EXT_sRGB', null,
2, null, null
],
shader_texture_lod: [
1, 'EXT_shader_texture_lod', null,
2, null, null
],
texture_filter_anisotropic: [
1, 'EXT_texture_filter_anisotropic', null,
2, 'EXT_texture_filter_anisotropic', null
],
element_index_uint: [
1, 'OES_element_index_uint', null,
2, null, null
],
standard_derivatives: [
1, 'OES_standard_derivatives', null,
2, null, null
],
texture_float: [
1, 'OES_texture_float', null,
2, null, null
],
texture_float_linear: [
1, 'OES_texture_float_linear', null,
2, 'OES_texture_float_linear', null
],
texture_half_float: [
1, 'OES_texture_half_float', null,
2, null, null
],
texture_half_float_linear: [
1, 'OES_texture_half_float_linear', null,
2, 'OES_texture_half_float_linear', null
],
vertex_array_object: [
1, 'OES_vertex_array_object', null,
2, null, null
],
compressed_texture_astc: [
1, 'WEBGL_compressed_texture_astc', null,
2, 'WEBGL_compressed_texture_astc', null
],
compressed_texture_atc: [
1, 'WEBGL_compressed_texture_atc', null,
2, 'WEBGL_compressed_texture_atc', null
],
compressed_texture_etc: [
1, 'WEBGL_compressed_texture_etc', null,
2, 'WEBGL_compressed_texture_etc', null
],
compressed_texture_etc1: [
1, 'WEBGL_compressed_texture_etc1', null,
2, 'WEBGL_compressed_texture_etc1', null
],
compressed_texture_pvrtc: [
1, 'WEBGL_compressed_texture_pvrtc', null,
2, 'WEBGL_compressed_texture_pvrtc', null
],
compressed_texture_s3tc: [
1, 'WEBGL_compressed_texture_s3tc', null,
2, 'WEBGL_compressed_texture_s3tc', null
],
compressed_texture_s3tc_srgb: [
1, 'WEBGL_compressed_texture_s3tc_srgb', null,
2, 'WEBGL_compressed_texture_s3tc_srgb', null
],
debug_renderer_info: [
1, 'WEBGL_debug_renderer_info', null,
2, 'WEBGL_debug_renderer_info', null
],
debug_shaders: [
1, 'WEBGL_debug_shaders', null,
2, 'WEBGL_debug_shaders', null
],
depth_texture: [
1, 'WEBGL_depth_texture', null,
2, null, null
],
draw_buffers: [
1, 'WEBGL_draw_buffers', [
'drawBuffersWEBGL', 'drawBuffers'
],
2, null, null
],
lose_context: [
1, 'WEBGL_lose_context', null,
2, 'WEBGL_lose_context', null
]
};
const functions = new Map();
function getExtensionByVersion(meta, context, version) {
for (var i = 0, c = meta.length; i < c; i += 3) {
if (meta[i] === version) {
const name = meta[i + 1];
if (!name) {
return context;
} else {
const ext = context.getExtension(name);
if (!ext) {
return null;
}
const mappings = meta[i + 2];
if (!!mappings) {
for (var j = 0, n = mappings.length; j < n; j += 2) {
context[mappings[j + 1]] = ext[mappings[j]].bind(ext);
}
}
return ext;
}
}
}
return null;
}
function makeApplierFunction(code) {
code = funcParser.parse(code);
return new Function('location', 'gl', 'out', 'getValue', 'mat4', code);
}
export class RenderTargetWrapper {
get id() {
return this._id;
}
set id(value) {
if (!value) {
this._id = `#RenderTargetWrapper-rt-${++rtwUidGenerator}`;
this._dirty = true;
return;
}
if (typeof value !== 'string') {
throw new Error('`value` is not type of String!');
}
this._id = value;
this._dirty = true;
}
get width() {
return this._width;
}
set width(value) {
if (typeof value !== 'number') {
throw new Error('`value` is not type of Number!');
}
this._width = value;
this._dirty = true;
}
get height() {
return this._height;
}
set height(value) {
if (typeof value !== 'number') {
throw new Error('`value` is not type of Number!');
}
this._height = value;
this._dirty = true;
}
get level() {
return this._level;
}
set level(value) {
if (typeof value !== 'number') {
throw new Error('`value` is not type of Number!');
}
this._level = Math.max(0, value | 0);
this._dirty = true;
}
get potMode() {
return this._potMode;
}
set potMode(value) {
if (!value) {
this._potMode = null;
return;
}
if (typeof value !== 'string') {
throw new Error('`value` is not type of String!');
}
this._potMode = value;
this._dirty = true;
}
get floatPointData() {
return this._floatPointData;
}
set floatPointData(value) {
if (typeof value !== 'boolean') {
throw new Error('`value` is not type of Boolean!');
}
this._floatPointData = value;
this._dirty = true;
}
get pushPopMode() {
return this._pushPopMode;
}
set pushPopMode(value) {
if (typeof value !== 'boolean') {
throw new Error('`value` is not type of Boolean!');
}
this._pushPopMode = value;
}
get targets() {
return this._targets;
}
set targets(value) {
if (!value) {
this._targets = null;
return;
}
if (!Array.isArray(value)) {
throw new Error('`value` is not type of Array!');
}
this._targets = value;
this._dirty = true;
}
constructor() {
this._renderer = null;
this._id = `#RenderTargetWrapper-rt-${++rtwUidGenerator}`;
this._idUsed = null;
this._width = -1;
this._height = -1;
this._level = 0;
this._potMode = null;
this._floatPointData = false;
this._pushPopMode = false;
this._targets = null;
this._dirty = true;
}
dispose() {
const { _renderer, _idUsed } = this;
if (!!_renderer) {
if (!!_idUsed) {
_renderer.unregisterRenderTarget(_idUsed);
}
}
this._renderer = null;
this._id = null;
this._idUsed = null;
this._potMode = null;
this._targets = null;
}
enable(renderer) {
this._ensureState(renderer);
const { _idUsed } = this;
if (!!_idUsed) {
if (this._pushPopMode) {
renderer.pushRenderTarget(_idUsed);
} else {
renderer.enableRenderTarget(_idUsed);
}
}
}
disable() {
const { _renderer } = this;
if (!!_renderer) {
if (this._pushPopMode) {
_renderer.popRenderTarget();
} else {
_renderer.disableRenderTarget();
}
}
}
rebuild() {
this._dirty = true;
}
_ensureState(renderer) {
if (!this._dirty) {
return;
}
const { _id } = this;
if (!_id) {
throw new Error('`id` cannot be null!');
}
if (!!this._idUsed) {
renderer.unregisterRenderTarget(this._idUsed);
}
const { _potMode, _level, _width, _height } = this;
const width = _width < 0 ? renderer.canvas.width : _width;
const height = _height < 0 ? renderer.canvas.height : _height;
const w = !_potMode
? width
: getPOT(width, _potMode === 'upper');
const h = !_potMode
? height
: getPOT(height, _potMode === 'upper');
const s = getMipmapScale(_level);
this._idUsed = this._id;
if (targets === undefined || targets === null) {
renderer.registerRenderTarget(
this._idUsed,
(w * s) | 0,
(h * s) | 0,
this._floatPointData
);
} else {
renderer.registerRenderTargetMulti(
this._idUsed,
(w * s) | 0,
(h * s) | 0,
this._targets
);
}
this._renderer = renderer;
this._dirty = false;
}
}
/**
* Rendering command base class.
*/
export class Command {
/**
* Dispose (release all internal resources).
*
* @example
* command.dispose();
* command = null;
*/
dispose() {}
/**
* Called when command is executed.
*
* @abstract
* @param {WebGLRenderingContext} gl - WebGL context.
* @param {RenderSystem} renderer - Render system that is used to render.
* @param {number} deltaTime - Delta time.
* @param {string} layer - Layer id.
*/
onRender(gl, renderer, deltaTime, layer) {
throw new Error('Not implemented!');
}
/**
* Called on view resize.
*
* @param {number} width - Width.
* @param {number} height - Height.
*/
onResize(width, height) {}
}
/**
* Rendering pipeline base class.
* Pipeline is a set of commands to render at once.
*/
export class Pipeline extends Command {
get commands() {
return this._commands;
}
set commands(value) {
if (!value) {
this._commands = null;
return;
}
if (!Array.isArray(value)) {
throw new Error('`value` is not type of Array!');
}
for (const item of value) {
if (!(item instanceof Command)) {
throw new Error('One of `value` items is not type of Command!');
}
}
this._commands = value;
}
/**
* Constructor.
*/
constructor(commands = null) {
super();
this.commands = commands;
}
/**
* @override
*/
dispose() {
const { _commands } = this;
if (!!_commands) {
for (const command of _commands) {
command.dispose();
}
this._commands = null;
}
}
/**
* @override
*/
onRender(gl, renderer, deltaTime, layer) {
const { _commands } = this;
if (!!_commands) {
for (const command of _commands) {
command.onRender(gl, renderer, deltaTime, layer);
}
}
}
/**
* @override
*/
onResize(width, height) {
const { _commands } = this;
if (!!_commands) {
for (const command of _commands) {
command.onResize(width, height);
}
}
}
}
/**
* Command to render fullscreen image with given shader.
*/
export class RenderFullscreenCommand extends Command {
/** @type {string|null} */
get shader() {
return this._shader;
}
/** @type {string|null} */
set shader(value) {
if (!value) {
this._shader = null;
return;
}
if (typeof value !== 'string') {
throw new Error('`value` is not type of String!');
}
this._shader = value;
}
/** @type {*} */
get overrideUniforms() {
return this._overrideUniforms;
}
/** @type {*} */
get overrideSamplers() {
return this._overrideSamplers;
}
/**
* Constructor.
*/
constructor() {
super();
this._context = null;
this._vertexBuffer = null;
this._indexBuffer = null;
this._shader = null;
this._overrideUniforms = new Map();
this._overrideSamplers = new Map();
this._dirty = true;
}
/**
* Destructor (dispose internal resources).
*
* @example
* command.dispose();
* pass = null;
*/
dispose() {
const { _context, _vertexBuffer, _indexBuffer } = this;
if (!!_context) {
if (!!_vertexBuffer) {
_context.deleteBuffer(_vertexBuffer);
}
if (!!_indexBuffer) {
_context.deleteBuffer(_indexBuffer);
}
}
this._overrideUniforms.clear();
this._overrideSamplers.clear();
this._context = null;
this._vertexBuffer = null;
this._indexBuffer = null;
this._shader = null;
this._overrideUniforms = null;
this._overrideSamplers = null;
}
/**
* Called when camera need to postprocess it's rendered image.
*
* @param {WebGLRenderingContext} gl - WebGL context.
* @param {RenderSystem} renderer - Render system that is used to render.
* @param {number} deltaTime - Delta time.
* @param {string|null} layer - Layer ID.
*/
onRender(gl, renderer, deltaTime, layer) {
const {
_shader,
_overrideUniforms,
_overrideSamplers
} = this;
if (!_shader) {
console.warn('Trying to render PostprocessPass without shader!');
return;
}
this._ensureState(gl, renderer);
gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indexBuffer);
if (this._dirty) {
this._dirty = false;
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
}
renderer.enableShader(_shader);
if (_overrideUniforms.size > 0) {
for (const [ name, value ] of _overrideUniforms) {
renderer.overrideShaderUniform(name, value);
}
}
if (_overrideSamplers.size > 0) {
for (const [ name, { texture, filtering } ] of _overrideSamplers) {
if (texture !== '') {
renderer.overrideShaderSampler(name, texture, filtering);
}
}
}
gl.drawElements(
gl.TRIANGLES,
indices.length,
gl.UNSIGNED_SHORT,
0
);
renderer.disableShader();
}
_ensureState(gl, renderer) {
this._context = gl;
if (!this._vertexBuffer) {
this._vertexBuffer = gl.createBuffer();
this._dirty = true;
}
if (!this._indexBuffer) {
this._indexBuffer = gl.createBuffer();
this._dirty = true;
}
}
}
/**
* Rendering graphics onto screen canvas.
*
* @example
* const system = new RenderSystem('screen-0');
*/
export default class RenderSystem extends System {
/** @type {*} */
static get propsTypes() {
return {
useDevicePixelRatio: 'boolean',
timeScale: 'number',
collectStats: 'boolean'
};
}
/** @type {number} */
get contextVersion() {
return this._contextVersion;
}
/** @type {boolean} */
get useDevicePixelRatio() {
return this._useDevicePixelRatio;
}
/** @type {boolean} */
set useDevicePixelRatio(value) {
this._useDevicePixelRatio = !!value;
}
/** @type {number} */
get timeScale() {
return this._timeScale;
}
/** @type {number} */
set timeScale(value) {
if (typeof value !== 'number') {
throw new Error('`value` is not type of Number!');
}
this._timeScale = value;
}
/** @type {boolean} */
get collectStats() {
return this._collectStats;
}
/** @type {boolean} */
set collectStats(value) {
if (typeof value !== 'boolean') {
throw new Error('`value` is not type of Boolean!');
}
this._collectStats = value;
}
/** @type {number} */
get passedTime() {
return this._passedTime;
}
/** @type {HTMLCanvasElement} */
get canvas() {
return this._canvas;
}
/** @type {Events} */
get events() {
return this._events;
}
/** @type {string} */
get activeShader() {
return this._activeShader;
}
/** @type {string} */
get activeRenderTarget() {
return this._activeRenderTarget;
}
/** @type {string} */
get clearColor() {
return this._clearColor;
}
/** @type {mat4} */
get projectionMatrix() {
return this._projectionMatrix;
}
/** @type {mat4} */
get viewMatrix() {
return this._viewMatrix;
}
/** @type {mat4} */
get modelMatrix() {
return this._modelMatrix;
}
/** @type {Map} */
get stats() {
return this._stats;
}
/** @type {string} */
get statsText() {
if (!this._collectStats) {
return '';
}
const { _stats } = this;
const deltaTime = _stats.get('delta-time');
const passedTime = _stats.get('passed-time');
const fps = `FPS: ${(1000 / deltaTime) | 0} (${1000 / deltaTime})`;
const dt = `Delta time: ${deltaTime | 0} ms (${deltaTime})`;
const pt = `Passed time: ${passedTime | 0} ms (${passedTime})`;
const sc = `Shader changes: ${_stats.get('shader-changes')}`;
const f = `Frames: ${_stats.get('frames')}`;
const s = `Shaders: ${_stats.get('shaders')}`;
const t = `Textures: ${_stats.get('textures')}`;
const rt = `Render targets: ${_stats.get('renderTargets')}`;
const e = `Extensions:${_stats.get('extensions').map(e => `\n - ${e}`).join()}`;
return `${fps}\n${dt}\n${pt}\n${sc}\n${f}\n${s}\n${t}\n${rt}\n${e}`;
}
/**
* Constructor.
* Automaticaly binds into specified canvas.
*
* @param {string} canvas - HTML canvas element id.
* @param {boolean} optimize - Optimize rendering pipeline.
* @param {Array.<string>} extensions - array with WebGL extensions list.
* @param {number} contextVersion - WebGL context version number.
* @param {boolean} manualMode - Manually trigger rendering next frames.
*/
constructor(canvas, optimize = true, extensions = null, contextVersion = 1, manualMode = false) {
super();
this._manualMode = !!manualMode;
this._extensions = new Map();
this._contextVersion = contextVersion | 0;
this._useDevicePixelRatio = false;
this._timeScale = 1;
this._collectStats = false;
this._animationFrame = 0;
this._lastTimestamp = null;
this._canvas = null;
this._context = null;
this._shaders = new Map();
this._textures = new Map();
this._renderTargets = new Map();
this._renderTargetsStack = [];
this._events = new Events();
this._activeShader = null;
this._activeRenderTarget = null;
this._activeViewportSize = vec2.create();
this._clearColor = vec4.create();
this._projectionMatrix = mat4.create();
this._viewMatrix = mat4.create();
this._modelMatrix = mat4.create();
this._blendingConstants = {};
this._stats = new Map();
this._counterShaderChanges = 0;
this._counterFrames = 0;
this._optimize = !!optimize;
this._passedTime = 0;
this._shaderApplierOut = mat4.create();
this._shaderApplierGetValue = name => {
if (name === 'model-matrix') {
return this._modelMatrix;
} else if (name === 'view-matrix') {
return this._viewMatrix;
} else if (name === 'projection-matrix') {
return this._projectionMatrix;
} else {
throw new Error(`Unknown matrix: ${name}`);
}
};
this.__onFrame = this._onFrame.bind(this);
if (!!extensions) {
for (const name of extensions) {
this._extensions.set(name, null);
}
}
this._setup(canvas);
}
/**
* Destructor (disposes internal resources).
*
* @example
* system.dispose();
* sustem = null;
*/
dispose() {
const { _context, _shaders, _textures, _renderTargets, _events } = this;
this._stopAnimation();
_context.clear(_context.COLOR_BUFFER_BIT);
for (const shader of _shaders.keys()) {
this.unregisterShader(shader);
}
for (const texture of _textures.keys()) {
this.unregisterTexture(texture);
}
for (const renderTarget of _renderTargets.keys()) {
this.unregisterRenderTarget(renderTarget);
}
_events.dispose();
this._extensions = null;
this._lastTimestamp = null;
this._canvas = null;
this._context = null;
this._shaders = null;
this._textures = null;
this._renderTargets = null;
this._renderTargetsStack = null;
this._events = null;
this._activeShader = null;
this._activeRenderTarget = null;
this._activeViewportSize = null;
this._clearColor = null;
this._projectionMatrix = null;
this._viewMatrix = null;
this._modelMatrix = null;
this._blendingConstants = null;
this._stats = null;
this._shaderApplierOut = null;
this._shaderApplierGetValue = null;
this.__onFrame = null;
}
/**
* Get loaded WebGL extension by it's name.
*
* @param {string} name - Extension name.
*
* @return {*|null} WebGL extension or null if not found.
*
* @example
* const extension = system.extension('vertex_array_object');
* if (!!extension) {
* const vao = extension.createVertexArrayOES();
* extension.bindVertexArrayOES(vao);
* }
*/
extension(name) {
return this._extensions.get(name) || null;
}
/**
* Load WebGL extension by it's name.
*
* @param {string} name - Extension name.
*
* @return {*|null} WebGL extension or null if not supported.
*
* @example
* const extension = system.requestExtension('vertex_array_object');
* if (!!extension) {
* const vao = extension.createVertexArrayOES();
* extension.bindVertexArrayOES(vao);
* }
*/
requestExtension(name) {
const { _context, _contextVersion, _extensions } = this;
if (!_context) {
throw new Error('WebGL context is not yet ready!');
}
let ext = _extensions.get(name);
if (!!ext) {
return ext;
}
const meta = extensions[name];
if (!meta) {
throw new Error(`Unsupported extension: ${name}`);
}
ext = getExtensionByVersion(meta, _context, _contextVersion);
if (!!ext) {
_extensions.set(name, ext);
} else {
console.warn(`Could not get WebGL extension: ${name}`);
}
return ext || null;
}
/**
* Load WebGL extensions by their names.
*
* @param {string[]} args - Extension names.
*
* @return {boolean} True if all are supported and loaded, false otherwise.
*
* @example
* const supported = system.requestExtensions('texture_float', 'texture_float_linear');
* if (!supported) {
* throw new Error('One of requested WebGL extensions is not supported!');
* }
*/
requestExtensions(...args) {
for (const arg of args) {
if (!this.requestExtension(arg)) {
return false;
}
}
return true;
}
/**
* Execute rendering command.
*
* @param {Command} command - command to execute.
* @param {number} deltaTime - Delta time.
* @param {string|null} layer - Layer ID.
*/
executeCommand(command, deltaTime, layer) {
if (!(command instanceof Command)) {
throw new Error('`command` is not type of Command!');
}
if (typeof deltaTime !== 'number') {
throw new Error('`deltaTime` is not type of Number!');
}
command.onRender(this._context, this, deltaTime, layer);
}
/**
* Register new shader.
*
* @param {string} id - Shader id.
* @param {string} vertex - Vertex shader code.
* @param {string} fragment - Fragment shader code.
* @param {*} layoutInfo - Vertex layout description.
* @param {*} uniformsInfo - Uniforms description.
* @param {*} samplersInfo - Samplers description.
* @param {*} blendingInfo - Blending mode description.
* @param {string[]|null} extensionsInfo - Required extensions list.
*
* @example
* system.registerShader(
* 'red',
* 'attribute vec2 aPosition;\nvoid main() { gl_Position = vec4(aPosition, 0.0, 1.0); }',
* 'void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); }',
* { aPosition: { size: 2, stride: 2, offset: 0 } },
* {},
* { source: 'src-alpha', destination: 'one-minus-src-alpha' }
* );
*/
registerShader(
id,
vertex,
fragment,
layoutInfo,
uniformsInfo,
samplersInfo,
blendingInfo,
extensionsInfo
) {
if (typeof id !== 'string') {
throw new Error('`id` is not type of String!');
}
if (typeof vertex !== 'string') {
throw new Error('`vertex` is not type of String!');
}
if (typeof fragment !== 'string') {
throw new Error('`fragment` is not type of String!');
}
if (!layoutInfo) {
throw new Error('`layoutInfo` cannot be null!');
}
this.unregisterShader(id);
if (Array.isArray(extensionsInfo) && extensionsInfo.length > 0) {
if (!this.requestExtensions(...extensionsInfo)) {
throw new Error(`One of shader extensions is not supported (${id})!`);
}
}
const gl = this._context;
const shader = gl.createProgram();
const vshader = gl.createShader(gl.VERTEX_SHADER);
const fshader = gl.createShader(gl.FRAGMENT_SHADER);
const deleteAll = () => {
gl.deleteShader(vshader);
gl.deleteShader(fshader);
gl.deleteProgram(shader);
};
// TODO: fix problem with forced GLSL 3 in WebGL 2.
// const { _contextVersion } = this;
// if (_contextVersion > 1) {
// vertex = `#version 300 es\n#define OXY_ctx_ver ${_contextVersion}\n${vertex}`;
// fragment = `#version 300 es\n#define OXY_ctx_ver ${_contextVersion}\n${fragment}`;
// } else {
// vertex = `#define OXY_ctx_ver ${_contextVersion}\n${vertex}`;
// fragment = `#define OXY_ctx_ver ${_contextVersion}\n${fragment}`;
// }
gl.shaderSource(vshader, vertex);
gl.shaderSource(fshader, fragment);
gl.compileShader(vshader);
gl.compileShader(fshader);
if (!gl.getShaderParameter(vshader, gl.COMPILE_STATUS)) {
const log = gl.getShaderInfoLog(vshader);
deleteAll();
throw new Error(`Cannot compile vertex shader: ${id}\nLog: ${log}`);
}
if (!gl.getShaderParameter(fshader, gl.COMPILE_STATUS)) {
const log = gl.getShaderInfoLog(fshader);
deleteAll();
throw new Error(`Cannot compile fragment shader: ${id}\nLog: ${log}`);
}
gl.attachShader(shader, vshader);
gl.attachShader(shader, fshader);
gl.linkProgram(shader);
if (!gl.getProgramParameter(shader, gl.LINK_STATUS)) {
const log = gl.getProgramInfoLog(shader);
deleteAll();
throw new Error(`Cannot link shader program: ${id}\nLog: ${log}`);
}
const layout = new Map();
const uniforms = new Map();
const samplers = new Map();
let blending = null;
for (const name in layoutInfo) {
const { size, stride, offset } = layoutInfo[name];
if (typeof size !== 'number' ||
typeof stride !== 'number' ||
typeof offset !== 'number'
) {
deleteAll();
throw new Error(
`Shader layout does not have proper settings: ${id} (${name})`
);
}
const location = gl.getAttribLocation(shader, name);
if (location < 0) {
deleteAll();
throw new Error(
`Shader does not have attribute: ${id} (${name})`
);
}
layout.set(name, {
location,
size,
stride,
offset
});
}
if (layout.size === 0) {
deleteAll();
throw new Error(`Shader layout cannot be empty: ${id}`);
}
if (!!uniformsInfo) {
for (const name in uniformsInfo) {
const mapping = uniformsInfo[name];
if (typeof mapping !== 'string' &&
typeof mapping !== 'number' &&
!(mapping instanceof Array)
) {
deleteAll();
throw new Error(
`Shader uniform does not have proper settings: ${id} (${name})`
);
}
let func = null;
if (typeof mapping === 'string' && mapping.startsWith('@')) {
func = functions[mapping];
if (!func) {
func = functions[mapping] = makeApplierFunction(mapping);
}
}
const location = gl.getUniformLocation(shader, name);
if (!location) {
deleteAll();
throw new Error(
`Shader does not have uniform: ${id} (${name})`
);
}
const forcedUpdate =
!!func ||
mapping === 'projection-matrix' ||
mapping === 'view-matrix' ||
mapping === 'model-matrix' ||
mapping === 'time' ||
mapping === 'viewport-size' ||
mapping === 'inverse-viewport-size';
uniforms.set(name, {
location,
mapping: !func ? mapping : func,
forcedUpdate
});
}
}
if (!!samplersInfo) {
for (const name in samplersInfo) {
const { channel, texture, filtering } = samplersInfo[name];
if (typeof channel !== 'number' ||
(!!texture && typeof texture !== 'string') ||
(!!filtering && typeof filtering !== 'string')
) {
deleteAll();
throw new Error(
`Shader sampler does not have proper settings: ${id} (${name})`
);
}
const location = gl.getUniformLocation(shader, name);
if (!location) {
deleteAll();
throw new Error(
`Shader does not have sampler: ${id} (${name})`
);
}
samplers.set(name, {
location,
channel,
texture,
filtering
});
}
}
if (!!blendingInfo) {
const { source, destinat