UNPKG

@babylonjs/core

Version:

Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.

1,195 lines (1,193 loc) 95.4 kB
import { Engine } from "../Engines/engine.js"; import { InternalTexture } from "../Materials/Textures/internalTexture.js"; import { Texture } from "../Materials/Textures/texture.js"; import { DataBuffer } from "../Buffers/dataBuffer.js"; import { Tools } from "../Misc/tools.js"; import { Observable } from "../Misc/observable.js"; import { CreateRadianceImageDataArrayBufferViews, GetEnvInfo, UploadEnvSpherical } from "../Misc/environmentTextureTools.js"; import { Logger } from "../Misc/logger.js"; import { ThinEngine } from "./thinEngine.js"; import { EngineStore } from "./engineStore.js"; import { ShaderCodeInliner } from "./Processors/shaderCodeInliner.js"; import { NativeShaderProcessor } from "./Native/nativeShaderProcessors.js"; import { NativeDataStream } from "./Native/nativeDataStream.js"; import { NativePipelineContext } from "./Native/nativePipelineContext.js"; import { NativeRenderTargetWrapper } from "./Native/nativeRenderTargetWrapper.js"; import { NativeHardwareTexture } from "./Native/nativeHardwareTexture.js"; import { getNativeAlphaMode, getNativeAttribType, getNativeSamplingMode, getNativeTextureFormat, getNativeStencilDepthFail, getNativeStencilDepthPass, getNativeStencilFunc, getNativeStencilOpFail, getNativeAddressMode, } from "./Native/nativeHelpers.js"; import { checkNonFloatVertexBuffers } from "../Buffers/buffer.nonFloatVertexBuffers.js"; import { NativeShaderProcessingContext } from "./Native/nativeShaderProcessingContext.js"; import "../Buffers/buffer.align.js"; import { _GetCompatibleTextureLoader } from "../Materials/Textures/Loaders/textureLoaderManager.js"; import { _TimeToken } from "../Instrumentation/timeToken.js"; const onNativeObjectInitialized = new Observable(); if (typeof self !== "undefined" && !Object.prototype.hasOwnProperty.call(self, "_native")) { let __native; Object.defineProperty(self, "_native", { get: () => __native, set: (value) => { __native = value; if (__native) { onNativeObjectInitialized.notifyObservers(__native); } }, }); } /** * Returns _native only after it has been defined by BabylonNative. * @internal */ export async function AcquireNativeObjectAsync() { return await new Promise((resolve) => { if (typeof _native === "undefined") { onNativeObjectInitialized.addOnce((nativeObject) => resolve(nativeObject)); } else { resolve(_native); } }); } /** * Registers a constructor on the _native object. See NativeXRFrame for an example. * @internal */ export async function RegisterNativeTypeAsync(typeName, constructor) { (await AcquireNativeObjectAsync())[typeName] = constructor; } /** * Container for accessors for natively-stored mesh data buffers. */ class NativeDataBuffer extends DataBuffer { } /** @internal */ class CommandBufferEncoder { constructor(_engine) { this._engine = _engine; this._pending = new Array(); this._isCommandBufferScopeActive = false; this._commandStream = NativeEngine._createNativeDataStream(); this._engine.setCommandDataStream(this._commandStream); } beginCommandScope() { if (this._isCommandBufferScopeActive) { throw new Error("Command scope already active."); } this._isCommandBufferScopeActive = true; } endCommandScope() { if (!this._isCommandBufferScopeActive) { throw new Error("Command scope is not active."); } this._isCommandBufferScopeActive = false; this._submit(); } startEncodingCommand(command) { this._commandStream.writeNativeData(command); } encodeCommandArgAsUInt32(commandArg) { this._commandStream.writeUint32(commandArg); } encodeCommandArgAsUInt32s(commandArg) { this._commandStream.writeUint32Array(commandArg); } encodeCommandArgAsInt32(commandArg) { this._commandStream.writeInt32(commandArg); } encodeCommandArgAsInt32s(commandArg) { this._commandStream.writeInt32Array(commandArg); } encodeCommandArgAsFloat32(commandArg) { this._commandStream.writeFloat32(commandArg); } encodeCommandArgAsFloat32s(commandArg) { this._commandStream.writeFloat32Array(commandArg); } encodeCommandArgAsNativeData(commandArg) { this._commandStream.writeNativeData(commandArg); this._pending.push(commandArg); } finishEncodingCommand() { if (!this._isCommandBufferScopeActive) { this._submit(); } } _submit() { this._engine.submitCommands(); this._pending.length = 0; } } const remappedAttributesNames = []; /** @internal */ export class NativeEngine extends Engine { setHardwareScalingLevel(level) { super.setHardwareScalingLevel(level); this._engine.setHardwareScalingLevel(level); } constructor(options = {}) { super(null, false, undefined, options.adaptToDeviceRatio); this._engine = new _native.Engine({ version: Engine.Version, nonFloatVertexBuffers: true, }); this._camera = _native.Camera ? new _native.Camera() : null; this._commandBufferEncoder = new CommandBufferEncoder(this._engine); this._frameStats = { gpuTimeNs: Number.NaN }; this._boundBuffersVertexArray = null; this._currentDepthTest = _native.Engine.DEPTH_TEST_LEQUAL; this._stencilTest = false; this._stencilMask = 255; this._stencilFunc = 519; this._stencilFuncRef = 0; this._stencilFuncMask = 255; this._stencilOpStencilFail = 7680; this._stencilOpDepthFail = 7680; this._stencilOpStencilDepthPass = 7681; this._zOffset = 0; this._zOffsetUnits = 0; this._depthWrite = true; // warning for non supported fill mode has already been displayed this._fillModeWarningDisplayed = false; if (_native.Engine.PROTOCOL_VERSION !== NativeEngine.PROTOCOL_VERSION) { throw new Error(`Protocol version mismatch: ${_native.Engine.PROTOCOL_VERSION} (Native) !== ${NativeEngine.PROTOCOL_VERSION} (JS)`); } if (this._engine.setDeviceLostCallback) { this._engine.setDeviceLostCallback(() => { this.onContextLostObservable.notifyObservers(this); this._contextWasLost = true; this._restoreEngineAfterContextLost(); }); } this._webGLVersion = 2; this.disableUniformBuffers = true; this._shaderPlatformName = "NATIVE"; // TODO: Initialize this more correctly based on the hardware capabilities. // Init caps this._caps = { maxTexturesImageUnits: 16, maxVertexTextureImageUnits: 16, maxCombinedTexturesImageUnits: 32, maxTextureSize: _native.Engine.CAPS_LIMITS_MAX_TEXTURE_SIZE, maxCubemapTextureSize: 512, maxRenderTextureSize: 512, maxVertexAttribs: 16, maxVaryingVectors: 16, maxDrawBuffers: 8, maxFragmentUniformVectors: 16, maxVertexUniformVectors: 16, standardDerivatives: true, astc: null, pvrtc: null, etc1: null, etc2: null, bptc: null, maxAnisotropy: 16, // TODO: Retrieve this smartly. Currently set to D3D11 maximum allowable value. uintIndices: true, fragmentDepthSupported: false, highPrecisionShaderSupported: true, colorBufferFloat: false, supportFloatTexturesResolve: false, rg11b10ufColorRenderable: false, textureFloat: true, textureFloatLinearFiltering: true, textureFloatRender: true, textureHalfFloat: true, textureHalfFloatLinearFiltering: true, textureHalfFloatRender: true, textureLOD: true, texelFetch: false, drawBuffersExtension: false, depthTextureExtension: false, vertexArrayObject: true, instancedArrays: true, supportOcclusionQuery: false, canUseTimestampForTimerQuery: false, blendMinMax: false, maxMSAASamples: 16, canUseGLInstanceID: true, canUseGLVertexID: true, supportComputeShaders: false, supportSRGBBuffers: true, supportTransformFeedbacks: false, textureMaxLevel: false, texture2DArrayMaxLayerCount: _native.Engine.CAPS_LIMITS_MAX_TEXTURE_LAYERS, disableMorphTargetTexture: false, parallelShaderCompile: { COMPLETION_STATUS_KHR: 0 }, textureNorm16: false, blendParametersPerTarget: false, dualSourceBlending: false, }; this._features = { forceBitmapOverHTMLImageElement: true, supportRenderAndCopyToLodForFloatTextures: false, supportDepthStencilTexture: false, supportShadowSamplers: false, uniformBufferHardCheckMatrix: false, allowTexturePrefiltering: false, trackUbosInFrame: false, checkUbosContentBeforeUpload: false, supportCSM: false, basisNeedsPOT: false, support3DTextures: false, needTypeSuffixInShaderConstants: false, supportMSAA: true, supportSSAO2: false, supportIBLShadows: false, supportExtendedTextureFormats: false, supportSwitchCaseInShader: false, supportSyncTextureRead: false, needsInvertingBitmap: true, useUBOBindingCache: true, needShaderCodeInlining: true, needToAlwaysBindUniformBuffers: false, supportRenderPasses: true, supportSpriteInstancing: false, forceVertexBufferStrideAndOffsetMultiple4Bytes: true, _checkNonFloatVertexBuffersDontRecreatePipelineContext: false, _collectUbosUpdatedInFrame: false, }; Tools.Log("Babylon Native (v" + Engine.Version + ") launched"); Tools.LoadScript = function (scriptUrl, onSuccess, onError, scriptId) { Tools.LoadFile(scriptUrl, (data) => { Function(data).apply(null); if (onSuccess) { onSuccess(); } }, undefined, undefined, false, (request, exception) => { if (onError) { onError("LoadScript Error", exception); } }); }; // Wrappers if (typeof URL === "undefined") { window.URL = { createObjectURL: function () { }, revokeObjectURL: function () { }, }; } if (typeof Blob === "undefined") { window.Blob = function (v) { return v; }; } // polyfill for Chakra if (!Array.prototype.flat) { Object.defineProperty(Array.prototype, "flat", { configurable: true, value: function flat() { const depth = isNaN(arguments[0]) ? 1 : Number(arguments[0]); return depth ? Array.prototype.reduce.call(this, function (acc, cur) { if (Array.isArray(cur)) { // eslint-disable-next-line prefer-spread acc.push.apply(acc, flat.call(cur, depth - 1)); } else { acc.push(cur); } return acc; }, []) : Array.prototype.slice.call(this); }, writable: true, }); } // Currently we do not fully configure the ThinEngine on construction of NativeEngine. // Setup resolution scaling based on display settings. const devicePixelRatio = window ? window.devicePixelRatio || 1.0 : 1.0; this._hardwareScalingLevel = options.adaptToDeviceRatio ? 1.0 / devicePixelRatio : 1.0; this._engine.setHardwareScalingLevel(this._hardwareScalingLevel); this._lastDevicePixelRatio = devicePixelRatio; this.resize(); const currentDepthFunction = this.getDepthFunction(); if (currentDepthFunction) { this.setDepthFunction(currentDepthFunction); } // Shader processor this._shaderProcessor = new NativeShaderProcessor(); this.onNewSceneAddedObservable.add((scene) => { const originalRender = scene.render; scene.render = (...args) => { this._commandBufferEncoder.beginCommandScope(); originalRender.apply(scene, args); this._commandBufferEncoder.endCommandScope(); }; }); } dispose() { super.dispose(); if (this._boundBuffersVertexArray) { this._deleteVertexArray(this._boundBuffersVertexArray); } this._engine.dispose(); } /** @internal */ static _createNativeDataStream() { return new NativeDataStream(); } /** * Can be used to override the current requestAnimationFrame requester. * @internal */ _queueNewFrame(bindedRenderFunction, requester) { // Use the provided requestAnimationFrame, unless the requester is the window. In that case, we will default to the Babylon Native version of requestAnimationFrame. if (requester.requestAnimationFrame && requester !== window) { requester.requestAnimationFrame(bindedRenderFunction); } else { this._engine.requestAnimationFrame(bindedRenderFunction); } return 0; } _restoreEngineAfterContextLost() { this._clearEmptyResources(); const depthTest = this._depthCullingState.depthTest; // backup those values because the call to initEngine / wipeCaches will reset them const depthFunc = this._depthCullingState.depthFunc; const depthMask = this._depthCullingState.depthMask; const stencilTest = this._stencilState.stencilTest; this._rebuildGraphicsResources(); this._depthCullingState.depthTest = depthTest; this._depthCullingState.depthFunc = depthFunc; this._depthCullingState.depthMask = depthMask; this._stencilState.stencilTest = stencilTest; this._flagContextRestored(); } /** * Override default engine behavior. * @param framebuffer */ _bindUnboundFramebuffer(framebuffer) { if (this._currentFramebuffer !== framebuffer) { if (this._currentFramebuffer) { this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_UNBINDFRAMEBUFFER); this._commandBufferEncoder.encodeCommandArgAsNativeData(this._currentFramebuffer); this._commandBufferEncoder.finishEncodingCommand(); } if (framebuffer) { this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_BINDFRAMEBUFFER); this._commandBufferEncoder.encodeCommandArgAsNativeData(framebuffer); this._commandBufferEncoder.finishEncodingCommand(); } this._currentFramebuffer = framebuffer; } } /** * Gets host document * @returns the host document object */ getHostDocument() { return null; } clear(color, backBuffer, depth, stencil = false, stencilClearValue = 0) { if (this.useReverseDepthBuffer) { throw new Error("reverse depth buffer is not currently implemented"); } this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_CLEAR); this._commandBufferEncoder.encodeCommandArgAsUInt32(backBuffer && color ? 1 : 0); this._commandBufferEncoder.encodeCommandArgAsFloat32(color ? color.r : 0); this._commandBufferEncoder.encodeCommandArgAsFloat32(color ? color.g : 0); this._commandBufferEncoder.encodeCommandArgAsFloat32(color ? color.b : 0); this._commandBufferEncoder.encodeCommandArgAsFloat32(color ? color.a : 1); this._commandBufferEncoder.encodeCommandArgAsUInt32(depth ? 1 : 0); this._commandBufferEncoder.encodeCommandArgAsFloat32(1); this._commandBufferEncoder.encodeCommandArgAsUInt32(stencil ? 1 : 0); this._commandBufferEncoder.encodeCommandArgAsUInt32(stencilClearValue); this._commandBufferEncoder.finishEncodingCommand(); } createIndexBuffer(indices, updateable, _label) { const data = this._normalizeIndexData(indices); const buffer = new NativeDataBuffer(); buffer.references = 1; buffer.is32Bits = data.BYTES_PER_ELEMENT === 4; if (data.byteLength) { buffer.nativeIndexBuffer = this._engine.createIndexBuffer(data.buffer, data.byteOffset, data.byteLength, buffer.is32Bits, updateable ?? false); } return buffer; } createVertexBuffer(vertices, updateable, _label) { const data = ArrayBuffer.isView(vertices) ? vertices : new Float32Array(vertices); const buffer = new NativeDataBuffer(); buffer.references = 1; if (data.byteLength) { buffer.nativeVertexBuffer = this._engine.createVertexBuffer(data.buffer, data.byteOffset, data.byteLength, updateable ?? false); } return buffer; } _recordVertexArrayObject(vertexArray, vertexBuffers, indexBuffer, effect, overrideVertexBuffers) { if (!effect._checkedNonFloatVertexBuffers) { checkNonFloatVertexBuffers(vertexBuffers, effect); effect._checkedNonFloatVertexBuffers = true; } if (indexBuffer) { this._engine.recordIndexBuffer(vertexArray, indexBuffer.nativeIndexBuffer); } const attributes = effect.getAttributesNames(); for (let index = 0; index < attributes.length; index++) { const location = effect.getAttributeLocation(index); if (location >= 0) { const kind = attributes[index]; let vertexBuffer = null; if (overrideVertexBuffers) { vertexBuffer = overrideVertexBuffers[kind]; } if (!vertexBuffer) { vertexBuffer = vertexBuffers[kind]; } if (vertexBuffer) { const buffer = vertexBuffer.effectiveBuffer; if (buffer && buffer.nativeVertexBuffer) { this._engine.recordVertexBuffer(vertexArray, buffer.nativeVertexBuffer, location, vertexBuffer.effectiveByteOffset, vertexBuffer.effectiveByteStride, vertexBuffer.getSize(), getNativeAttribType(vertexBuffer.type), vertexBuffer.normalized, vertexBuffer.getInstanceDivisor()); } } } } } bindBuffers(vertexBuffers, indexBuffer, effect) { if (this._boundBuffersVertexArray) { this._deleteVertexArray(this._boundBuffersVertexArray); } this._boundBuffersVertexArray = this._engine.createVertexArray(); this._recordVertexArrayObject(this._boundBuffersVertexArray, vertexBuffers, indexBuffer, effect); this.bindVertexArrayObject(this._boundBuffersVertexArray); } recordVertexArrayObject(vertexBuffers, indexBuffer, effect, overrideVertexBuffers) { const vertexArray = this._engine.createVertexArray(); this._recordVertexArrayObject(vertexArray, vertexBuffers, indexBuffer, effect, overrideVertexBuffers); return vertexArray; } _deleteVertexArray(vertexArray) { this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_DELETEVERTEXARRAY); this._commandBufferEncoder.encodeCommandArgAsNativeData(vertexArray); this._commandBufferEncoder.finishEncodingCommand(); } bindVertexArrayObject(vertexArray) { this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_BINDVERTEXARRAY); this._commandBufferEncoder.encodeCommandArgAsNativeData(vertexArray); this._commandBufferEncoder.finishEncodingCommand(); } releaseVertexArrayObject(vertexArray) { this._deleteVertexArray(vertexArray); } getAttributes(pipelineContext, attributesNames) { const nativePipelineContext = pipelineContext; const nativeShaderProcessingContext = nativePipelineContext.shaderProcessingContext; remappedAttributesNames.length = 0; for (let index = 0; index < attributesNames.length; index++) { const origAttributeName = attributesNames[index]; const attributeName = nativeShaderProcessingContext.remappedAttributeNames[origAttributeName] ?? origAttributeName; remappedAttributesNames[index] = attributeName; } return this._engine.getAttributes(nativePipelineContext.program, remappedAttributesNames); } /** * Triangle Fan and Line Loop are not supported by modern rendering API * @param fillMode defines the primitive to use * @returns true if supported */ _checkSupportedFillMode(fillMode) { if (fillMode == 5 || fillMode == 8) { if (!this._fillModeWarningDisplayed) { Logger.Warn("Line Loop and Triangle Fan are not supported fill modes with Babylon Native. Elements with these fill mode will not be visible."); this._fillModeWarningDisplayed = true; } return false; } return true; } /** * Draw a list of indexed primitives * @param fillMode defines the primitive to use * @param indexStart defines the starting index * @param indexCount defines the number of index to draw * @param instancesCount defines the number of instances to draw (if instantiation is enabled) */ drawElementsType(fillMode, indexStart, indexCount, instancesCount) { if (!this._checkSupportedFillMode(fillMode)) { return; } // Apply states this._drawCalls.addCount(1, false); if (instancesCount) { this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_DRAWINDEXEDINSTANCED); this._commandBufferEncoder.encodeCommandArgAsUInt32(fillMode); this._commandBufferEncoder.encodeCommandArgAsUInt32(indexStart); this._commandBufferEncoder.encodeCommandArgAsUInt32(indexCount); this._commandBufferEncoder.encodeCommandArgAsUInt32(instancesCount); } else { this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_DRAWINDEXED); this._commandBufferEncoder.encodeCommandArgAsUInt32(fillMode); this._commandBufferEncoder.encodeCommandArgAsUInt32(indexStart); this._commandBufferEncoder.encodeCommandArgAsUInt32(indexCount); } this._commandBufferEncoder.finishEncodingCommand(); } /** * Draw a list of unindexed primitives * @param fillMode defines the primitive to use * @param verticesStart defines the index of first vertex to draw * @param verticesCount defines the count of vertices to draw * @param instancesCount defines the number of instances to draw (if instantiation is enabled) */ drawArraysType(fillMode, verticesStart, verticesCount, instancesCount) { if (!this._checkSupportedFillMode(fillMode)) { return; } // Apply states this._drawCalls.addCount(1, false); if (instancesCount) { this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_DRAWINSTANCED); this._commandBufferEncoder.encodeCommandArgAsUInt32(fillMode); this._commandBufferEncoder.encodeCommandArgAsUInt32(verticesStart); this._commandBufferEncoder.encodeCommandArgAsUInt32(verticesCount); this._commandBufferEncoder.encodeCommandArgAsUInt32(instancesCount); } else { this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_DRAW); this._commandBufferEncoder.encodeCommandArgAsUInt32(fillMode); this._commandBufferEncoder.encodeCommandArgAsUInt32(verticesStart); this._commandBufferEncoder.encodeCommandArgAsUInt32(verticesCount); } this._commandBufferEncoder.finishEncodingCommand(); } createPipelineContext(shaderProcessingContext) { const isAsync = !!this._caps.parallelShaderCompile; return new NativePipelineContext(this, isAsync, shaderProcessingContext); } createMaterialContext() { return undefined; } createDrawContext() { return undefined; } /** * Function is not technically Async * @internal */ // eslint-disable-next-line no-restricted-syntax _preparePipelineContextAsync(pipelineContext, vertexSourceCode, fragmentSourceCode, createAsRaw, _rawVertexSourceCode, _rawFragmentSourceCode, _rebuildRebind, defines, _transformFeedbackVaryings, _key, onReady) { if (createAsRaw) { this.createRawShaderProgram(); } else { this.createShaderProgram(pipelineContext, vertexSourceCode, fragmentSourceCode, defines); } onReady(); } /** * @internal */ _getShaderProcessingContext(_shaderLanguage) { return new NativeShaderProcessingContext(); } /** * @internal */ _executeWhenRenderingStateIsCompiled(pipelineContext, action) { const nativePipelineContext = pipelineContext; if (nativePipelineContext.isAsync) { if (nativePipelineContext.onCompiled) { const oldHandler = nativePipelineContext.onCompiled; nativePipelineContext.onCompiled = () => { oldHandler(); action(); }; } else { nativePipelineContext.onCompiled = action; } } else { action(); } } createRawShaderProgram() { throw new Error("Not Supported"); } createShaderProgram(pipelineContext, vertexCode, fragmentCode, defines) { const nativePipelineContext = pipelineContext; this.onBeforeShaderCompilationObservable.notifyObservers(this); const vertexInliner = new ShaderCodeInliner(vertexCode); vertexInliner.processCode(); vertexCode = vertexInliner.code; const fragmentInliner = new ShaderCodeInliner(fragmentCode); fragmentInliner.processCode(); fragmentCode = fragmentInliner.code; vertexCode = ThinEngine._ConcatenateShader(vertexCode, defines); fragmentCode = ThinEngine._ConcatenateShader(fragmentCode, defines); const onSuccess = () => { nativePipelineContext.isCompiled = true; nativePipelineContext.onCompiled?.(); this.onAfterShaderCompilationObservable.notifyObservers(this); }; if (pipelineContext.isAsync) { nativePipelineContext.program = this._engine.createProgramAsync(vertexCode, fragmentCode, onSuccess, (error) => { nativePipelineContext.compilationError = error; }); } else { try { nativePipelineContext.program = this._engine.createProgram(vertexCode, fragmentCode); onSuccess(); } catch (e) { const message = e?.message; throw new Error("SHADER ERROR" + (typeof message === "string" ? "\n" + message : "")); } } return nativePipelineContext.program; } /** * Inline functions in shader code that are marked to be inlined * @param code code to inline * @returns inlined code */ inlineShaderCode(code) { const sci = new ShaderCodeInliner(code); sci.debug = false; sci.processCode(); return sci.code; } _setProgram(program) { if (this._currentProgram !== program) { this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETPROGRAM); this._commandBufferEncoder.encodeCommandArgAsNativeData(program); this._commandBufferEncoder.finishEncodingCommand(); this._currentProgram = program; } } _deletePipelineContext(pipelineContext) { const nativePipelineContext = pipelineContext; if (nativePipelineContext && nativePipelineContext.program) { this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_DELETEPROGRAM); this._commandBufferEncoder.encodeCommandArgAsNativeData(nativePipelineContext.program); this._commandBufferEncoder.finishEncodingCommand(); } } getUniforms(pipelineContext, uniformsNames) { const nativePipelineContext = pipelineContext; return this._engine.getUniforms(nativePipelineContext.program, uniformsNames); } bindUniformBlock(pipelineContext, blockName, index) { // TODO throw new Error("Not Implemented"); } bindSamplers(effect) { const nativePipelineContext = effect.getPipelineContext(); this._setProgram(nativePipelineContext.program); // TODO: share this with engine? const samplers = effect.getSamplers(); for (let index = 0; index < samplers.length; index++) { const uniform = effect.getUniform(samplers[index]); if (uniform) { this._boundUniforms[index] = uniform; } } this._currentEffect = null; } getRenderWidth(useScreen = false) { if (!useScreen && this._currentRenderTarget) { return this._currentRenderTarget.width; } return this._engine.getRenderWidth(); } getRenderHeight(useScreen = false) { if (!useScreen && this._currentRenderTarget) { return this._currentRenderTarget.height; } return this._engine.getRenderHeight(); } setViewport(viewport, requiredWidth, requiredHeight) { this._cachedViewport = viewport; this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETVIEWPORT); this._commandBufferEncoder.encodeCommandArgAsFloat32(viewport.x); this._commandBufferEncoder.encodeCommandArgAsFloat32(viewport.y); this._commandBufferEncoder.encodeCommandArgAsFloat32(viewport.width); this._commandBufferEncoder.encodeCommandArgAsFloat32(viewport.height); this._commandBufferEncoder.finishEncodingCommand(); } enableScissor(x, y, width, height) { this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETSCISSOR); this._commandBufferEncoder.encodeCommandArgAsFloat32(x); this._commandBufferEncoder.encodeCommandArgAsFloat32(y); this._commandBufferEncoder.encodeCommandArgAsFloat32(width); this._commandBufferEncoder.encodeCommandArgAsFloat32(height); this._commandBufferEncoder.finishEncodingCommand(); } disableScissor() { this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETSCISSOR); this._commandBufferEncoder.encodeCommandArgAsFloat32(0); this._commandBufferEncoder.encodeCommandArgAsFloat32(0); this._commandBufferEncoder.encodeCommandArgAsFloat32(0); this._commandBufferEncoder.encodeCommandArgAsFloat32(0); this._commandBufferEncoder.finishEncodingCommand(); } setStateCullFaceType(_cullBackFaces, _force) { throw new Error("setStateCullFaceType: Not Implemented"); } setState(culling, zOffset = 0, force, reverseSide = false, cullBackFaces, stencil, zOffsetUnits = 0) { this._zOffset = zOffset; this._zOffsetUnits = zOffsetUnits; if (this._zOffset !== 0) { Tools.Warn("zOffset is not supported in Native engine."); } this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETSTATE); this._commandBufferEncoder.encodeCommandArgAsUInt32(culling ? 1 : 0); this._commandBufferEncoder.encodeCommandArgAsFloat32(zOffset); this._commandBufferEncoder.encodeCommandArgAsFloat32(zOffsetUnits); this._commandBufferEncoder.encodeCommandArgAsUInt32((this.cullBackFaces ?? cullBackFaces ?? true) ? 1 : 0); this._commandBufferEncoder.encodeCommandArgAsUInt32(reverseSide ? 1 : 0); this._commandBufferEncoder.finishEncodingCommand(); } /** * Gets the client rect of native canvas. Needed for InputManager. * @returns a client rectangle */ getInputElementClientRect() { const rect = { bottom: this.getRenderHeight(), height: this.getRenderHeight(), left: 0, right: this.getRenderWidth(), top: 0, width: this.getRenderWidth(), x: 0, y: 0, toJSON: () => { }, }; return rect; } /** * Set the z offset Factor to apply to current rendering * @param value defines the offset to apply */ setZOffset(value) { if (value !== this._zOffset) { this._zOffset = value; this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETZOFFSET); this._commandBufferEncoder.encodeCommandArgAsFloat32(this.useReverseDepthBuffer ? -value : value); this._commandBufferEncoder.finishEncodingCommand(); } } /** * Gets the current value of the zOffset Factor * @returns the current zOffset Factor state */ getZOffset() { return this._zOffset; } /** * Set the z offset Units to apply to current rendering * @param value defines the offset to apply */ setZOffsetUnits(value) { if (value !== this._zOffsetUnits) { this._zOffsetUnits = value; this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETZOFFSETUNITS); this._commandBufferEncoder.encodeCommandArgAsFloat32(this.useReverseDepthBuffer ? -value : value); this._commandBufferEncoder.finishEncodingCommand(); } } /** * Gets the current value of the zOffset Units * @returns the current zOffset Units state */ getZOffsetUnits() { return this._zOffsetUnits; } /** * Enable or disable depth buffering * @param enable defines the state to set */ setDepthBuffer(enable) { this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETDEPTHTEST); this._commandBufferEncoder.encodeCommandArgAsUInt32(enable ? this._currentDepthTest : _native.Engine.DEPTH_TEST_ALWAYS); this._commandBufferEncoder.finishEncodingCommand(); } /** * Gets a boolean indicating if depth writing is enabled * @returns the current depth writing state */ getDepthWrite() { return this._depthWrite; } getDepthFunction() { switch (this._currentDepthTest) { case _native.Engine.DEPTH_TEST_NEVER: return 512; case _native.Engine.DEPTH_TEST_ALWAYS: return 519; case _native.Engine.DEPTH_TEST_GREATER: return 516; case _native.Engine.DEPTH_TEST_GEQUAL: return 518; case _native.Engine.DEPTH_TEST_NOTEQUAL: return 517; case _native.Engine.DEPTH_TEST_EQUAL: return 514; case _native.Engine.DEPTH_TEST_LESS: return 513; case _native.Engine.DEPTH_TEST_LEQUAL: return 515; } return null; } setDepthFunction(depthFunc) { let nativeDepthFunc = 0; switch (depthFunc) { case 512: nativeDepthFunc = _native.Engine.DEPTH_TEST_NEVER; break; case 519: nativeDepthFunc = _native.Engine.DEPTH_TEST_ALWAYS; break; case 516: nativeDepthFunc = _native.Engine.DEPTH_TEST_GREATER; break; case 518: nativeDepthFunc = _native.Engine.DEPTH_TEST_GEQUAL; break; case 517: nativeDepthFunc = _native.Engine.DEPTH_TEST_NOTEQUAL; break; case 514: nativeDepthFunc = _native.Engine.DEPTH_TEST_EQUAL; break; case 513: nativeDepthFunc = _native.Engine.DEPTH_TEST_LESS; break; case 515: nativeDepthFunc = _native.Engine.DEPTH_TEST_LEQUAL; break; } this._currentDepthTest = nativeDepthFunc; this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETDEPTHTEST); this._commandBufferEncoder.encodeCommandArgAsUInt32(this._currentDepthTest); this._commandBufferEncoder.finishEncodingCommand(); } /** * Enable or disable depth writing * @param enable defines the state to set */ setDepthWrite(enable) { this._depthWrite = enable; this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETDEPTHWRITE); this._commandBufferEncoder.encodeCommandArgAsUInt32(Number(enable)); this._commandBufferEncoder.finishEncodingCommand(); } /** * Enable or disable color writing * @param enable defines the state to set */ setColorWrite(enable) { this._colorWrite = enable; this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETCOLORWRITE); this._commandBufferEncoder.encodeCommandArgAsUInt32(Number(enable)); this._commandBufferEncoder.finishEncodingCommand(); } /** * Gets a boolean indicating if color writing is enabled * @returns the current color writing state */ getColorWrite() { return this._colorWrite; } applyStencil() { this._setStencil(this._stencilMask, getNativeStencilOpFail(this._stencilOpStencilFail), getNativeStencilDepthFail(this._stencilOpDepthFail), getNativeStencilDepthPass(this._stencilOpStencilDepthPass), getNativeStencilFunc(this._stencilFunc), this._stencilFuncRef); } _setStencil(mask, stencilOpFail, depthOpFail, depthOpPass, func, ref) { this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETSTENCIL); this._commandBufferEncoder.encodeCommandArgAsUInt32(mask); this._commandBufferEncoder.encodeCommandArgAsUInt32(stencilOpFail); this._commandBufferEncoder.encodeCommandArgAsUInt32(depthOpFail); this._commandBufferEncoder.encodeCommandArgAsUInt32(depthOpPass); this._commandBufferEncoder.encodeCommandArgAsUInt32(func); this._commandBufferEncoder.encodeCommandArgAsUInt32(ref); this._commandBufferEncoder.finishEncodingCommand(); } /** * Enable or disable the stencil buffer * @param enable defines if the stencil buffer must be enabled or disabled */ setStencilBuffer(enable) { this._stencilTest = enable; if (enable) { this.applyStencil(); } else { this._setStencil(255, _native.Engine.STENCIL_OP_FAIL_S_KEEP, _native.Engine.STENCIL_OP_FAIL_Z_KEEP, _native.Engine.STENCIL_OP_PASS_Z_KEEP, _native.Engine.STENCIL_TEST_ALWAYS, 0); } } /** * Gets a boolean indicating if stencil buffer is enabled * @returns the current stencil buffer state */ getStencilBuffer() { return this._stencilTest; } /** * Gets the current stencil operation when stencil passes * @returns a number defining stencil operation to use when stencil passes */ getStencilOperationPass() { return this._stencilOpStencilDepthPass; } /** * Sets the stencil operation to use when stencil passes * @param operation defines the stencil operation to use when stencil passes */ setStencilOperationPass(operation) { this._stencilOpStencilDepthPass = operation; this.applyStencil(); } /** * Sets the current stencil mask * @param mask defines the new stencil mask to use */ setStencilMask(mask) { this._stencilMask = mask; this.applyStencil(); } /** * Sets the current stencil function * @param stencilFunc defines the new stencil function to use */ setStencilFunction(stencilFunc) { this._stencilFunc = stencilFunc; this.applyStencil(); } /** * Sets the current stencil reference * @param reference defines the new stencil reference to use */ setStencilFunctionReference(reference) { this._stencilFuncRef = reference; this.applyStencil(); } /** * Sets the current stencil mask * @param mask defines the new stencil mask to use */ setStencilFunctionMask(mask) { this._stencilFuncMask = mask; } /** * Sets the stencil operation to use when stencil fails * @param operation defines the stencil operation to use when stencil fails */ setStencilOperationFail(operation) { this._stencilOpStencilFail = operation; this.applyStencil(); } /** * Sets the stencil operation to use when depth fails * @param operation defines the stencil operation to use when depth fails */ setStencilOperationDepthFail(operation) { this._stencilOpDepthFail = operation; this.applyStencil(); } /** * Gets the current stencil mask * @returns a number defining the new stencil mask to use */ getStencilMask() { return this._stencilMask; } /** * Gets the current stencil function * @returns a number defining the stencil function to use */ getStencilFunction() { return this._stencilFunc; } /** * Gets the current stencil reference value * @returns a number defining the stencil reference value to use */ getStencilFunctionReference() { return this._stencilFuncRef; } /** * Gets the current stencil mask * @returns a number defining the stencil mask to use */ getStencilFunctionMask() { return this._stencilFuncMask; } /** * Gets the current stencil operation when stencil fails * @returns a number defining stencil operation to use when stencil fails */ getStencilOperationFail() { return this._stencilOpStencilFail; } /** * Gets the current stencil operation when depth fails * @returns a number defining stencil operation to use when depth fails */ getStencilOperationDepthFail() { return this._stencilOpDepthFail; } /** * Sets alpha constants used by some alpha blending modes * @param r defines the red component * @param g defines the green component * @param b defines the blue component * @param a defines the alpha component */ setAlphaConstants(r, g, b, a) { throw new Error("Setting alpha blend constant color not yet implemented."); } /** * Sets the current alpha mode * @param mode defines the mode to use (one of the BABYLON.undefined) * @param noDepthWriteChange defines if depth writing state should remains unchanged (false by default) * @param targetIndex defines the index of the target to set the alpha mode for (default is 0) * @see https://doc.babylonjs.com/features/featuresDeepDive/materials/advanced/transparent_rendering */ setAlphaMode(mode, noDepthWriteChange = false, targetIndex = 0) { if (this._alphaMode[targetIndex] === mode) { return; } const nativeMode = getNativeAlphaMode(mode); this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETBLENDMODE); this._commandBufferEncoder.encodeCommandArgAsUInt32(nativeMode); this._commandBufferEncoder.finishEncodingCommand(); if (!noDepthWriteChange) { this.setDepthWrite(mode === 0); } this._alphaMode[targetIndex] = mode; } setInt(uniform, int) { if (!uniform) { return false; } this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETINT); this._commandBufferEncoder.encodeCommandArgAsNativeData(uniform); this._commandBufferEncoder.encodeCommandArgAsInt32(int); this._commandBufferEncoder.finishEncodingCommand(); return true; } setIntArray(uniform, array) { if (!uniform) { return false; } this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETINTARRAY); this._commandBufferEncoder.encodeCommandArgAsNativeData(uniform); this._commandBufferEncoder.encodeCommandArgAsInt32s(array); this._commandBufferEncoder.finishEncodingCommand(); return true; } setIntArray2(uniform, array) { if (!uniform) { return false; } this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETINTARRAY2); this._commandBufferEncoder.encodeCommandArgAsNativeData(uniform); this._commandBufferEncoder.encodeCommandArgAsInt32s(array); this._commandBufferEncoder.finishEncodingCommand(); return true; } setIntArray3(uniform, array) { if (!uniform) { return false; } this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETINTARRAY3); this._commandBufferEncoder.encodeCommandArgAsNativeData(uniform); this._commandBufferEncoder.encodeCommandArgAsInt32s(array); this._commandBufferEncoder.finishEncodingCommand(); return true; } setIntArray4(uniform, array) { if (!uniform) { return false; } this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETINTARRAY4); this._commandBufferEncoder.encodeCommandArgAsNativeData(uniform); this._commandBufferEncoder.encodeCommandArgAsInt32s(array); this._commandBufferEncoder.finishEncodingCommand(); return true; } setFloatArray(uniform, array) { if (!uniform) { return false; } this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETFLOATARRAY); this._commandBufferEncoder.encodeCommandArgAsNativeData(uniform); this._commandBufferEncoder.encodeCommandArgAsFloat32s(array); this._commandBufferEncoder.finishEncodingCommand(); return true; } setFloatArray2(uniform, array) { if (!uniform) { return false; } this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETFLOATARRAY2); this._commandBufferEncoder.encodeCommandArgAsNativeData(uniform); this._commandBufferEncoder.encodeCommandArgAsFloat32s(array); this._commandBufferEncoder.finishEncodingCommand(); return true; } setFloatArray3(uniform, array) { if (!uniform) { return false; } this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETFLOATARRAY3); this._commandBufferEncoder.encodeCommandArgAsNativeData(uniform); this._commandBufferEncoder.encodeCommandArgAsFloat32s(array); this._commandBufferEncoder.finishEncodingCommand(); return true; } setFloatArray4(uniform, array) { if (!uniform) { return false; } this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETFLOATARRAY4); this._commandBufferEncoder.encodeCommandArgAsNativeData(uniform); this._commandBufferEncoder.encodeCommandArgAsFloat32s(array); this._commandBufferEncoder.finishEncodingCommand(); return true; } setArray(uniform, array) { if (!uniform) { return false; } return this.setFloatArray(uniform, new Float32Array(array)); } setArray2(uniform, array) { if (!uniform) { return false; } return this.setFloatArray2(uniform, new Float32Array(array)); } setArray3(uniform, array) { if (!uniform) { return false; } return this.setFloatArray3(uniform, new Float32Array(array)); } setArray4(uniform, array) { if (!uniform) { return false; } return this.setFloatArray4(uniform, new Float32Array(array)); } setMatrices(uniform, matrices) { if (!uniform) { return false; } this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETMATRICES); this._commandBufferEncoder.encodeCommandArgAsNativeData(uniform); this._commandBufferEncoder.encodeCommandArgAsFloat32s(matrices); this._commandBufferEncoder.finishEncodingCommand(); return true; } setMatrix3x3(uniform, matrix) { if (!uniform) { return false; } this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETMATRIX3X3); this._commandBufferEncoder.encodeCommandArgAsNativeData(uniform); this._commandBufferEncoder.encodeCommandArgAsFloat32s(matrix); this._commandBufferEncoder.finishEncodingCommand();