@lightningjs/renderer 3.0.0-beta10 → 3.0.0-beta12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -202
- package/NOTICE +3 -3
- package/README.md +133 -133
- package/dist/src/common/CommonTypes.d.ts +2 -2
- package/dist/src/core/CoreNode.d.ts +8 -7
- package/dist/src/core/CoreNode.js +57 -61
- package/dist/src/core/CoreNode.js.map +1 -1
- package/dist/src/core/CoreTextNode.d.ts +3 -0
- package/dist/src/core/CoreTextNode.js +54 -19
- package/dist/src/core/CoreTextNode.js.map +1 -1
- package/dist/src/core/Stage.js +4 -4
- package/dist/src/core/Stage.js.map +1 -1
- package/dist/src/core/lib/textureCompression.js +4 -4
- package/dist/src/core/lib/textureCompression.js.map +1 -1
- package/dist/src/core/platform.d.ts +10 -0
- package/dist/src/core/platform.js +81 -0
- package/dist/src/core/platform.js.map +1 -0
- package/dist/src/core/renderers/CoreShader.d.ts +9 -0
- package/dist/src/core/renderers/CoreShader.js +28 -0
- package/dist/src/core/renderers/CoreShader.js.map +1 -0
- package/dist/src/core/renderers/CoreShaderNode.js +2 -2
- package/dist/src/core/renderers/CoreShaderNode.js.map +1 -1
- package/dist/src/core/renderers/canvas/CanvasCoreRenderer.d.ts +33 -0
- package/dist/src/core/renderers/canvas/CanvasCoreRenderer.js +250 -0
- package/dist/src/core/renderers/canvas/CanvasCoreRenderer.js.map +1 -0
- package/dist/src/core/renderers/canvas/CanvasCoreTexture.d.ts +16 -0
- package/dist/src/core/renderers/canvas/CanvasCoreTexture.js +124 -0
- package/dist/src/core/renderers/canvas/CanvasCoreTexture.js.map +1 -0
- package/dist/src/core/renderers/canvas/CanvasRenderer.js +1 -1
- package/dist/src/core/renderers/canvas/CanvasRenderer.js.map +1 -1
- package/dist/src/core/renderers/canvas/CanvasTexture.js +5 -5
- package/dist/src/core/renderers/canvas/CanvasTexture.js.map +1 -1
- package/dist/src/core/renderers/canvas/internal/C2DShaderUtils.d.ts +13 -0
- package/dist/src/core/renderers/canvas/internal/C2DShaderUtils.js +113 -192
- package/dist/src/core/renderers/canvas/internal/C2DShaderUtils.js.map +1 -1
- package/dist/src/core/renderers/canvas/internal/ColorUtils.d.ts +0 -2
- package/dist/src/core/renderers/canvas/internal/ColorUtils.js +0 -14
- package/dist/src/core/renderers/canvas/internal/ColorUtils.js.map +1 -1
- package/dist/src/core/renderers/canvas/shaders/UnsupportedShader.d.ts +10 -0
- package/dist/src/core/renderers/canvas/shaders/UnsupportedShader.js +43 -0
- package/dist/src/core/renderers/canvas/shaders/UnsupportedShader.js.map +1 -0
- package/dist/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.d.ts +12 -0
- package/dist/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.js +58 -0
- package/dist/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.js.map +1 -0
- package/dist/src/core/renderers/webgl/WebGlCoreCtxSubTexture.d.ts +9 -0
- package/dist/src/core/renderers/webgl/WebGlCoreCtxSubTexture.js +38 -0
- package/dist/src/core/renderers/webgl/WebGlCoreCtxSubTexture.js.map +1 -0
- package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.d.ts +56 -0
- package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js +239 -0
- package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js.map +1 -0
- package/dist/src/core/renderers/webgl/WebGlCoreRenderOp.d.ts +34 -0
- package/dist/src/core/renderers/webgl/WebGlCoreRenderOp.js +114 -0
- package/dist/src/core/renderers/webgl/WebGlCoreRenderOp.js.map +1 -0
- package/dist/src/core/renderers/webgl/WebGlCoreRenderer.d.ts +133 -0
- package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js +616 -0
- package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js.map +1 -0
- package/dist/src/core/renderers/webgl/WebGlCoreShader.d.ts +83 -0
- package/dist/src/core/renderers/webgl/WebGlCoreShader.js +233 -0
- package/dist/src/core/renderers/webgl/WebGlCoreShader.js.map +1 -0
- package/dist/src/core/renderers/webgl/WebGlCtxRenderTexture.js +5 -6
- package/dist/src/core/renderers/webgl/WebGlCtxRenderTexture.js.map +1 -1
- package/dist/src/core/renderers/webgl/WebGlCtxSubTexture.d.ts +13 -0
- package/dist/src/core/renderers/webgl/WebGlCtxSubTexture.js +34 -5
- package/dist/src/core/renderers/webgl/WebGlCtxSubTexture.js.map +1 -1
- package/dist/src/core/renderers/webgl/WebGlCtxTexture.js +21 -21
- package/dist/src/core/renderers/webgl/WebGlCtxTexture.js.map +1 -1
- package/dist/src/core/renderers/webgl/WebGlRenderOp.js +1 -1
- package/dist/src/core/renderers/webgl/WebGlRenderOp.js.map +1 -1
- package/dist/src/core/renderers/webgl/WebGlRenderer.js +12 -11
- package/dist/src/core/renderers/webgl/WebGlRenderer.js.map +1 -1
- package/dist/src/core/renderers/webgl/WebGlShaderProgram.js +2 -2
- package/dist/src/core/renderers/webgl/WebGlShaderProgram.js.map +1 -1
- package/dist/src/core/renderers/webgl/internal/ShaderUtils.js +35 -35
- package/dist/src/core/renderers/webgl/shaders/DefaultShader.d.ts +9 -0
- package/dist/src/core/renderers/webgl/shaders/DefaultShader.js +87 -0
- package/dist/src/core/renderers/webgl/shaders/DefaultShader.js.map +1 -0
- package/dist/src/core/renderers/webgl/shaders/DefaultShaderBatched.d.ts +10 -0
- package/dist/src/core/renderers/webgl/shaders/DefaultShaderBatched.js +119 -0
- package/dist/src/core/renderers/webgl/shaders/DefaultShaderBatched.js.map +1 -0
- package/dist/src/core/renderers/webgl/shaders/DynamicShader.d.ts +29 -0
- package/dist/src/core/renderers/webgl/shaders/DynamicShader.js +413 -0
- package/dist/src/core/renderers/webgl/shaders/DynamicShader.js.map +1 -0
- package/dist/src/core/renderers/webgl/shaders/RoundedRectangle.d.ts +28 -0
- package/dist/src/core/renderers/webgl/shaders/RoundedRectangle.js +131 -0
- package/dist/src/core/renderers/webgl/shaders/RoundedRectangle.js.map +1 -0
- package/dist/src/core/renderers/webgl/shaders/SdfShader.d.ts +47 -0
- package/dist/src/core/renderers/webgl/shaders/SdfShader.js +160 -0
- package/dist/src/core/renderers/webgl/shaders/SdfShader.js.map +1 -0
- package/dist/src/core/renderers/webgl/shaders/effects/BorderBottomEffect.d.ts +31 -0
- package/dist/src/core/renderers/webgl/shaders/effects/BorderBottomEffect.js +71 -0
- package/dist/src/core/renderers/webgl/shaders/effects/BorderBottomEffect.js.map +1 -0
- package/dist/src/core/renderers/webgl/shaders/effects/BorderEffect.d.ts +30 -0
- package/dist/src/core/renderers/webgl/shaders/effects/BorderEffect.js +58 -0
- package/dist/src/core/renderers/webgl/shaders/effects/BorderEffect.js.map +1 -0
- package/dist/src/core/renderers/webgl/shaders/effects/BorderLeftEffect.d.ts +31 -0
- package/dist/src/core/renderers/webgl/shaders/effects/BorderLeftEffect.js +71 -0
- package/dist/src/core/renderers/webgl/shaders/effects/BorderLeftEffect.js.map +1 -0
- package/dist/src/core/renderers/webgl/shaders/effects/BorderRightEffect.d.ts +31 -0
- package/dist/src/core/renderers/webgl/shaders/effects/BorderRightEffect.js +71 -0
- package/dist/src/core/renderers/webgl/shaders/effects/BorderRightEffect.js.map +1 -0
- package/dist/src/core/renderers/webgl/shaders/effects/BorderTopEffect.d.ts +31 -0
- package/dist/src/core/renderers/webgl/shaders/effects/BorderTopEffect.js +71 -0
- package/dist/src/core/renderers/webgl/shaders/effects/BorderTopEffect.js.map +1 -0
- package/dist/src/core/renderers/webgl/shaders/effects/EffectUtils.d.ts +9 -0
- package/dist/src/core/renderers/webgl/shaders/effects/EffectUtils.js +136 -0
- package/dist/src/core/renderers/webgl/shaders/effects/EffectUtils.js.map +1 -0
- package/dist/src/core/renderers/webgl/shaders/effects/FadeOutEffect.d.ts +36 -0
- package/dist/src/core/renderers/webgl/shaders/effects/FadeOutEffect.js +85 -0
- package/dist/src/core/renderers/webgl/shaders/effects/FadeOutEffect.js.map +1 -0
- package/dist/src/core/renderers/webgl/shaders/effects/GlitchEffect.d.ts +45 -0
- package/dist/src/core/renderers/webgl/shaders/effects/GlitchEffect.js +104 -0
- package/dist/src/core/renderers/webgl/shaders/effects/GlitchEffect.js.map +1 -0
- package/dist/src/core/renderers/webgl/shaders/effects/GrayscaleEffect.d.ts +22 -0
- package/dist/src/core/renderers/webgl/shaders/effects/GrayscaleEffect.js +45 -0
- package/dist/src/core/renderers/webgl/shaders/effects/GrayscaleEffect.js.map +1 -0
- package/dist/src/core/renderers/webgl/shaders/effects/HolePunchEffect.d.ts +58 -0
- package/dist/src/core/renderers/webgl/shaders/effects/HolePunchEffect.js +80 -0
- package/dist/src/core/renderers/webgl/shaders/effects/HolePunchEffect.js.map +1 -0
- package/dist/src/core/renderers/webgl/shaders/effects/LinearGradientEffect.d.ts +35 -0
- package/dist/src/core/renderers/webgl/shaders/effects/LinearGradientEffect.js +129 -0
- package/dist/src/core/renderers/webgl/shaders/effects/LinearGradientEffect.js.map +1 -0
- package/dist/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.d.ts +39 -0
- package/dist/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.js +116 -0
- package/dist/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.js.map +1 -0
- package/dist/src/core/renderers/webgl/shaders/effects/RadialProgressEffect.d.ts +61 -0
- package/dist/src/core/renderers/webgl/shaders/effects/RadialProgressEffect.js +127 -0
- package/dist/src/core/renderers/webgl/shaders/effects/RadialProgressEffect.js.map +1 -0
- package/dist/src/core/renderers/webgl/shaders/effects/RadiusEffect.d.ts +40 -0
- package/dist/src/core/renderers/webgl/shaders/effects/RadiusEffect.js +71 -0
- package/dist/src/core/renderers/webgl/shaders/effects/RadiusEffect.js.map +1 -0
- package/dist/src/core/renderers/webgl/shaders/effects/ShaderEffect.d.ts +115 -0
- package/dist/src/core/renderers/webgl/shaders/effects/ShaderEffect.js +61 -0
- package/dist/src/core/renderers/webgl/shaders/effects/ShaderEffect.js.map +1 -0
- package/dist/src/core/shaders/canvas/Border.js +4 -4
- package/dist/src/core/shaders/canvas/Border.js.map +1 -1
- package/dist/src/core/shaders/canvas/HolePunch.js +3 -3
- package/dist/src/core/shaders/canvas/HolePunch.js.map +1 -1
- package/dist/src/core/shaders/canvas/LinearGradient.js +2 -2
- package/dist/src/core/shaders/canvas/LinearGradient.js.map +1 -1
- package/dist/src/core/shaders/canvas/RadialGradient.js +4 -4
- package/dist/src/core/shaders/canvas/RadialGradient.js.map +1 -1
- package/dist/src/core/shaders/canvas/Rounded.js +1 -1
- package/dist/src/core/shaders/canvas/Rounded.js.map +1 -1
- package/dist/src/core/shaders/canvas/RoundedWithBorder.js +3 -3
- package/dist/src/core/shaders/canvas/RoundedWithBorder.js.map +1 -1
- package/dist/src/core/shaders/canvas/RoundedWithBorderAndShadow.js +3 -3
- package/dist/src/core/shaders/canvas/RoundedWithBorderAndShadow.js.map +1 -1
- package/dist/src/core/shaders/canvas/RoundedWithShadow.js +1 -1
- package/dist/src/core/shaders/canvas/RoundedWithShadow.js.map +1 -1
- package/dist/src/core/shaders/templates/BorderTemplate.d.ts +1 -1
- package/dist/src/core/shaders/templates/BorderTemplate.js +10 -10
- package/dist/src/core/shaders/templates/BorderTemplate.js.map +1 -1
- package/dist/src/core/shaders/templates/HolePunchTemplate.d.ts +2 -2
- package/dist/src/core/shaders/templates/HolePunchTemplate.js +2 -2
- package/dist/src/core/shaders/templates/HolePunchTemplate.js.map +1 -1
- package/dist/src/core/shaders/templates/RadialGradientTemplate.d.ts +2 -2
- package/dist/src/core/shaders/templates/RadialGradientTemplate.js +2 -2
- package/dist/src/core/shaders/templates/RadialGradientTemplate.js.map +1 -1
- package/dist/src/core/shaders/templates/shaderUtils.d.ts +5 -0
- package/dist/src/core/shaders/templates/shaderUtils.js +41 -0
- package/dist/src/core/shaders/templates/shaderUtils.js.map +1 -0
- package/dist/src/core/shaders/webgl/Border.js +83 -83
- package/dist/src/core/shaders/webgl/Border.js.map +1 -1
- package/dist/src/core/shaders/webgl/Default.js +47 -47
- package/dist/src/core/shaders/webgl/DefaultBatched.js +61 -61
- package/dist/src/core/shaders/webgl/HolePunch.js +34 -34
- package/dist/src/core/shaders/webgl/HolePunch.js.map +1 -1
- package/dist/src/core/shaders/webgl/LinearGradient.js +36 -36
- package/dist/src/core/shaders/webgl/RadialGradient.js +35 -35
- package/dist/src/core/shaders/webgl/RadialGradient.js.map +1 -1
- package/dist/src/core/shaders/webgl/Rounded.js +72 -72
- package/dist/src/core/shaders/webgl/Rounded.js.map +1 -1
- package/dist/src/core/shaders/webgl/RoundedWithBorder.js +113 -113
- package/dist/src/core/shaders/webgl/RoundedWithBorder.js.map +1 -1
- package/dist/src/core/shaders/webgl/RoundedWithBorderAndShadow.js +132 -132
- package/dist/src/core/shaders/webgl/RoundedWithBorderAndShadow.js.map +1 -1
- package/dist/src/core/shaders/webgl/RoundedWithShadow.js +55 -55
- package/dist/src/core/shaders/webgl/RoundedWithShadow.js.map +1 -1
- package/dist/src/core/shaders/webgl/SdfShader.js +62 -62
- package/dist/src/core/shaders/webgl/Shadow.js +83 -83
- package/dist/src/core/shaders/webgl/Spinner.d.ts +1 -0
- package/dist/src/core/shaders/webgl/Spinner.js +2 -0
- package/dist/src/core/shaders/webgl/Spinner.js.map +1 -0
- package/dist/src/core/text-rendering/CanvasFontHandler.d.ts +16 -0
- package/dist/src/core/text-rendering/CanvasFontHandler.js +29 -0
- package/dist/src/core/text-rendering/CanvasFontHandler.js.map +1 -1
- package/dist/src/core/text-rendering/SdfFontHandler.d.ts +15 -0
- package/dist/src/core/text-rendering/SdfFontHandler.js +34 -2
- package/dist/src/core/text-rendering/SdfFontHandler.js.map +1 -1
- package/dist/src/core/text-rendering/TextRenderer.d.ts +2 -0
- package/dist/src/core/text-rendering/TextTextureRendererUtils.js.map +1 -1
- package/dist/src/core/text-rendering/font-face-types/SdfTrFontFace/SdfTrFontFace.js +2 -2
- package/dist/src/core/text-rendering/font-face-types/SdfTrFontFace/SdfTrFontFace.js.map +1 -1
- package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js +0 -5
- package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js.map +1 -1
- package/dist/src/core/text-rendering/renderers/LightningTextTextureRenderer.d.ts +1 -7
- package/dist/src/core/text-rendering/renderers/LightningTextTextureRenderer.js +2 -50
- package/dist/src/core/text-rendering/renderers/LightningTextTextureRenderer.js.map +1 -1
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.d.ts +3 -2
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js +83 -42
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js.map +1 -1
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.d.ts +1 -1
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.js +8 -66
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.js.map +1 -1
- package/dist/src/core/text-rendering/renderers/TextRenderer.d.ts +4 -14
- package/dist/src/core/text-rendering/renderers/TextRenderer.js +0 -3
- package/dist/src/core/text-rendering/renderers/TextRenderer.js.map +1 -1
- package/dist/src/core/text-rendering/sdf/PeekableGenerator.d.ts +12 -0
- package/dist/src/core/text-rendering/sdf/PeekableGenerator.js +61 -0
- package/dist/src/core/text-rendering/sdf/PeekableGenerator.js.map +1 -0
- package/dist/src/core/text-rendering/sdf/SimpleFontShaper.d.ts +45 -0
- package/dist/src/core/text-rendering/sdf/SimpleFontShaper.js +69 -0
- package/dist/src/core/text-rendering/sdf/SimpleFontShaper.js.map +1 -0
- package/dist/src/core/textures/ColorTexture.js +1 -1
- package/dist/src/core/textures/ColorTexture.js.map +1 -1
- package/dist/src/core/textures/ImageTexture.d.ts +2 -2
- package/dist/src/core/textures/ImageTexture.js +11 -11
- package/dist/src/core/textures/ImageTexture.js.map +1 -1
- package/dist/src/core/textures/NoiseTexture.d.ts +2 -2
- package/dist/src/core/textures/NoiseTexture.js +6 -6
- package/dist/src/core/textures/NoiseTexture.js.map +1 -1
- package/dist/src/core/textures/RenderTexture.d.ts +6 -6
- package/dist/src/core/textures/RenderTexture.js +10 -10
- package/dist/src/core/textures/RenderTexture.js.map +1 -1
- package/dist/src/core/textures/SubTexture.d.ts +4 -4
- package/dist/src/core/textures/SubTexture.js +8 -8
- package/dist/src/core/textures/SubTexture.js.map +1 -1
- package/dist/src/core/textures/Texture.d.ts +3 -4
- package/dist/src/core/textures/Texture.js +4 -4
- package/dist/src/core/textures/Texture.js.map +1 -1
- package/dist/src/main-api/DynamicShaderController.d.ts +29 -0
- package/dist/src/main-api/DynamicShaderController.js +58 -0
- package/dist/src/main-api/DynamicShaderController.js.map +1 -0
- package/dist/src/main-api/Inspector.js +7 -7
- package/dist/src/main-api/Inspector.js.map +1 -1
- package/dist/src/main-api/Renderer.js +2 -2
- package/dist/src/main-api/Renderer.js.map +1 -1
- package/dist/src/main-api/ShaderController.d.ts +31 -0
- package/dist/src/main-api/ShaderController.js +37 -0
- package/dist/src/main-api/ShaderController.js.map +1 -0
- package/dist/tsconfig.dist.tsbuildinfo +1 -1
- package/exports/canvas-shaders.ts +28 -28
- package/exports/canvas.ts +45 -45
- package/exports/index.ts +82 -82
- package/exports/inspector.ts +24 -24
- package/exports/utils.ts +50 -50
- package/exports/webgl-shaders.ts +28 -28
- package/exports/webgl.ts +52 -52
- package/package.json +2 -1
- package/src/common/CommonTypes.ts +146 -146
- package/src/common/EventEmitter.ts +77 -77
- package/src/common/IAnimationController.ts +92 -92
- package/src/common/IEventEmitter.ts +28 -28
- package/src/core/CoreNode.test.ts +202 -202
- package/src/core/CoreNode.ts +2481 -2491
- package/src/core/CoreShaderManager.ts +188 -188
- package/src/core/CoreTextNode.ts +483 -443
- package/src/core/CoreTextureManager.ts +565 -565
- package/src/core/Stage.ts +906 -906
- package/src/core/TextureMemoryManager.ts +445 -445
- package/src/core/animations/AnimationManager.ts +38 -38
- package/src/core/animations/CoreAnimation.ts +291 -291
- package/src/core/animations/CoreAnimationController.ts +166 -166
- package/src/core/lib/ContextSpy.ts +41 -41
- package/src/core/lib/ImageWorker.ts +286 -286
- package/src/core/lib/Matrix3d.ts +244 -244
- package/src/core/lib/RenderCoords.ts +71 -71
- package/src/core/lib/WebGlContextWrapper.ts +1381 -1381
- package/src/core/lib/colorCache.ts +20 -20
- package/src/core/lib/colorParser.ts +85 -85
- package/src/core/lib/textureCompression.ts +152 -152
- package/src/core/lib/textureSvg.ts +78 -78
- package/src/core/lib/utils.ts +412 -412
- package/src/core/lib/validateImageBitmap.ts +87 -87
- package/src/core/platforms/Platform.ts +77 -77
- package/src/core/platforms/web/WebPlatform.ts +121 -121
- package/src/core/renderers/CoreContextTexture.ts +43 -43
- package/src/core/renderers/CoreRenderOp.ts +22 -22
- package/src/core/renderers/CoreRenderer.ts +110 -110
- package/src/core/renderers/CoreShaderNode.ts +175 -175
- package/src/core/renderers/CoreShaderProgram.ts +23 -23
- package/src/core/renderers/canvas/CanvasRenderer.ts +283 -283
- package/src/core/renderers/canvas/CanvasShaderNode.ts +96 -96
- package/src/core/renderers/canvas/CanvasTexture.ts +156 -156
- package/src/core/renderers/webgl/WebGlCtxRenderTexture.ts +81 -91
- package/src/core/renderers/webgl/WebGlCtxSubTexture.ts +95 -50
- package/src/core/renderers/webgl/WebGlCtxTexture.ts +301 -310
- package/src/core/renderers/webgl/WebGlRenderOp.ts +167 -167
- package/src/core/renderers/webgl/WebGlRenderer.ts +746 -747
- package/src/core/renderers/webgl/WebGlShaderNode.ts +435 -435
- package/src/core/renderers/webgl/WebGlShaderProgram.ts +341 -341
- package/src/core/renderers/webgl/internal/BufferCollection.ts +54 -54
- package/src/core/renderers/webgl/internal/RendererUtils.ts +155 -155
- package/src/core/renderers/webgl/internal/ShaderUtils.ts +281 -281
- package/src/core/renderers/webgl/internal/WebGlUtils.ts +35 -35
- package/src/core/shaders/canvas/Border.ts +75 -75
- package/src/core/shaders/canvas/HolePunch.ts +55 -62
- package/src/core/shaders/canvas/LinearGradient.ts +71 -71
- package/src/core/shaders/canvas/RadialGradient.ts +99 -99
- package/src/core/shaders/canvas/Rounded.ts +55 -55
- package/src/core/shaders/canvas/RoundedWithBorder.ts +72 -74
- package/src/core/shaders/canvas/RoundedWithBorderAndShadow.ts +88 -90
- package/src/core/shaders/canvas/RoundedWithShadow.ts +70 -70
- package/src/core/shaders/canvas/Shadow.ts +52 -52
- package/src/core/shaders/canvas/utils/render.ts +151 -151
- package/src/core/shaders/templates/BorderTemplate.ts +115 -115
- package/src/core/shaders/templates/HolePunchTemplate.ts +82 -82
- package/src/core/shaders/templates/LinearGradientTemplate.ts +71 -71
- package/src/core/shaders/templates/RadialGradientTemplate.ts +81 -81
- package/src/core/shaders/templates/RoundedTemplate.ts +98 -98
- package/src/core/shaders/templates/RoundedWithBorderAndShadowTemplate.ts +38 -38
- package/src/core/shaders/templates/RoundedWithBorderTemplate.ts +35 -35
- package/src/core/shaders/templates/RoundedWithShadowTemplate.ts +35 -35
- package/src/core/shaders/templates/ShadowTemplate.ts +106 -106
- package/src/core/shaders/utils.ts +46 -46
- package/src/core/shaders/webgl/Border.ts +116 -116
- package/src/core/shaders/webgl/Default.ts +89 -89
- package/src/core/shaders/webgl/DefaultBatched.ts +129 -129
- package/src/core/shaders/webgl/HolePunch.ts +75 -75
- package/src/core/shaders/webgl/LinearGradient.ts +82 -82
- package/src/core/shaders/webgl/RadialGradient.ts +85 -85
- package/src/core/shaders/webgl/Rounded.ts +113 -117
- package/src/core/shaders/webgl/RoundedWithBorder.ts +151 -155
- package/src/core/shaders/webgl/RoundedWithBorderAndShadow.ts +175 -175
- package/src/core/shaders/webgl/RoundedWithShadow.ts +94 -98
- package/src/core/shaders/webgl/SdfShader.ts +134 -134
- package/src/core/shaders/webgl/Shadow.ts +115 -115
- package/src/core/text-rendering/CanvasFontHandler.ts +210 -176
- package/src/core/text-rendering/CanvasTextRenderer.ts +622 -622
- package/src/core/text-rendering/SdfFontHandler.ts +554 -517
- package/src/core/text-rendering/SdfTextRenderer.ts +466 -466
- package/src/core/text-rendering/TextRenderer.ts +406 -404
- package/src/core/text-rendering/Utils.ts +257 -257
- package/src/core/text-rendering/canvas/Settings.ts +99 -99
- package/src/core/text-rendering/canvas/Utils.test.ts +206 -206
- package/src/core/text-rendering/canvas/Utils.ts +178 -178
- package/src/core/text-rendering/canvas/calculateRenderInfo.ts +299 -299
- package/src/core/text-rendering/canvas/draw.ts +165 -165
- package/src/core/text-rendering/sdf/Utils.test.ts +402 -402
- package/src/core/text-rendering/sdf/Utils.ts +436 -436
- package/src/core/text-rendering/sdf/index.ts +20 -20
- package/src/core/textures/ColorTexture.ts +102 -102
- package/src/core/textures/ImageTexture.ts +418 -418
- package/src/core/textures/NoiseTexture.ts +104 -104
- package/src/core/textures/RenderTexture.ts +85 -85
- package/src/core/textures/SubTexture.ts +205 -205
- package/src/core/textures/Texture.ts +381 -381
- package/src/core/utils.ts +227 -227
- package/src/env.d.ts +7 -7
- package/src/main-api/INode.ts +100 -100
- package/src/main-api/Inspector.ts +567 -567
- package/src/main-api/Renderer.ts +873 -873
- package/src/main-api/utils.ts +45 -45
- package/src/utils.ts +267 -267
- package/COPYING +0 -1
package/src/core/Stage.ts
CHANGED
|
@@ -1,906 +1,906 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* If not stated otherwise in this file or this component's LICENSE file the
|
|
3
|
-
* following copyright and licenses apply:
|
|
4
|
-
*
|
|
5
|
-
* Copyright 2023 Comcast Cable Communications Management, LLC.
|
|
6
|
-
*
|
|
7
|
-
* Licensed under the Apache License, Version 2.0 (the License);
|
|
8
|
-
* you may not use this file except in compliance with the License.
|
|
9
|
-
* You may obtain a copy of the License at
|
|
10
|
-
*
|
|
11
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
-
*
|
|
13
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
-
* See the License for the specific language governing permissions and
|
|
17
|
-
* limitations under the License.
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import { assertTruthy, setPremultiplyMode } from '../utils.js';
|
|
21
|
-
import { AnimationManager } from './animations/AnimationManager.js';
|
|
22
|
-
import {
|
|
23
|
-
UpdateType,
|
|
24
|
-
CoreNode,
|
|
25
|
-
CoreNodeRenderState,
|
|
26
|
-
type CoreNodeProps,
|
|
27
|
-
} from './CoreNode.js';
|
|
28
|
-
import { CoreTextureManager } from './CoreTextureManager.js';
|
|
29
|
-
import { CoreShaderManager } from './CoreShaderManager.js';
|
|
30
|
-
import {
|
|
31
|
-
type FontHandler,
|
|
32
|
-
type FontLoadOptions,
|
|
33
|
-
type TextRenderer,
|
|
34
|
-
type TextRenderers,
|
|
35
|
-
type TrProps,
|
|
36
|
-
} from './text-rendering/TextRenderer.js';
|
|
37
|
-
|
|
38
|
-
import { EventEmitter } from '../common/EventEmitter.js';
|
|
39
|
-
import { ContextSpy } from './lib/ContextSpy.js';
|
|
40
|
-
import type {
|
|
41
|
-
FpsUpdatePayload,
|
|
42
|
-
FrameTickPayload,
|
|
43
|
-
QuadsUpdatePayload,
|
|
44
|
-
} from '../common/CommonTypes.js';
|
|
45
|
-
import {
|
|
46
|
-
TextureMemoryManager,
|
|
47
|
-
type TextureMemoryManagerSettings,
|
|
48
|
-
} from './TextureMemoryManager.js';
|
|
49
|
-
import { CoreRenderer } from './renderers/CoreRenderer.js';
|
|
50
|
-
import { CoreTextNode, type CoreTextNodeProps } from './CoreTextNode.js';
|
|
51
|
-
import { santizeCustomDataMap } from '../main-api/utils.js';
|
|
52
|
-
import { pointInBound } from './lib/utils.js';
|
|
53
|
-
import type { CoreShaderNode } from './renderers/CoreShaderNode.js';
|
|
54
|
-
import { createBound, createPreloadBounds, type Bound } from './lib/utils.js';
|
|
55
|
-
import type { Texture } from './textures/Texture.js';
|
|
56
|
-
import { ColorTexture } from './textures/ColorTexture.js';
|
|
57
|
-
import type { Platform } from './platforms/Platform.js';
|
|
58
|
-
import type { WebPlatform } from './platforms/web/WebPlatform.js';
|
|
59
|
-
import type { RendererMainSettings } from '../main-api/Renderer.js';
|
|
60
|
-
|
|
61
|
-
export type StageOptions = Omit<
|
|
62
|
-
RendererMainSettings,
|
|
63
|
-
'inspector' | 'platform'
|
|
64
|
-
> & {
|
|
65
|
-
textureMemory: TextureMemoryManagerSettings;
|
|
66
|
-
canvas: HTMLCanvasElement | OffscreenCanvas;
|
|
67
|
-
fpsUpdateInterval: number;
|
|
68
|
-
eventBus: EventEmitter;
|
|
69
|
-
platform: Platform | WebPlatform;
|
|
70
|
-
inspector: boolean;
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
export type StageFpsUpdateHandler = (
|
|
74
|
-
stage: Stage,
|
|
75
|
-
fpsData: FpsUpdatePayload,
|
|
76
|
-
) => void;
|
|
77
|
-
|
|
78
|
-
export type StageFrameTickHandler = (
|
|
79
|
-
stage: Stage,
|
|
80
|
-
frameTickData: FrameTickPayload,
|
|
81
|
-
) => void;
|
|
82
|
-
|
|
83
|
-
export interface Point {
|
|
84
|
-
x: number;
|
|
85
|
-
y: number;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const autoStart = true;
|
|
89
|
-
|
|
90
|
-
export class Stage {
|
|
91
|
-
/// Module Instances
|
|
92
|
-
public readonly animationManager: AnimationManager;
|
|
93
|
-
public readonly txManager: CoreTextureManager;
|
|
94
|
-
public readonly txMemManager: TextureMemoryManager;
|
|
95
|
-
public readonly textRenderers: Record<string, TextRenderer> = {};
|
|
96
|
-
public readonly fontHandlers: Record<string, FontHandler> = {};
|
|
97
|
-
public readonly shManager: CoreShaderManager;
|
|
98
|
-
public readonly renderer: CoreRenderer;
|
|
99
|
-
public readonly root: CoreNode;
|
|
100
|
-
public readonly interactiveNodes: Set<CoreNode> = new Set();
|
|
101
|
-
public boundsMargin: [number, number, number, number];
|
|
102
|
-
public readonly defShaderNode: CoreShaderNode | null = null;
|
|
103
|
-
public strictBound: Bound;
|
|
104
|
-
public preloadBound: Bound;
|
|
105
|
-
public readonly strictBounds: boolean;
|
|
106
|
-
public readonly defaultTexture: Texture | null = null;
|
|
107
|
-
public pixelRatio: number;
|
|
108
|
-
public readonly bufferMemory: number = 2e6;
|
|
109
|
-
public readonly platform: Platform | WebPlatform;
|
|
110
|
-
public readonly calculateTextureCoord: boolean;
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Target frame time in milliseconds (calculated from targetFPS)
|
|
114
|
-
*
|
|
115
|
-
* @remarks
|
|
116
|
-
* This is pre-calculated to avoid recalculating on every frame.
|
|
117
|
-
* - 0 means no throttling (use display refresh rate)
|
|
118
|
-
* - >0 means throttle to this frame time (1000 / targetFPS)
|
|
119
|
-
*/
|
|
120
|
-
public targetFrameTime: number = 0;
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Renderer Event Bus for the Stage to emit events onto
|
|
124
|
-
*
|
|
125
|
-
* @remarks
|
|
126
|
-
* In reality this is just the RendererMain instance, which is an EventEmitter.
|
|
127
|
-
* this allows us to directly emit events from the Stage to RendererMain
|
|
128
|
-
* without having to set up forwarding handlers.
|
|
129
|
-
*/
|
|
130
|
-
public readonly eventBus: EventEmitter;
|
|
131
|
-
|
|
132
|
-
/// State
|
|
133
|
-
deltaTime = 0;
|
|
134
|
-
lastFrameTime = 0;
|
|
135
|
-
currentFrameTime = 0;
|
|
136
|
-
private clrColor = 0x00000000;
|
|
137
|
-
private fpsNumFrames = 0;
|
|
138
|
-
private fpsElapsedTime = 0;
|
|
139
|
-
private numQuadsRendered = 0;
|
|
140
|
-
private renderRequested = false;
|
|
141
|
-
private frameEventQueue: [name: string, payload: unknown][] = [];
|
|
142
|
-
|
|
143
|
-
// Font resolve optimisation flags
|
|
144
|
-
private hasOnlyOneFontEngine: boolean;
|
|
145
|
-
private hasOnlyCanvasFontEngine: boolean;
|
|
146
|
-
private hasCanvasEngine: boolean;
|
|
147
|
-
private singleFontEngine: TextRenderer | null = null;
|
|
148
|
-
private singleFontHandler: FontHandler | null = null;
|
|
149
|
-
|
|
150
|
-
// Debug data
|
|
151
|
-
contextSpy: ContextSpy | null = null;
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Stage constructor
|
|
155
|
-
*/
|
|
156
|
-
constructor(public options: StageOptions) {
|
|
157
|
-
const {
|
|
158
|
-
canvas,
|
|
159
|
-
clearColor,
|
|
160
|
-
appWidth,
|
|
161
|
-
appHeight,
|
|
162
|
-
boundsMargin,
|
|
163
|
-
enableContextSpy,
|
|
164
|
-
forceWebGL2,
|
|
165
|
-
numImageWorkers,
|
|
166
|
-
textureMemory,
|
|
167
|
-
renderEngine,
|
|
168
|
-
fontEngines,
|
|
169
|
-
createImageBitmapSupport,
|
|
170
|
-
platform,
|
|
171
|
-
} = options;
|
|
172
|
-
|
|
173
|
-
assertTruthy(
|
|
174
|
-
platform !== null,
|
|
175
|
-
'A CorePlatform is not provided in the options',
|
|
176
|
-
);
|
|
177
|
-
|
|
178
|
-
this.platform = platform;
|
|
179
|
-
|
|
180
|
-
this.eventBus = options.eventBus;
|
|
181
|
-
|
|
182
|
-
// Calculate target frame time from targetFPS option
|
|
183
|
-
this.targetFrameTime = options.targetFPS > 0 ? 1000 / options.targetFPS : 0;
|
|
184
|
-
|
|
185
|
-
this.txManager = new CoreTextureManager(this, {
|
|
186
|
-
numImageWorkers,
|
|
187
|
-
createImageBitmapSupport,
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
// Wait for the Texture Manager to initialize
|
|
191
|
-
// once it does, request a render
|
|
192
|
-
this.txManager.on('initialized', () => {
|
|
193
|
-
this.requestRender();
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
this.txMemManager = new TextureMemoryManager(this, textureMemory);
|
|
197
|
-
|
|
198
|
-
this.animationManager = new AnimationManager();
|
|
199
|
-
this.contextSpy = enableContextSpy ? new ContextSpy() : null;
|
|
200
|
-
this.strictBounds = options.strictBounds;
|
|
201
|
-
|
|
202
|
-
let bm = [0, 0, 0, 0] as [number, number, number, number];
|
|
203
|
-
if (boundsMargin) {
|
|
204
|
-
bm = Array.isArray(boundsMargin)
|
|
205
|
-
? boundsMargin
|
|
206
|
-
: [boundsMargin, boundsMargin, boundsMargin, boundsMargin];
|
|
207
|
-
}
|
|
208
|
-
this.boundsMargin = bm;
|
|
209
|
-
|
|
210
|
-
// precalculate our viewport bounds
|
|
211
|
-
this.strictBound = createBound(0, 0, appWidth, appHeight);
|
|
212
|
-
this.preloadBound = createPreloadBounds(this.strictBound, bm);
|
|
213
|
-
|
|
214
|
-
this.clrColor = clearColor;
|
|
215
|
-
|
|
216
|
-
this.pixelRatio =
|
|
217
|
-
options.devicePhysicalPixelRatio * options.deviceLogicalPixelRatio;
|
|
218
|
-
|
|
219
|
-
this.renderer = new renderEngine({
|
|
220
|
-
stage: this,
|
|
221
|
-
canvas,
|
|
222
|
-
contextSpy: this.contextSpy,
|
|
223
|
-
forceWebGL2,
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
this.shManager = new CoreShaderManager(this);
|
|
227
|
-
|
|
228
|
-
this.defShaderNode = this.renderer.getDefaultShaderNode();
|
|
229
|
-
this.calculateTextureCoord = this.renderer.getTextureCoords !== undefined;
|
|
230
|
-
|
|
231
|
-
const renderMode = this.renderer.mode || 'webgl';
|
|
232
|
-
|
|
233
|
-
this.createDefaultTexture();
|
|
234
|
-
setPremultiplyMode(renderMode);
|
|
235
|
-
|
|
236
|
-
// Must do this after renderer is created
|
|
237
|
-
this.txManager.renderer = this.renderer;
|
|
238
|
-
|
|
239
|
-
// Create text renderers
|
|
240
|
-
this.hasOnlyOneFontEngine = fontEngines.length === 1;
|
|
241
|
-
this.hasOnlyCanvasFontEngine =
|
|
242
|
-
fontEngines.length === 1 && fontEngines[0]!.type === 'canvas';
|
|
243
|
-
this.hasCanvasEngine = false;
|
|
244
|
-
this.singleFontEngine = this.hasOnlyOneFontEngine
|
|
245
|
-
? (fontEngines[0] as TextRenderer)
|
|
246
|
-
: null;
|
|
247
|
-
this.singleFontHandler = this.hasOnlyOneFontEngine
|
|
248
|
-
? (fontEngines[0]?.font as FontHandler)
|
|
249
|
-
: null;
|
|
250
|
-
|
|
251
|
-
if (this.singleFontEngine === null) {
|
|
252
|
-
// Multiple font engines case
|
|
253
|
-
// Filter out incompatible engines first
|
|
254
|
-
const compatibleEngines = fontEngines.filter(
|
|
255
|
-
(fontEngine: TextRenderer) => {
|
|
256
|
-
const type = fontEngine.type;
|
|
257
|
-
|
|
258
|
-
if (type === 'sdf' && renderMode === 'canvas') {
|
|
259
|
-
console.warn(
|
|
260
|
-
'MsdfTextRenderer is not compatible with Canvas renderer. Skipping...',
|
|
261
|
-
);
|
|
262
|
-
return false;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
if (type === 'canvas') {
|
|
266
|
-
this.hasCanvasEngine = true;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
return true;
|
|
270
|
-
},
|
|
271
|
-
);
|
|
272
|
-
|
|
273
|
-
// Sort engines: SDF first, Canvas last, others in between
|
|
274
|
-
const sortedEngines = compatibleEngines.sort(
|
|
275
|
-
(a: TextRenderer, b: TextRenderer) => {
|
|
276
|
-
if (a.type === 'sdf') return -1;
|
|
277
|
-
if (b.type === 'sdf') return 1;
|
|
278
|
-
if (a.type === 'canvas') return 1;
|
|
279
|
-
if (b.type === 'canvas') return -1;
|
|
280
|
-
return 0;
|
|
281
|
-
},
|
|
282
|
-
);
|
|
283
|
-
|
|
284
|
-
// Initialize engines in sorted order
|
|
285
|
-
sortedEngines.forEach((fontEngine: TextRenderer) => {
|
|
286
|
-
const type = fontEngine.type;
|
|
287
|
-
|
|
288
|
-
// Add to map for type-based access
|
|
289
|
-
this.textRenderers[type] = fontEngine;
|
|
290
|
-
this.textRenderers[type].init(this);
|
|
291
|
-
|
|
292
|
-
this.fontHandlers[type] = fontEngine.font;
|
|
293
|
-
});
|
|
294
|
-
} else {
|
|
295
|
-
// Single font engine case - initialize it directly
|
|
296
|
-
const fontEngine = this.singleFontEngine;
|
|
297
|
-
const type = fontEngine.type;
|
|
298
|
-
|
|
299
|
-
// Check compatibility
|
|
300
|
-
if (type === 'sdf' && renderMode === 'canvas') {
|
|
301
|
-
console.warn(
|
|
302
|
-
'MsdfTextRenderer is not compatible with Canvas renderer. Skipping...',
|
|
303
|
-
);
|
|
304
|
-
} else {
|
|
305
|
-
if (type === 'canvas') {
|
|
306
|
-
this.hasCanvasEngine = true;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// Add to map for type-based access
|
|
310
|
-
this.textRenderers[type] = fontEngine;
|
|
311
|
-
this.fontHandlers[type] = fontEngine.font;
|
|
312
|
-
this.textRenderers[type].init(this);
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
if (Object.keys(this.textRenderers).length === 0) {
|
|
317
|
-
console.warn('No text renderers available. Your text will not render.');
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// create root node
|
|
321
|
-
const rootNode = new CoreNode(this, {
|
|
322
|
-
x: 0,
|
|
323
|
-
y: 0,
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
alpha: 1,
|
|
327
|
-
autosize: false,
|
|
328
|
-
boundsMargin: null,
|
|
329
|
-
clipping: false,
|
|
330
|
-
color: 0x00000000,
|
|
331
|
-
colorTop: 0x00000000,
|
|
332
|
-
colorBottom: 0x00000000,
|
|
333
|
-
colorLeft: 0x00000000,
|
|
334
|
-
colorRight: 0x00000000,
|
|
335
|
-
colorTl: 0x00000000,
|
|
336
|
-
colorTr: 0x00000000,
|
|
337
|
-
colorBl: 0x00000000,
|
|
338
|
-
colorBr: 0x00000000,
|
|
339
|
-
zIndex: 0,
|
|
340
|
-
zIndexLocked: 0,
|
|
341
|
-
scaleX: 1,
|
|
342
|
-
scaleY: 1,
|
|
343
|
-
mountX: 0,
|
|
344
|
-
mountY: 0,
|
|
345
|
-
mount: 0,
|
|
346
|
-
pivot: 0.5,
|
|
347
|
-
pivotX: 0.5,
|
|
348
|
-
pivotY: 0.5,
|
|
349
|
-
rotation: 0,
|
|
350
|
-
parent: null,
|
|
351
|
-
texture: null,
|
|
352
|
-
textureOptions: {},
|
|
353
|
-
shader: this.defShaderNode,
|
|
354
|
-
rtt: false,
|
|
355
|
-
src: null,
|
|
356
|
-
scale: 1,
|
|
357
|
-
strictBounds: this.strictBounds,
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
this.root = rootNode;
|
|
361
|
-
|
|
362
|
-
// execute platform start loop
|
|
363
|
-
if (autoStart === true) {
|
|
364
|
-
this.platform.startLoop(this);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
setClearColor(color: number) {
|
|
369
|
-
this.clearColor = color;
|
|
370
|
-
this.renderer.updateClearColor(color);
|
|
371
|
-
this.renderRequested = true;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
/**
|
|
375
|
-
* Update the target frame time based on the current targetFPS setting
|
|
376
|
-
*
|
|
377
|
-
* @remarks
|
|
378
|
-
* This should be called whenever the targetFPS option is changed
|
|
379
|
-
* to ensure targetFrameTime stays in sync.
|
|
380
|
-
* targetFPS of 0 means no throttling (targetFrameTime = 0)
|
|
381
|
-
* targetFPS > 0 means throttle to 1000/targetFPS milliseconds
|
|
382
|
-
*/
|
|
383
|
-
updateTargetFrameTime() {
|
|
384
|
-
this.targetFrameTime =
|
|
385
|
-
this.options.targetFPS > 0 ? 1000 / this.options.targetFPS : 0;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
updateFrameTime() {
|
|
389
|
-
const newFrameTime = this.platform!.getTimeStamp();
|
|
390
|
-
this.lastFrameTime = this.currentFrameTime;
|
|
391
|
-
this.currentFrameTime = newFrameTime;
|
|
392
|
-
this.deltaTime = !this.lastFrameTime
|
|
393
|
-
? 100 / 6
|
|
394
|
-
: newFrameTime - this.lastFrameTime;
|
|
395
|
-
this.txManager.frameTime = newFrameTime;
|
|
396
|
-
this.txMemManager.frameTime = newFrameTime;
|
|
397
|
-
|
|
398
|
-
// This event is emitted at the beginning of the frame (before any updates
|
|
399
|
-
// or rendering), so no need to to use `stage.queueFrameEvent` here.
|
|
400
|
-
this.eventBus.emit('frameTick', {
|
|
401
|
-
time: this.currentFrameTime,
|
|
402
|
-
delta: this.deltaTime,
|
|
403
|
-
});
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
/**
|
|
407
|
-
* Create default PixelTexture
|
|
408
|
-
*/
|
|
409
|
-
createDefaultTexture() {
|
|
410
|
-
(this.defaultTexture as ColorTexture) = this.txManager.createTexture(
|
|
411
|
-
'ColorTexture',
|
|
412
|
-
{
|
|
413
|
-
color: 0xffffffff,
|
|
414
|
-
},
|
|
415
|
-
);
|
|
416
|
-
|
|
417
|
-
assertTruthy(this.defaultTexture instanceof ColorTexture);
|
|
418
|
-
this.txManager.loadTexture(this.defaultTexture, true);
|
|
419
|
-
|
|
420
|
-
// Mark the default texture as ALWAYS renderable
|
|
421
|
-
// This prevents it from ever being cleaned up.
|
|
422
|
-
// Fixes https://github.com/lightning-js/renderer/issues/262
|
|
423
|
-
this.defaultTexture.setRenderableOwner(this, true);
|
|
424
|
-
|
|
425
|
-
// When the default texture is loaded, request a render in case the
|
|
426
|
-
// RAF is paused. Fixes: https://github.com/lightning-js/renderer/issues/123
|
|
427
|
-
this.defaultTexture.once('loaded', () => {
|
|
428
|
-
this.requestRender();
|
|
429
|
-
});
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
/**
|
|
433
|
-
* Update animations
|
|
434
|
-
*/
|
|
435
|
-
updateAnimations() {
|
|
436
|
-
const { animationManager } = this;
|
|
437
|
-
if (!this.root) {
|
|
438
|
-
return;
|
|
439
|
-
}
|
|
440
|
-
// step animation
|
|
441
|
-
animationManager.update(this.deltaTime);
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
/**
|
|
445
|
-
* Check if the scene has updates
|
|
446
|
-
*/
|
|
447
|
-
hasSceneUpdates() {
|
|
448
|
-
return (
|
|
449
|
-
!!this.root.updateType ||
|
|
450
|
-
this.renderRequested ||
|
|
451
|
-
this.txManager.hasUpdates()
|
|
452
|
-
);
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
/**
|
|
456
|
-
* Start a new frame draw
|
|
457
|
-
*/
|
|
458
|
-
drawFrame() {
|
|
459
|
-
const { renderer, renderRequested, root } = this;
|
|
460
|
-
const txMemManager = this.txMemManager;
|
|
461
|
-
|
|
462
|
-
// Update tree if needed
|
|
463
|
-
if (root.updateType !== 0) {
|
|
464
|
-
root.update(this.deltaTime, root.clippingRect);
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
// Process some textures asynchronously but don't block the frame
|
|
468
|
-
// Use a background task to prevent frame drops
|
|
469
|
-
this.txManager
|
|
470
|
-
.processSome(this.options.textureProcessingTimeLimit)
|
|
471
|
-
.catch((err) => {
|
|
472
|
-
console.error('Error processing textures:', err);
|
|
473
|
-
});
|
|
474
|
-
|
|
475
|
-
// Reset render operations and clear the canvas
|
|
476
|
-
renderer.reset();
|
|
477
|
-
|
|
478
|
-
// Check if we need to cleanup textures
|
|
479
|
-
if (txMemManager.criticalCleanupRequested === true) {
|
|
480
|
-
txMemManager.cleanup(false);
|
|
481
|
-
|
|
482
|
-
if (txMemManager.criticalCleanupRequested === true) {
|
|
483
|
-
// If we still need to cleanup, request another but aggressive cleanup
|
|
484
|
-
txMemManager.cleanup(true);
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
// If we have RTT nodes draw them first
|
|
489
|
-
// So we can use them as textures in the main scene
|
|
490
|
-
if (renderer.rttNodes.length > 0) {
|
|
491
|
-
renderer.renderRTTNodes();
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
// Fill quads buffer
|
|
495
|
-
this.addQuads(this.root);
|
|
496
|
-
|
|
497
|
-
// Perform render pass
|
|
498
|
-
renderer.render();
|
|
499
|
-
|
|
500
|
-
this.calculateFps();
|
|
501
|
-
this.calculateQuads();
|
|
502
|
-
|
|
503
|
-
// Reset renderRequested flag if it was set
|
|
504
|
-
if (renderRequested === true) {
|
|
505
|
-
this.renderRequested = false;
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
/**
|
|
510
|
-
* Queue an event to be emitted after the current/next frame is rendered
|
|
511
|
-
*
|
|
512
|
-
* @remarks
|
|
513
|
-
* When we are operating in the context of the render loop, we may want to
|
|
514
|
-
* emit events that are related to the current frame. However, we generally do
|
|
515
|
-
* NOT want to emit events directly in the middle of the render loop, since
|
|
516
|
-
* this could enable event handlers to modify the scene graph and cause
|
|
517
|
-
* unexpected behavior. Instead, we queue up events to be emitted and then
|
|
518
|
-
* flush the queue after the frame has been rendered.
|
|
519
|
-
*
|
|
520
|
-
* @param name
|
|
521
|
-
* @param data
|
|
522
|
-
*/
|
|
523
|
-
queueFrameEvent(name: string, data: unknown) {
|
|
524
|
-
this.frameEventQueue.push([name, data]);
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
/**
|
|
528
|
-
* Emit all queued frame events
|
|
529
|
-
*
|
|
530
|
-
* @remarks
|
|
531
|
-
* This method should be called after the frame has been rendered to emit
|
|
532
|
-
* all events that were queued during the frame.
|
|
533
|
-
*
|
|
534
|
-
* See {@link queueFrameEvent} for more information.
|
|
535
|
-
*/
|
|
536
|
-
flushFrameEvents() {
|
|
537
|
-
for (const [name, data] of this.frameEventQueue) {
|
|
538
|
-
this.eventBus.emit(name, data);
|
|
539
|
-
}
|
|
540
|
-
this.frameEventQueue = [];
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
calculateFps() {
|
|
544
|
-
// If there's an FPS update interval, emit the FPS update event
|
|
545
|
-
// when the specified interval has elapsed.
|
|
546
|
-
const { fpsUpdateInterval } = this.options;
|
|
547
|
-
if (fpsUpdateInterval) {
|
|
548
|
-
this.fpsNumFrames++;
|
|
549
|
-
this.fpsElapsedTime += this.deltaTime;
|
|
550
|
-
if (this.fpsElapsedTime >= fpsUpdateInterval) {
|
|
551
|
-
const fps = Math.round(
|
|
552
|
-
(this.fpsNumFrames * 1000) / this.fpsElapsedTime,
|
|
553
|
-
);
|
|
554
|
-
this.fpsNumFrames = 0;
|
|
555
|
-
this.fpsElapsedTime = 0;
|
|
556
|
-
this.queueFrameEvent('fpsUpdate', {
|
|
557
|
-
fps,
|
|
558
|
-
contextSpyData: this.contextSpy?.getData() ?? null,
|
|
559
|
-
} satisfies FpsUpdatePayload);
|
|
560
|
-
this.contextSpy?.reset();
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
calculateQuads() {
|
|
566
|
-
const quads = this.renderer.getQuadCount();
|
|
567
|
-
if (quads && quads !== this.numQuadsRendered) {
|
|
568
|
-
this.numQuadsRendered = quads;
|
|
569
|
-
this.queueFrameEvent('quadsUpdate', {
|
|
570
|
-
quads,
|
|
571
|
-
} satisfies QuadsUpdatePayload);
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
addQuads(node: CoreNode) {
|
|
576
|
-
assertTruthy(this.renderer);
|
|
577
|
-
|
|
578
|
-
// If the node is renderable and has a loaded texture, render it
|
|
579
|
-
if (node.isRenderable === true) {
|
|
580
|
-
node.renderQuads(this.renderer);
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
for (let i = 0; i < node.children.length; i++) {
|
|
584
|
-
const child = node.children[i];
|
|
585
|
-
|
|
586
|
-
if (child === undefined) {
|
|
587
|
-
continue;
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
if (
|
|
591
|
-
child.worldAlpha === 0 ||
|
|
592
|
-
(child.strictBounds === true &&
|
|
593
|
-
child.renderState === CoreNodeRenderState.OutOfBounds)
|
|
594
|
-
) {
|
|
595
|
-
continue;
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
this.addQuads(child);
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
/**
|
|
603
|
-
* Request a render pass without forcing an update
|
|
604
|
-
*/
|
|
605
|
-
requestRender() {
|
|
606
|
-
this.renderRequested = true;
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
/**
|
|
610
|
-
* Given a font name, and possible renderer override, return the best compatible text renderer.
|
|
611
|
-
*
|
|
612
|
-
* @remarks
|
|
613
|
-
* Will try to return a canvas renderer if no other suitable renderer can be resolved.
|
|
614
|
-
*
|
|
615
|
-
* @param fontFamily
|
|
616
|
-
* @param textRendererOverride
|
|
617
|
-
* @returns
|
|
618
|
-
*/
|
|
619
|
-
resolveTextRenderer(
|
|
620
|
-
trProps: TrProps,
|
|
621
|
-
textRendererOverride: keyof TextRenderers | null = null,
|
|
622
|
-
): TextRenderer | null {
|
|
623
|
-
// If we have an overide, return it
|
|
624
|
-
if (textRendererOverride !== null) {
|
|
625
|
-
const overrideKey = String(textRendererOverride);
|
|
626
|
-
if (this.textRenderers[overrideKey] === undefined) {
|
|
627
|
-
console.warn(`Text renderer override '${overrideKey}' not found.`);
|
|
628
|
-
return null;
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
return this.textRenderers[overrideKey];
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
// If we have only one font engine early return it
|
|
635
|
-
if (this.singleFontEngine !== null) {
|
|
636
|
-
// If we have only one font engine and its the canvas engine, we can just return it
|
|
637
|
-
if (this.hasOnlyCanvasFontEngine === true) {
|
|
638
|
-
return this.singleFontEngine;
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
// If we have only one font engine and it can render the font, return it
|
|
642
|
-
if (this.singleFontHandler?.canRenderFont(trProps) === true) {
|
|
643
|
-
return this.singleFontEngine;
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
// If we have only one font engine and it cannot render the font, return null
|
|
647
|
-
console.warn(`Text renderer cannot render font`, trProps);
|
|
648
|
-
|
|
649
|
-
return null;
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
// Multi font handling - If we have multiple font engines, we need to resolve the best one
|
|
653
|
-
|
|
654
|
-
// First check SDF
|
|
655
|
-
if (this.fontHandlers['sdf']?.canRenderFont(trProps) === true) {
|
|
656
|
-
return this.textRenderers.sdf || null;
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
// If we have a canvas engine, we can return it (it can render all fonts)
|
|
660
|
-
if (this.hasCanvasEngine === true) {
|
|
661
|
-
return this.textRenderers.canvas || null;
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
// If we have no font engines, return null
|
|
665
|
-
console.warn('No text renderers available. Your text will not render.');
|
|
666
|
-
return null;
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
createNode(props: Partial<CoreNodeProps>) {
|
|
670
|
-
const resolvedProps = this.resolveNodeDefaults(props);
|
|
671
|
-
return new CoreNode(this, resolvedProps);
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
createTextNode(props: Partial<CoreTextNodeProps>) {
|
|
675
|
-
const fontSize = props.fontSize || 16;
|
|
676
|
-
const resolvedProps = Object.assign(this.resolveNodeDefaults(props), {
|
|
677
|
-
text: props.text || '',
|
|
678
|
-
textRendererOverride: props.textRendererOverride || null,
|
|
679
|
-
fontSize,
|
|
680
|
-
fontFamily: props.fontFamily || 'sans-serif',
|
|
681
|
-
fontStyle: props.fontStyle || 'normal',
|
|
682
|
-
textAlign: props.textAlign || 'left',
|
|
683
|
-
offsetY: props.offsetY || 0,
|
|
684
|
-
letterSpacing: props.letterSpacing || 0,
|
|
685
|
-
lineHeight: props.lineHeight || 0,
|
|
686
|
-
maxLines: props.maxLines || 0,
|
|
687
|
-
textBaseline: props.textBaseline || 'alphabetic',
|
|
688
|
-
verticalAlign: props.verticalAlign || 'middle',
|
|
689
|
-
overflowSuffix: props.overflowSuffix || '...',
|
|
690
|
-
wordBreak: props.wordBreak || 'normal',
|
|
691
|
-
maxWidth: props.maxWidth || 0,
|
|
692
|
-
maxHeight: props.maxHeight || 0,
|
|
693
|
-
forceLoad: props.forceLoad || false,
|
|
694
|
-
});
|
|
695
|
-
|
|
696
|
-
const resolvedTextRenderer = this.resolveTextRenderer(
|
|
697
|
-
resolvedProps,
|
|
698
|
-
resolvedProps.textRendererOverride as keyof TextRenderers | null,
|
|
699
|
-
);
|
|
700
|
-
|
|
701
|
-
if (!resolvedTextRenderer) {
|
|
702
|
-
throw new Error(
|
|
703
|
-
`No compatible text renderer found for ${resolvedProps.fontFamily}`,
|
|
704
|
-
);
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
return new CoreTextNode(this, resolvedProps, resolvedTextRenderer);
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
setBoundsMargin(value: number | [number, number, number, number]) {
|
|
711
|
-
this.boundsMargin = Array.isArray(value)
|
|
712
|
-
? value
|
|
713
|
-
: [value, value, value, value];
|
|
714
|
-
|
|
715
|
-
this.root.setUpdateType(UpdateType.RenderBounds);
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
/**
|
|
719
|
-
* Update the viewport bounds
|
|
720
|
-
*/
|
|
721
|
-
updateViewportBounds() {
|
|
722
|
-
const { appWidth, appHeight } = this.options;
|
|
723
|
-
this.strictBound = createBound(0, 0, appWidth, appHeight);
|
|
724
|
-
this.preloadBound = createPreloadBounds(
|
|
725
|
-
this.strictBound,
|
|
726
|
-
this.boundsMargin,
|
|
727
|
-
);
|
|
728
|
-
this.root.setUpdateType(UpdateType.RenderBounds | UpdateType.Children);
|
|
729
|
-
this.root.childUpdateType |= UpdateType.RenderBounds;
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
/** Find all nodes at a given point
|
|
733
|
-
* @param data
|
|
734
|
-
*/
|
|
735
|
-
findNodesAtPoint(data: Point): CoreNode[] {
|
|
736
|
-
const x = data.x / this.options.deviceLogicalPixelRatio;
|
|
737
|
-
const y = data.y / this.options.deviceLogicalPixelRatio;
|
|
738
|
-
const nodes: CoreNode[] = [];
|
|
739
|
-
for (const node of this.interactiveNodes) {
|
|
740
|
-
if (node.isRenderable === false) {
|
|
741
|
-
continue;
|
|
742
|
-
}
|
|
743
|
-
if (pointInBound(x, y, node.renderBound!) === true) {
|
|
744
|
-
nodes.push(node);
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
return nodes;
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
/**
|
|
751
|
-
* Find the top node at a given point
|
|
752
|
-
* @param data
|
|
753
|
-
* @returns
|
|
754
|
-
*/
|
|
755
|
-
getNodeFromPosition(data: Point): CoreNode | null {
|
|
756
|
-
const nodes: CoreNode[] = this.findNodesAtPoint(data);
|
|
757
|
-
if (nodes.length === 0) {
|
|
758
|
-
return null;
|
|
759
|
-
}
|
|
760
|
-
let topNode = nodes[0] as CoreNode;
|
|
761
|
-
for (let i = 0; i < nodes.length; i++) {
|
|
762
|
-
if (nodes[i]!.zIndex > topNode.zIndex) {
|
|
763
|
-
topNode = nodes[i]!;
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
return topNode || null;
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
/**
|
|
770
|
-
* Resolves the default property values for a Node
|
|
771
|
-
*
|
|
772
|
-
* @remarks
|
|
773
|
-
* This method is used internally by the RendererMain to resolve the default
|
|
774
|
-
* property values for a Node. It is exposed publicly so that it can be used
|
|
775
|
-
* by Core Driver implementations.
|
|
776
|
-
*
|
|
777
|
-
* @param props
|
|
778
|
-
* @returns
|
|
779
|
-
*/
|
|
780
|
-
protected resolveNodeDefaults(props: Partial<CoreNodeProps>): CoreNodeProps {
|
|
781
|
-
const {
|
|
782
|
-
colorTop: top,
|
|
783
|
-
colorBottom: bottom,
|
|
784
|
-
colorLeft: left,
|
|
785
|
-
colorRight: right,
|
|
786
|
-
} = props;
|
|
787
|
-
|
|
788
|
-
const color = props.color ?? 0xffffffff;
|
|
789
|
-
const colorTop = top ?? color;
|
|
790
|
-
const colorBottom = bottom ?? color;
|
|
791
|
-
const colorLeft = left ?? color;
|
|
792
|
-
const colorRight = right ?? color;
|
|
793
|
-
|
|
794
|
-
const colorTl = props.colorTl ?? top ?? left ?? color;
|
|
795
|
-
const colorTr = props.colorTr ?? top ?? right ?? color;
|
|
796
|
-
const colorBl = props.colorBl ?? bottom ?? left ?? color;
|
|
797
|
-
const colorBr = props.colorBr ?? bottom ?? right ?? color;
|
|
798
|
-
|
|
799
|
-
const scale = props.scale ?? null;
|
|
800
|
-
const mount = props.mount ?? 0;
|
|
801
|
-
const pivot = props.pivot ?? 0.5;
|
|
802
|
-
|
|
803
|
-
const data = this.options.inspector
|
|
804
|
-
? santizeCustomDataMap(props.data ?? {})
|
|
805
|
-
: {};
|
|
806
|
-
|
|
807
|
-
return {
|
|
808
|
-
x: props.x ?? 0,
|
|
809
|
-
y: props.y ?? 0,
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
alpha: props.alpha ?? 1,
|
|
813
|
-
autosize: props.autosize ?? false,
|
|
814
|
-
boundsMargin: props.boundsMargin ?? null,
|
|
815
|
-
clipping: props.clipping ?? false,
|
|
816
|
-
color,
|
|
817
|
-
colorTop,
|
|
818
|
-
colorBottom,
|
|
819
|
-
colorLeft,
|
|
820
|
-
colorRight,
|
|
821
|
-
colorTl,
|
|
822
|
-
colorTr,
|
|
823
|
-
colorBl,
|
|
824
|
-
colorBr,
|
|
825
|
-
zIndex: props.zIndex ?? 0,
|
|
826
|
-
zIndexLocked: props.zIndexLocked ?? 0,
|
|
827
|
-
parent: props.parent ?? null,
|
|
828
|
-
texture: props.texture ?? null,
|
|
829
|
-
textureOptions: props.textureOptions ?? {},
|
|
830
|
-
shader: props.shader ?? this.defShaderNode,
|
|
831
|
-
src: props.src ?? null,
|
|
832
|
-
srcHeight: props.srcHeight,
|
|
833
|
-
srcWidth: props.srcWidth,
|
|
834
|
-
srcX: props.srcX,
|
|
835
|
-
srcY: props.srcY,
|
|
836
|
-
scale,
|
|
837
|
-
scaleX: props.scaleX ?? scale ?? 1,
|
|
838
|
-
scaleY: props.scaleY ?? scale ?? 1,
|
|
839
|
-
mount,
|
|
840
|
-
mountX: props.mountX ?? mount,
|
|
841
|
-
mountY: props.mountY ?? mount,
|
|
842
|
-
pivot,
|
|
843
|
-
pivotX: props.pivotX ?? pivot,
|
|
844
|
-
pivotY: props.pivotY ?? pivot,
|
|
845
|
-
rotation: props.rotation ?? 0,
|
|
846
|
-
rtt: props.rtt ?? false,
|
|
847
|
-
data,
|
|
848
|
-
imageType: props.imageType,
|
|
849
|
-
interactive: props.interactive ?? false,
|
|
850
|
-
strictBounds: props.strictBounds ?? this.strictBounds,
|
|
851
|
-
};
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
/**
|
|
855
|
-
* Cleanup Orphaned Textures
|
|
856
|
-
*
|
|
857
|
-
* @remarks
|
|
858
|
-
* This method is used to cleanup orphaned textures that are no longer in use.
|
|
859
|
-
*/
|
|
860
|
-
cleanup(aggressive: boolean) {
|
|
861
|
-
this.txMemManager.cleanup(aggressive);
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
set clearColor(value: number) {
|
|
865
|
-
this.renderer.updateClearColor(value);
|
|
866
|
-
this.renderRequested = true;
|
|
867
|
-
this.clrColor = value;
|
|
868
|
-
}
|
|
869
|
-
|
|
870
|
-
get clearColor() {
|
|
871
|
-
return this.clrColor;
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
/**
|
|
875
|
-
* Load a font using a specific text renderer type
|
|
876
|
-
*
|
|
877
|
-
* @remarks
|
|
878
|
-
* This method allows consumers to explicitly load fonts for a specific
|
|
879
|
-
* text renderer type (e.g., 'canvas', 'sdf'). Consumers must specify
|
|
880
|
-
* the renderer type to ensure fonts are loaded with the correct pipeline.
|
|
881
|
-
*
|
|
882
|
-
* For Canvas fonts, provide fontUrl (e.g., .ttf, .woff, .woff2)
|
|
883
|
-
* For SDF fonts, provide atlasUrl (image) and atlasDataUrl (JSON glyph data)
|
|
884
|
-
*
|
|
885
|
-
* @param rendererType - The type of text renderer ('canvas', 'sdf', etc.)
|
|
886
|
-
* @param options - Font loading options specific to the renderer type
|
|
887
|
-
* @returns Promise that resolves when the font is loaded
|
|
888
|
-
*/
|
|
889
|
-
async loadFont(
|
|
890
|
-
rendererType: TextRenderers,
|
|
891
|
-
options: FontLoadOptions,
|
|
892
|
-
): Promise<void> {
|
|
893
|
-
const rendererTypeKey = String(rendererType);
|
|
894
|
-
const fontHandler = this.fontHandlers[rendererTypeKey];
|
|
895
|
-
|
|
896
|
-
if (!fontHandler) {
|
|
897
|
-
throw new Error(
|
|
898
|
-
`Font handler for renderer type '${rendererTypeKey}' not found. Available types: ${Object.keys(
|
|
899
|
-
this.fontHandlers,
|
|
900
|
-
).join(', ')}`,
|
|
901
|
-
);
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
return fontHandler.loadFont(this, options);
|
|
905
|
-
}
|
|
906
|
-
}
|
|
1
|
+
/*
|
|
2
|
+
* If not stated otherwise in this file or this component's LICENSE file the
|
|
3
|
+
* following copyright and licenses apply:
|
|
4
|
+
*
|
|
5
|
+
* Copyright 2023 Comcast Cable Communications Management, LLC.
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the Apache License, Version 2.0 (the License);
|
|
8
|
+
* you may not use this file except in compliance with the License.
|
|
9
|
+
* You may obtain a copy of the License at
|
|
10
|
+
*
|
|
11
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
*
|
|
13
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
* See the License for the specific language governing permissions and
|
|
17
|
+
* limitations under the License.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { assertTruthy, setPremultiplyMode } from '../utils.js';
|
|
21
|
+
import { AnimationManager } from './animations/AnimationManager.js';
|
|
22
|
+
import {
|
|
23
|
+
UpdateType,
|
|
24
|
+
CoreNode,
|
|
25
|
+
CoreNodeRenderState,
|
|
26
|
+
type CoreNodeProps,
|
|
27
|
+
} from './CoreNode.js';
|
|
28
|
+
import { CoreTextureManager } from './CoreTextureManager.js';
|
|
29
|
+
import { CoreShaderManager } from './CoreShaderManager.js';
|
|
30
|
+
import {
|
|
31
|
+
type FontHandler,
|
|
32
|
+
type FontLoadOptions,
|
|
33
|
+
type TextRenderer,
|
|
34
|
+
type TextRenderers,
|
|
35
|
+
type TrProps,
|
|
36
|
+
} from './text-rendering/TextRenderer.js';
|
|
37
|
+
|
|
38
|
+
import { EventEmitter } from '../common/EventEmitter.js';
|
|
39
|
+
import { ContextSpy } from './lib/ContextSpy.js';
|
|
40
|
+
import type {
|
|
41
|
+
FpsUpdatePayload,
|
|
42
|
+
FrameTickPayload,
|
|
43
|
+
QuadsUpdatePayload,
|
|
44
|
+
} from '../common/CommonTypes.js';
|
|
45
|
+
import {
|
|
46
|
+
TextureMemoryManager,
|
|
47
|
+
type TextureMemoryManagerSettings,
|
|
48
|
+
} from './TextureMemoryManager.js';
|
|
49
|
+
import { CoreRenderer } from './renderers/CoreRenderer.js';
|
|
50
|
+
import { CoreTextNode, type CoreTextNodeProps } from './CoreTextNode.js';
|
|
51
|
+
import { santizeCustomDataMap } from '../main-api/utils.js';
|
|
52
|
+
import { pointInBound } from './lib/utils.js';
|
|
53
|
+
import type { CoreShaderNode } from './renderers/CoreShaderNode.js';
|
|
54
|
+
import { createBound, createPreloadBounds, type Bound } from './lib/utils.js';
|
|
55
|
+
import type { Texture } from './textures/Texture.js';
|
|
56
|
+
import { ColorTexture } from './textures/ColorTexture.js';
|
|
57
|
+
import type { Platform } from './platforms/Platform.js';
|
|
58
|
+
import type { WebPlatform } from './platforms/web/WebPlatform.js';
|
|
59
|
+
import type { RendererMainSettings } from '../main-api/Renderer.js';
|
|
60
|
+
|
|
61
|
+
export type StageOptions = Omit<
|
|
62
|
+
RendererMainSettings,
|
|
63
|
+
'inspector' | 'platform'
|
|
64
|
+
> & {
|
|
65
|
+
textureMemory: TextureMemoryManagerSettings;
|
|
66
|
+
canvas: HTMLCanvasElement | OffscreenCanvas;
|
|
67
|
+
fpsUpdateInterval: number;
|
|
68
|
+
eventBus: EventEmitter;
|
|
69
|
+
platform: Platform | WebPlatform;
|
|
70
|
+
inspector: boolean;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export type StageFpsUpdateHandler = (
|
|
74
|
+
stage: Stage,
|
|
75
|
+
fpsData: FpsUpdatePayload,
|
|
76
|
+
) => void;
|
|
77
|
+
|
|
78
|
+
export type StageFrameTickHandler = (
|
|
79
|
+
stage: Stage,
|
|
80
|
+
frameTickData: FrameTickPayload,
|
|
81
|
+
) => void;
|
|
82
|
+
|
|
83
|
+
export interface Point {
|
|
84
|
+
x: number;
|
|
85
|
+
y: number;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const autoStart = true;
|
|
89
|
+
|
|
90
|
+
export class Stage {
|
|
91
|
+
/// Module Instances
|
|
92
|
+
public readonly animationManager: AnimationManager;
|
|
93
|
+
public readonly txManager: CoreTextureManager;
|
|
94
|
+
public readonly txMemManager: TextureMemoryManager;
|
|
95
|
+
public readonly textRenderers: Record<string, TextRenderer> = {};
|
|
96
|
+
public readonly fontHandlers: Record<string, FontHandler> = {};
|
|
97
|
+
public readonly shManager: CoreShaderManager;
|
|
98
|
+
public readonly renderer: CoreRenderer;
|
|
99
|
+
public readonly root: CoreNode;
|
|
100
|
+
public readonly interactiveNodes: Set<CoreNode> = new Set();
|
|
101
|
+
public boundsMargin: [number, number, number, number];
|
|
102
|
+
public readonly defShaderNode: CoreShaderNode | null = null;
|
|
103
|
+
public strictBound: Bound;
|
|
104
|
+
public preloadBound: Bound;
|
|
105
|
+
public readonly strictBounds: boolean;
|
|
106
|
+
public readonly defaultTexture: Texture | null = null;
|
|
107
|
+
public pixelRatio: number;
|
|
108
|
+
public readonly bufferMemory: number = 2e6;
|
|
109
|
+
public readonly platform: Platform | WebPlatform;
|
|
110
|
+
public readonly calculateTextureCoord: boolean;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Target frame time in milliseconds (calculated from targetFPS)
|
|
114
|
+
*
|
|
115
|
+
* @remarks
|
|
116
|
+
* This is pre-calculated to avoid recalculating on every frame.
|
|
117
|
+
* - 0 means no throttling (use display refresh rate)
|
|
118
|
+
* - >0 means throttle to this frame time (1000 / targetFPS)
|
|
119
|
+
*/
|
|
120
|
+
public targetFrameTime: number = 0;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Renderer Event Bus for the Stage to emit events onto
|
|
124
|
+
*
|
|
125
|
+
* @remarks
|
|
126
|
+
* In reality this is just the RendererMain instance, which is an EventEmitter.
|
|
127
|
+
* this allows us to directly emit events from the Stage to RendererMain
|
|
128
|
+
* without having to set up forwarding handlers.
|
|
129
|
+
*/
|
|
130
|
+
public readonly eventBus: EventEmitter;
|
|
131
|
+
|
|
132
|
+
/// State
|
|
133
|
+
deltaTime = 0;
|
|
134
|
+
lastFrameTime = 0;
|
|
135
|
+
currentFrameTime = 0;
|
|
136
|
+
private clrColor = 0x00000000;
|
|
137
|
+
private fpsNumFrames = 0;
|
|
138
|
+
private fpsElapsedTime = 0;
|
|
139
|
+
private numQuadsRendered = 0;
|
|
140
|
+
private renderRequested = false;
|
|
141
|
+
private frameEventQueue: [name: string, payload: unknown][] = [];
|
|
142
|
+
|
|
143
|
+
// Font resolve optimisation flags
|
|
144
|
+
private hasOnlyOneFontEngine: boolean;
|
|
145
|
+
private hasOnlyCanvasFontEngine: boolean;
|
|
146
|
+
private hasCanvasEngine: boolean;
|
|
147
|
+
private singleFontEngine: TextRenderer | null = null;
|
|
148
|
+
private singleFontHandler: FontHandler | null = null;
|
|
149
|
+
|
|
150
|
+
// Debug data
|
|
151
|
+
contextSpy: ContextSpy | null = null;
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Stage constructor
|
|
155
|
+
*/
|
|
156
|
+
constructor(public options: StageOptions) {
|
|
157
|
+
const {
|
|
158
|
+
canvas,
|
|
159
|
+
clearColor,
|
|
160
|
+
appWidth,
|
|
161
|
+
appHeight,
|
|
162
|
+
boundsMargin,
|
|
163
|
+
enableContextSpy,
|
|
164
|
+
forceWebGL2,
|
|
165
|
+
numImageWorkers,
|
|
166
|
+
textureMemory,
|
|
167
|
+
renderEngine,
|
|
168
|
+
fontEngines,
|
|
169
|
+
createImageBitmapSupport,
|
|
170
|
+
platform,
|
|
171
|
+
} = options;
|
|
172
|
+
|
|
173
|
+
assertTruthy(
|
|
174
|
+
platform !== null,
|
|
175
|
+
'A CorePlatform is not provided in the options',
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
this.platform = platform;
|
|
179
|
+
|
|
180
|
+
this.eventBus = options.eventBus;
|
|
181
|
+
|
|
182
|
+
// Calculate target frame time from targetFPS option
|
|
183
|
+
this.targetFrameTime = options.targetFPS > 0 ? 1000 / options.targetFPS : 0;
|
|
184
|
+
|
|
185
|
+
this.txManager = new CoreTextureManager(this, {
|
|
186
|
+
numImageWorkers,
|
|
187
|
+
createImageBitmapSupport,
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// Wait for the Texture Manager to initialize
|
|
191
|
+
// once it does, request a render
|
|
192
|
+
this.txManager.on('initialized', () => {
|
|
193
|
+
this.requestRender();
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
this.txMemManager = new TextureMemoryManager(this, textureMemory);
|
|
197
|
+
|
|
198
|
+
this.animationManager = new AnimationManager();
|
|
199
|
+
this.contextSpy = enableContextSpy ? new ContextSpy() : null;
|
|
200
|
+
this.strictBounds = options.strictBounds;
|
|
201
|
+
|
|
202
|
+
let bm = [0, 0, 0, 0] as [number, number, number, number];
|
|
203
|
+
if (boundsMargin) {
|
|
204
|
+
bm = Array.isArray(boundsMargin)
|
|
205
|
+
? boundsMargin
|
|
206
|
+
: [boundsMargin, boundsMargin, boundsMargin, boundsMargin];
|
|
207
|
+
}
|
|
208
|
+
this.boundsMargin = bm;
|
|
209
|
+
|
|
210
|
+
// precalculate our viewport bounds
|
|
211
|
+
this.strictBound = createBound(0, 0, appWidth, appHeight);
|
|
212
|
+
this.preloadBound = createPreloadBounds(this.strictBound, bm);
|
|
213
|
+
|
|
214
|
+
this.clrColor = clearColor;
|
|
215
|
+
|
|
216
|
+
this.pixelRatio =
|
|
217
|
+
options.devicePhysicalPixelRatio * options.deviceLogicalPixelRatio;
|
|
218
|
+
|
|
219
|
+
this.renderer = new renderEngine({
|
|
220
|
+
stage: this,
|
|
221
|
+
canvas,
|
|
222
|
+
contextSpy: this.contextSpy,
|
|
223
|
+
forceWebGL2,
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
this.shManager = new CoreShaderManager(this);
|
|
227
|
+
|
|
228
|
+
this.defShaderNode = this.renderer.getDefaultShaderNode();
|
|
229
|
+
this.calculateTextureCoord = this.renderer.getTextureCoords !== undefined;
|
|
230
|
+
|
|
231
|
+
const renderMode = this.renderer.mode || 'webgl';
|
|
232
|
+
|
|
233
|
+
this.createDefaultTexture();
|
|
234
|
+
setPremultiplyMode(renderMode);
|
|
235
|
+
|
|
236
|
+
// Must do this after renderer is created
|
|
237
|
+
this.txManager.renderer = this.renderer;
|
|
238
|
+
|
|
239
|
+
// Create text renderers
|
|
240
|
+
this.hasOnlyOneFontEngine = fontEngines.length === 1;
|
|
241
|
+
this.hasOnlyCanvasFontEngine =
|
|
242
|
+
fontEngines.length === 1 && fontEngines[0]!.type === 'canvas';
|
|
243
|
+
this.hasCanvasEngine = false;
|
|
244
|
+
this.singleFontEngine = this.hasOnlyOneFontEngine
|
|
245
|
+
? (fontEngines[0] as TextRenderer)
|
|
246
|
+
: null;
|
|
247
|
+
this.singleFontHandler = this.hasOnlyOneFontEngine
|
|
248
|
+
? (fontEngines[0]?.font as FontHandler)
|
|
249
|
+
: null;
|
|
250
|
+
|
|
251
|
+
if (this.singleFontEngine === null) {
|
|
252
|
+
// Multiple font engines case
|
|
253
|
+
// Filter out incompatible engines first
|
|
254
|
+
const compatibleEngines = fontEngines.filter(
|
|
255
|
+
(fontEngine: TextRenderer) => {
|
|
256
|
+
const type = fontEngine.type;
|
|
257
|
+
|
|
258
|
+
if (type === 'sdf' && renderMode === 'canvas') {
|
|
259
|
+
console.warn(
|
|
260
|
+
'MsdfTextRenderer is not compatible with Canvas renderer. Skipping...',
|
|
261
|
+
);
|
|
262
|
+
return false;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (type === 'canvas') {
|
|
266
|
+
this.hasCanvasEngine = true;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return true;
|
|
270
|
+
},
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
// Sort engines: SDF first, Canvas last, others in between
|
|
274
|
+
const sortedEngines = compatibleEngines.sort(
|
|
275
|
+
(a: TextRenderer, b: TextRenderer) => {
|
|
276
|
+
if (a.type === 'sdf') return -1;
|
|
277
|
+
if (b.type === 'sdf') return 1;
|
|
278
|
+
if (a.type === 'canvas') return 1;
|
|
279
|
+
if (b.type === 'canvas') return -1;
|
|
280
|
+
return 0;
|
|
281
|
+
},
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
// Initialize engines in sorted order
|
|
285
|
+
sortedEngines.forEach((fontEngine: TextRenderer) => {
|
|
286
|
+
const type = fontEngine.type;
|
|
287
|
+
|
|
288
|
+
// Add to map for type-based access
|
|
289
|
+
this.textRenderers[type] = fontEngine;
|
|
290
|
+
this.textRenderers[type].init(this);
|
|
291
|
+
|
|
292
|
+
this.fontHandlers[type] = fontEngine.font;
|
|
293
|
+
});
|
|
294
|
+
} else {
|
|
295
|
+
// Single font engine case - initialize it directly
|
|
296
|
+
const fontEngine = this.singleFontEngine;
|
|
297
|
+
const type = fontEngine.type;
|
|
298
|
+
|
|
299
|
+
// Check compatibility
|
|
300
|
+
if (type === 'sdf' && renderMode === 'canvas') {
|
|
301
|
+
console.warn(
|
|
302
|
+
'MsdfTextRenderer is not compatible with Canvas renderer. Skipping...',
|
|
303
|
+
);
|
|
304
|
+
} else {
|
|
305
|
+
if (type === 'canvas') {
|
|
306
|
+
this.hasCanvasEngine = true;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Add to map for type-based access
|
|
310
|
+
this.textRenderers[type] = fontEngine;
|
|
311
|
+
this.fontHandlers[type] = fontEngine.font;
|
|
312
|
+
this.textRenderers[type].init(this);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (Object.keys(this.textRenderers).length === 0) {
|
|
317
|
+
console.warn('No text renderers available. Your text will not render.');
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// create root node
|
|
321
|
+
const rootNode = new CoreNode(this, {
|
|
322
|
+
x: 0,
|
|
323
|
+
y: 0,
|
|
324
|
+
w: appWidth,
|
|
325
|
+
h: appHeight,
|
|
326
|
+
alpha: 1,
|
|
327
|
+
autosize: false,
|
|
328
|
+
boundsMargin: null,
|
|
329
|
+
clipping: false,
|
|
330
|
+
color: 0x00000000,
|
|
331
|
+
colorTop: 0x00000000,
|
|
332
|
+
colorBottom: 0x00000000,
|
|
333
|
+
colorLeft: 0x00000000,
|
|
334
|
+
colorRight: 0x00000000,
|
|
335
|
+
colorTl: 0x00000000,
|
|
336
|
+
colorTr: 0x00000000,
|
|
337
|
+
colorBl: 0x00000000,
|
|
338
|
+
colorBr: 0x00000000,
|
|
339
|
+
zIndex: 0,
|
|
340
|
+
zIndexLocked: 0,
|
|
341
|
+
scaleX: 1,
|
|
342
|
+
scaleY: 1,
|
|
343
|
+
mountX: 0,
|
|
344
|
+
mountY: 0,
|
|
345
|
+
mount: 0,
|
|
346
|
+
pivot: 0.5,
|
|
347
|
+
pivotX: 0.5,
|
|
348
|
+
pivotY: 0.5,
|
|
349
|
+
rotation: 0,
|
|
350
|
+
parent: null,
|
|
351
|
+
texture: null,
|
|
352
|
+
textureOptions: {},
|
|
353
|
+
shader: this.defShaderNode,
|
|
354
|
+
rtt: false,
|
|
355
|
+
src: null,
|
|
356
|
+
scale: 1,
|
|
357
|
+
strictBounds: this.strictBounds,
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
this.root = rootNode;
|
|
361
|
+
|
|
362
|
+
// execute platform start loop
|
|
363
|
+
if (autoStart === true) {
|
|
364
|
+
this.platform.startLoop(this);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
setClearColor(color: number) {
|
|
369
|
+
this.clearColor = color;
|
|
370
|
+
this.renderer.updateClearColor(color);
|
|
371
|
+
this.renderRequested = true;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Update the target frame time based on the current targetFPS setting
|
|
376
|
+
*
|
|
377
|
+
* @remarks
|
|
378
|
+
* This should be called whenever the targetFPS option is changed
|
|
379
|
+
* to ensure targetFrameTime stays in sync.
|
|
380
|
+
* targetFPS of 0 means no throttling (targetFrameTime = 0)
|
|
381
|
+
* targetFPS > 0 means throttle to 1000/targetFPS milliseconds
|
|
382
|
+
*/
|
|
383
|
+
updateTargetFrameTime() {
|
|
384
|
+
this.targetFrameTime =
|
|
385
|
+
this.options.targetFPS > 0 ? 1000 / this.options.targetFPS : 0;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
updateFrameTime() {
|
|
389
|
+
const newFrameTime = this.platform!.getTimeStamp();
|
|
390
|
+
this.lastFrameTime = this.currentFrameTime;
|
|
391
|
+
this.currentFrameTime = newFrameTime;
|
|
392
|
+
this.deltaTime = !this.lastFrameTime
|
|
393
|
+
? 100 / 6
|
|
394
|
+
: newFrameTime - this.lastFrameTime;
|
|
395
|
+
this.txManager.frameTime = newFrameTime;
|
|
396
|
+
this.txMemManager.frameTime = newFrameTime;
|
|
397
|
+
|
|
398
|
+
// This event is emitted at the beginning of the frame (before any updates
|
|
399
|
+
// or rendering), so no need to to use `stage.queueFrameEvent` here.
|
|
400
|
+
this.eventBus.emit('frameTick', {
|
|
401
|
+
time: this.currentFrameTime,
|
|
402
|
+
delta: this.deltaTime,
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Create default PixelTexture
|
|
408
|
+
*/
|
|
409
|
+
createDefaultTexture() {
|
|
410
|
+
(this.defaultTexture as ColorTexture) = this.txManager.createTexture(
|
|
411
|
+
'ColorTexture',
|
|
412
|
+
{
|
|
413
|
+
color: 0xffffffff,
|
|
414
|
+
},
|
|
415
|
+
);
|
|
416
|
+
|
|
417
|
+
assertTruthy(this.defaultTexture instanceof ColorTexture);
|
|
418
|
+
this.txManager.loadTexture(this.defaultTexture, true);
|
|
419
|
+
|
|
420
|
+
// Mark the default texture as ALWAYS renderable
|
|
421
|
+
// This prevents it from ever being cleaned up.
|
|
422
|
+
// Fixes https://github.com/lightning-js/renderer/issues/262
|
|
423
|
+
this.defaultTexture.setRenderableOwner(this, true);
|
|
424
|
+
|
|
425
|
+
// When the default texture is loaded, request a render in case the
|
|
426
|
+
// RAF is paused. Fixes: https://github.com/lightning-js/renderer/issues/123
|
|
427
|
+
this.defaultTexture.once('loaded', () => {
|
|
428
|
+
this.requestRender();
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Update animations
|
|
434
|
+
*/
|
|
435
|
+
updateAnimations() {
|
|
436
|
+
const { animationManager } = this;
|
|
437
|
+
if (!this.root) {
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
// step animation
|
|
441
|
+
animationManager.update(this.deltaTime);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Check if the scene has updates
|
|
446
|
+
*/
|
|
447
|
+
hasSceneUpdates() {
|
|
448
|
+
return (
|
|
449
|
+
!!this.root.updateType ||
|
|
450
|
+
this.renderRequested ||
|
|
451
|
+
this.txManager.hasUpdates()
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Start a new frame draw
|
|
457
|
+
*/
|
|
458
|
+
drawFrame() {
|
|
459
|
+
const { renderer, renderRequested, root } = this;
|
|
460
|
+
const txMemManager = this.txMemManager;
|
|
461
|
+
|
|
462
|
+
// Update tree if needed
|
|
463
|
+
if (root.updateType !== 0) {
|
|
464
|
+
root.update(this.deltaTime, root.clippingRect);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Process some textures asynchronously but don't block the frame
|
|
468
|
+
// Use a background task to prevent frame drops
|
|
469
|
+
this.txManager
|
|
470
|
+
.processSome(this.options.textureProcessingTimeLimit)
|
|
471
|
+
.catch((err) => {
|
|
472
|
+
console.error('Error processing textures:', err);
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
// Reset render operations and clear the canvas
|
|
476
|
+
renderer.reset();
|
|
477
|
+
|
|
478
|
+
// Check if we need to cleanup textures
|
|
479
|
+
if (txMemManager.criticalCleanupRequested === true) {
|
|
480
|
+
txMemManager.cleanup(false);
|
|
481
|
+
|
|
482
|
+
if (txMemManager.criticalCleanupRequested === true) {
|
|
483
|
+
// If we still need to cleanup, request another but aggressive cleanup
|
|
484
|
+
txMemManager.cleanup(true);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// If we have RTT nodes draw them first
|
|
489
|
+
// So we can use them as textures in the main scene
|
|
490
|
+
if (renderer.rttNodes.length > 0) {
|
|
491
|
+
renderer.renderRTTNodes();
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// Fill quads buffer
|
|
495
|
+
this.addQuads(this.root);
|
|
496
|
+
|
|
497
|
+
// Perform render pass
|
|
498
|
+
renderer.render();
|
|
499
|
+
|
|
500
|
+
this.calculateFps();
|
|
501
|
+
this.calculateQuads();
|
|
502
|
+
|
|
503
|
+
// Reset renderRequested flag if it was set
|
|
504
|
+
if (renderRequested === true) {
|
|
505
|
+
this.renderRequested = false;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Queue an event to be emitted after the current/next frame is rendered
|
|
511
|
+
*
|
|
512
|
+
* @remarks
|
|
513
|
+
* When we are operating in the context of the render loop, we may want to
|
|
514
|
+
* emit events that are related to the current frame. However, we generally do
|
|
515
|
+
* NOT want to emit events directly in the middle of the render loop, since
|
|
516
|
+
* this could enable event handlers to modify the scene graph and cause
|
|
517
|
+
* unexpected behavior. Instead, we queue up events to be emitted and then
|
|
518
|
+
* flush the queue after the frame has been rendered.
|
|
519
|
+
*
|
|
520
|
+
* @param name
|
|
521
|
+
* @param data
|
|
522
|
+
*/
|
|
523
|
+
queueFrameEvent(name: string, data: unknown) {
|
|
524
|
+
this.frameEventQueue.push([name, data]);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Emit all queued frame events
|
|
529
|
+
*
|
|
530
|
+
* @remarks
|
|
531
|
+
* This method should be called after the frame has been rendered to emit
|
|
532
|
+
* all events that were queued during the frame.
|
|
533
|
+
*
|
|
534
|
+
* See {@link queueFrameEvent} for more information.
|
|
535
|
+
*/
|
|
536
|
+
flushFrameEvents() {
|
|
537
|
+
for (const [name, data] of this.frameEventQueue) {
|
|
538
|
+
this.eventBus.emit(name, data);
|
|
539
|
+
}
|
|
540
|
+
this.frameEventQueue = [];
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
calculateFps() {
|
|
544
|
+
// If there's an FPS update interval, emit the FPS update event
|
|
545
|
+
// when the specified interval has elapsed.
|
|
546
|
+
const { fpsUpdateInterval } = this.options;
|
|
547
|
+
if (fpsUpdateInterval) {
|
|
548
|
+
this.fpsNumFrames++;
|
|
549
|
+
this.fpsElapsedTime += this.deltaTime;
|
|
550
|
+
if (this.fpsElapsedTime >= fpsUpdateInterval) {
|
|
551
|
+
const fps = Math.round(
|
|
552
|
+
(this.fpsNumFrames * 1000) / this.fpsElapsedTime,
|
|
553
|
+
);
|
|
554
|
+
this.fpsNumFrames = 0;
|
|
555
|
+
this.fpsElapsedTime = 0;
|
|
556
|
+
this.queueFrameEvent('fpsUpdate', {
|
|
557
|
+
fps,
|
|
558
|
+
contextSpyData: this.contextSpy?.getData() ?? null,
|
|
559
|
+
} satisfies FpsUpdatePayload);
|
|
560
|
+
this.contextSpy?.reset();
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
calculateQuads() {
|
|
566
|
+
const quads = this.renderer.getQuadCount();
|
|
567
|
+
if (quads && quads !== this.numQuadsRendered) {
|
|
568
|
+
this.numQuadsRendered = quads;
|
|
569
|
+
this.queueFrameEvent('quadsUpdate', {
|
|
570
|
+
quads,
|
|
571
|
+
} satisfies QuadsUpdatePayload);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
addQuads(node: CoreNode) {
|
|
576
|
+
assertTruthy(this.renderer);
|
|
577
|
+
|
|
578
|
+
// If the node is renderable and has a loaded texture, render it
|
|
579
|
+
if (node.isRenderable === true) {
|
|
580
|
+
node.renderQuads(this.renderer);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
584
|
+
const child = node.children[i];
|
|
585
|
+
|
|
586
|
+
if (child === undefined) {
|
|
587
|
+
continue;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
if (
|
|
591
|
+
child.worldAlpha === 0 ||
|
|
592
|
+
(child.strictBounds === true &&
|
|
593
|
+
child.renderState === CoreNodeRenderState.OutOfBounds)
|
|
594
|
+
) {
|
|
595
|
+
continue;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
this.addQuads(child);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* Request a render pass without forcing an update
|
|
604
|
+
*/
|
|
605
|
+
requestRender() {
|
|
606
|
+
this.renderRequested = true;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* Given a font name, and possible renderer override, return the best compatible text renderer.
|
|
611
|
+
*
|
|
612
|
+
* @remarks
|
|
613
|
+
* Will try to return a canvas renderer if no other suitable renderer can be resolved.
|
|
614
|
+
*
|
|
615
|
+
* @param fontFamily
|
|
616
|
+
* @param textRendererOverride
|
|
617
|
+
* @returns
|
|
618
|
+
*/
|
|
619
|
+
resolveTextRenderer(
|
|
620
|
+
trProps: TrProps,
|
|
621
|
+
textRendererOverride: keyof TextRenderers | null = null,
|
|
622
|
+
): TextRenderer | null {
|
|
623
|
+
// If we have an overide, return it
|
|
624
|
+
if (textRendererOverride !== null) {
|
|
625
|
+
const overrideKey = String(textRendererOverride);
|
|
626
|
+
if (this.textRenderers[overrideKey] === undefined) {
|
|
627
|
+
console.warn(`Text renderer override '${overrideKey}' not found.`);
|
|
628
|
+
return null;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
return this.textRenderers[overrideKey];
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// If we have only one font engine early return it
|
|
635
|
+
if (this.singleFontEngine !== null) {
|
|
636
|
+
// If we have only one font engine and its the canvas engine, we can just return it
|
|
637
|
+
if (this.hasOnlyCanvasFontEngine === true) {
|
|
638
|
+
return this.singleFontEngine;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
// If we have only one font engine and it can render the font, return it
|
|
642
|
+
if (this.singleFontHandler?.canRenderFont(trProps) === true) {
|
|
643
|
+
return this.singleFontEngine;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// If we have only one font engine and it cannot render the font, return null
|
|
647
|
+
console.warn(`Text renderer cannot render font`, trProps);
|
|
648
|
+
|
|
649
|
+
return null;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// Multi font handling - If we have multiple font engines, we need to resolve the best one
|
|
653
|
+
|
|
654
|
+
// First check SDF
|
|
655
|
+
if (this.fontHandlers['sdf']?.canRenderFont(trProps) === true) {
|
|
656
|
+
return this.textRenderers.sdf || null;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// If we have a canvas engine, we can return it (it can render all fonts)
|
|
660
|
+
if (this.hasCanvasEngine === true) {
|
|
661
|
+
return this.textRenderers.canvas || null;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// If we have no font engines, return null
|
|
665
|
+
console.warn('No text renderers available. Your text will not render.');
|
|
666
|
+
return null;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
createNode(props: Partial<CoreNodeProps>) {
|
|
670
|
+
const resolvedProps = this.resolveNodeDefaults(props);
|
|
671
|
+
return new CoreNode(this, resolvedProps);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
createTextNode(props: Partial<CoreTextNodeProps>) {
|
|
675
|
+
const fontSize = props.fontSize || 16;
|
|
676
|
+
const resolvedProps = Object.assign(this.resolveNodeDefaults(props), {
|
|
677
|
+
text: props.text || '',
|
|
678
|
+
textRendererOverride: props.textRendererOverride || null,
|
|
679
|
+
fontSize,
|
|
680
|
+
fontFamily: props.fontFamily || 'sans-serif',
|
|
681
|
+
fontStyle: props.fontStyle || 'normal',
|
|
682
|
+
textAlign: props.textAlign || 'left',
|
|
683
|
+
offsetY: props.offsetY || 0,
|
|
684
|
+
letterSpacing: props.letterSpacing || 0,
|
|
685
|
+
lineHeight: props.lineHeight || 0,
|
|
686
|
+
maxLines: props.maxLines || 0,
|
|
687
|
+
textBaseline: props.textBaseline || 'alphabetic',
|
|
688
|
+
verticalAlign: props.verticalAlign || 'middle',
|
|
689
|
+
overflowSuffix: props.overflowSuffix || '...',
|
|
690
|
+
wordBreak: props.wordBreak || 'normal',
|
|
691
|
+
maxWidth: props.maxWidth || 0,
|
|
692
|
+
maxHeight: props.maxHeight || 0,
|
|
693
|
+
forceLoad: props.forceLoad || false,
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
const resolvedTextRenderer = this.resolveTextRenderer(
|
|
697
|
+
resolvedProps,
|
|
698
|
+
resolvedProps.textRendererOverride as keyof TextRenderers | null,
|
|
699
|
+
);
|
|
700
|
+
|
|
701
|
+
if (!resolvedTextRenderer) {
|
|
702
|
+
throw new Error(
|
|
703
|
+
`No compatible text renderer found for ${resolvedProps.fontFamily}`,
|
|
704
|
+
);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
return new CoreTextNode(this, resolvedProps, resolvedTextRenderer);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
setBoundsMargin(value: number | [number, number, number, number]) {
|
|
711
|
+
this.boundsMargin = Array.isArray(value)
|
|
712
|
+
? value
|
|
713
|
+
: [value, value, value, value];
|
|
714
|
+
|
|
715
|
+
this.root.setUpdateType(UpdateType.RenderBounds);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* Update the viewport bounds
|
|
720
|
+
*/
|
|
721
|
+
updateViewportBounds() {
|
|
722
|
+
const { appWidth, appHeight } = this.options;
|
|
723
|
+
this.strictBound = createBound(0, 0, appWidth, appHeight);
|
|
724
|
+
this.preloadBound = createPreloadBounds(
|
|
725
|
+
this.strictBound,
|
|
726
|
+
this.boundsMargin,
|
|
727
|
+
);
|
|
728
|
+
this.root.setUpdateType(UpdateType.RenderBounds | UpdateType.Children);
|
|
729
|
+
this.root.childUpdateType |= UpdateType.RenderBounds;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
/** Find all nodes at a given point
|
|
733
|
+
* @param data
|
|
734
|
+
*/
|
|
735
|
+
findNodesAtPoint(data: Point): CoreNode[] {
|
|
736
|
+
const x = data.x / this.options.deviceLogicalPixelRatio;
|
|
737
|
+
const y = data.y / this.options.deviceLogicalPixelRatio;
|
|
738
|
+
const nodes: CoreNode[] = [];
|
|
739
|
+
for (const node of this.interactiveNodes) {
|
|
740
|
+
if (node.isRenderable === false) {
|
|
741
|
+
continue;
|
|
742
|
+
}
|
|
743
|
+
if (pointInBound(x, y, node.renderBound!) === true) {
|
|
744
|
+
nodes.push(node);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
return nodes;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
/**
|
|
751
|
+
* Find the top node at a given point
|
|
752
|
+
* @param data
|
|
753
|
+
* @returns
|
|
754
|
+
*/
|
|
755
|
+
getNodeFromPosition(data: Point): CoreNode | null {
|
|
756
|
+
const nodes: CoreNode[] = this.findNodesAtPoint(data);
|
|
757
|
+
if (nodes.length === 0) {
|
|
758
|
+
return null;
|
|
759
|
+
}
|
|
760
|
+
let topNode = nodes[0] as CoreNode;
|
|
761
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
762
|
+
if (nodes[i]!.zIndex > topNode.zIndex) {
|
|
763
|
+
topNode = nodes[i]!;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
return topNode || null;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* Resolves the default property values for a Node
|
|
771
|
+
*
|
|
772
|
+
* @remarks
|
|
773
|
+
* This method is used internally by the RendererMain to resolve the default
|
|
774
|
+
* property values for a Node. It is exposed publicly so that it can be used
|
|
775
|
+
* by Core Driver implementations.
|
|
776
|
+
*
|
|
777
|
+
* @param props
|
|
778
|
+
* @returns
|
|
779
|
+
*/
|
|
780
|
+
protected resolveNodeDefaults(props: Partial<CoreNodeProps>): CoreNodeProps {
|
|
781
|
+
const {
|
|
782
|
+
colorTop: top,
|
|
783
|
+
colorBottom: bottom,
|
|
784
|
+
colorLeft: left,
|
|
785
|
+
colorRight: right,
|
|
786
|
+
} = props;
|
|
787
|
+
|
|
788
|
+
const color = props.color ?? 0xffffffff;
|
|
789
|
+
const colorTop = top ?? color;
|
|
790
|
+
const colorBottom = bottom ?? color;
|
|
791
|
+
const colorLeft = left ?? color;
|
|
792
|
+
const colorRight = right ?? color;
|
|
793
|
+
|
|
794
|
+
const colorTl = props.colorTl ?? top ?? left ?? color;
|
|
795
|
+
const colorTr = props.colorTr ?? top ?? right ?? color;
|
|
796
|
+
const colorBl = props.colorBl ?? bottom ?? left ?? color;
|
|
797
|
+
const colorBr = props.colorBr ?? bottom ?? right ?? color;
|
|
798
|
+
|
|
799
|
+
const scale = props.scale ?? null;
|
|
800
|
+
const mount = props.mount ?? 0;
|
|
801
|
+
const pivot = props.pivot ?? 0.5;
|
|
802
|
+
|
|
803
|
+
const data = this.options.inspector
|
|
804
|
+
? santizeCustomDataMap(props.data ?? {})
|
|
805
|
+
: {};
|
|
806
|
+
|
|
807
|
+
return {
|
|
808
|
+
x: props.x ?? 0,
|
|
809
|
+
y: props.y ?? 0,
|
|
810
|
+
w: props.w ?? 0,
|
|
811
|
+
h: props.h ?? 0,
|
|
812
|
+
alpha: props.alpha ?? 1,
|
|
813
|
+
autosize: props.autosize ?? false,
|
|
814
|
+
boundsMargin: props.boundsMargin ?? null,
|
|
815
|
+
clipping: props.clipping ?? false,
|
|
816
|
+
color,
|
|
817
|
+
colorTop,
|
|
818
|
+
colorBottom,
|
|
819
|
+
colorLeft,
|
|
820
|
+
colorRight,
|
|
821
|
+
colorTl,
|
|
822
|
+
colorTr,
|
|
823
|
+
colorBl,
|
|
824
|
+
colorBr,
|
|
825
|
+
zIndex: props.zIndex ?? 0,
|
|
826
|
+
zIndexLocked: props.zIndexLocked ?? 0,
|
|
827
|
+
parent: props.parent ?? null,
|
|
828
|
+
texture: props.texture ?? null,
|
|
829
|
+
textureOptions: props.textureOptions ?? {},
|
|
830
|
+
shader: props.shader ?? this.defShaderNode,
|
|
831
|
+
src: props.src ?? null,
|
|
832
|
+
srcHeight: props.srcHeight,
|
|
833
|
+
srcWidth: props.srcWidth,
|
|
834
|
+
srcX: props.srcX,
|
|
835
|
+
srcY: props.srcY,
|
|
836
|
+
scale,
|
|
837
|
+
scaleX: props.scaleX ?? scale ?? 1,
|
|
838
|
+
scaleY: props.scaleY ?? scale ?? 1,
|
|
839
|
+
mount,
|
|
840
|
+
mountX: props.mountX ?? mount,
|
|
841
|
+
mountY: props.mountY ?? mount,
|
|
842
|
+
pivot,
|
|
843
|
+
pivotX: props.pivotX ?? pivot,
|
|
844
|
+
pivotY: props.pivotY ?? pivot,
|
|
845
|
+
rotation: props.rotation ?? 0,
|
|
846
|
+
rtt: props.rtt ?? false,
|
|
847
|
+
data,
|
|
848
|
+
imageType: props.imageType,
|
|
849
|
+
interactive: props.interactive ?? false,
|
|
850
|
+
strictBounds: props.strictBounds ?? this.strictBounds,
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
/**
|
|
855
|
+
* Cleanup Orphaned Textures
|
|
856
|
+
*
|
|
857
|
+
* @remarks
|
|
858
|
+
* This method is used to cleanup orphaned textures that are no longer in use.
|
|
859
|
+
*/
|
|
860
|
+
cleanup(aggressive: boolean) {
|
|
861
|
+
this.txMemManager.cleanup(aggressive);
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
set clearColor(value: number) {
|
|
865
|
+
this.renderer.updateClearColor(value);
|
|
866
|
+
this.renderRequested = true;
|
|
867
|
+
this.clrColor = value;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
get clearColor() {
|
|
871
|
+
return this.clrColor;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
/**
|
|
875
|
+
* Load a font using a specific text renderer type
|
|
876
|
+
*
|
|
877
|
+
* @remarks
|
|
878
|
+
* This method allows consumers to explicitly load fonts for a specific
|
|
879
|
+
* text renderer type (e.g., 'canvas', 'sdf'). Consumers must specify
|
|
880
|
+
* the renderer type to ensure fonts are loaded with the correct pipeline.
|
|
881
|
+
*
|
|
882
|
+
* For Canvas fonts, provide fontUrl (e.g., .ttf, .woff, .woff2)
|
|
883
|
+
* For SDF fonts, provide atlasUrl (image) and atlasDataUrl (JSON glyph data)
|
|
884
|
+
*
|
|
885
|
+
* @param rendererType - The type of text renderer ('canvas', 'sdf', etc.)
|
|
886
|
+
* @param options - Font loading options specific to the renderer type
|
|
887
|
+
* @returns Promise that resolves when the font is loaded
|
|
888
|
+
*/
|
|
889
|
+
async loadFont(
|
|
890
|
+
rendererType: TextRenderers,
|
|
891
|
+
options: FontLoadOptions,
|
|
892
|
+
): Promise<void> {
|
|
893
|
+
const rendererTypeKey = String(rendererType);
|
|
894
|
+
const fontHandler = this.fontHandlers[rendererTypeKey];
|
|
895
|
+
|
|
896
|
+
if (!fontHandler) {
|
|
897
|
+
throw new Error(
|
|
898
|
+
`Font handler for renderer type '${rendererTypeKey}' not found. Available types: ${Object.keys(
|
|
899
|
+
this.fontHandlers,
|
|
900
|
+
).join(', ')}`,
|
|
901
|
+
);
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
return fontHandler.loadFont(this, options);
|
|
905
|
+
}
|
|
906
|
+
}
|